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:
Yury Delendik
2020-11-05 16:57:40 -06:00
committed by GitHub
parent df59ffb1b6
commit f60c0f3ec3
15 changed files with 396 additions and 351 deletions

View File

@@ -253,13 +253,16 @@ impl Context {
&self, &self,
isa: &dyn TargetIsa, isa: &dyn TargetIsa,
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> { ) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
if self.mach_compile_result.is_some() { if let Some(backend) = isa.get_mach_backend() {
return Ok(self use crate::isa::CallConv;
.mach_compile_result use crate::machinst::UnwindInfoKind;
.as_ref() let unwind_info_kind = match self.func.signature.call_conv {
.unwrap() CallConv::Fast | CallConv::Cold | CallConv::SystemV => UnwindInfoKind::SystemV,
.unwind_info CallConv::WindowsFastcall => UnwindInfoKind::Windows,
.clone()); _ => UnwindInfoKind::None,
};
let result = self.mach_compile_result.as_ref().unwrap();
return backend.emit_unwind_info(result, unwind_info_kind);
} }
isa.create_unwind_info(&self.func) isa.create_unwind_info(&self.func)
} }

View File

@@ -1,5 +1,5 @@
use super::*; use super::*;
use crate::isa::unwind::UnwindInfo; use crate::isa::unwind::input::UnwindInfo;
use crate::result::CodegenResult; use crate::result::CodegenResult;
pub struct AArch64UnwindInfo; pub struct AArch64UnwindInfo;
@@ -7,8 +7,7 @@ pub struct AArch64UnwindInfo;
impl UnwindInfoGenerator<Inst> for AArch64UnwindInfo { impl UnwindInfoGenerator<Inst> for AArch64UnwindInfo {
fn create_unwind_info( fn create_unwind_info(
_context: UnwindInfoContext<Inst>, _context: UnwindInfoContext<Inst>,
_kind: UnwindInfoKind, ) -> CodegenResult<Option<UnwindInfo<Reg>>> {
) -> CodegenResult<Option<UnwindInfo>> {
// TODO // TODO
Ok(None) Ok(None)
} }

View File

@@ -1,5 +1,5 @@
use super::*; use super::*;
use crate::isa::unwind::UnwindInfo; use crate::isa::unwind::input::UnwindInfo;
use crate::result::CodegenResult; use crate::result::CodegenResult;
pub struct Arm32UnwindInfo; pub struct Arm32UnwindInfo;
@@ -7,8 +7,7 @@ pub struct Arm32UnwindInfo;
impl UnwindInfoGenerator<Inst> for Arm32UnwindInfo { impl UnwindInfoGenerator<Inst> for Arm32UnwindInfo {
fn create_unwind_info( fn create_unwind_info(
_context: UnwindInfoContext<Inst>, _context: UnwindInfoContext<Inst>,
_kind: UnwindInfoKind, ) -> CodegenResult<Option<UnwindInfo<Reg>>> {
) -> CodegenResult<Option<UnwindInfo>> {
// TODO // TODO
Ok(None) Ok(None)
} }

View File

@@ -21,51 +21,68 @@ pub enum UnwindInfo {
SystemV(systemv::UnwindInfo), SystemV(systemv::UnwindInfo),
} }
pub(crate) mod input { /// Intermediate representation for the unwind information
/// generated by a backend.
pub mod input {
use crate::binemit::CodeOffset; use crate::binemit::CodeOffset;
use alloc::vec::Vec; use alloc::vec::Vec;
#[cfg(feature = "enable-serde")] #[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Elementary operation in the unwind operations.
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub(crate) enum UnwindCode<Reg> { pub enum UnwindCode<Reg> {
/// Defines that a register is saved at the specified offset.
SaveRegister { SaveRegister {
offset: CodeOffset, /// The saved register.
reg: Reg, reg: Reg,
/// The specified offset relative to the stack pointer.
stack_offset: u32, stack_offset: u32,
}, },
/// Defines that a register is as defined before call.
RestoreRegister { RestoreRegister {
offset: CodeOffset, /// The restored register.
reg: Reg, reg: Reg,
}, },
/// The stack pointer was adjusted to allocate the stack.
StackAlloc { StackAlloc {
offset: CodeOffset, /// Size to allocate.
size: u32, size: u32,
}, },
/// The stack pointer was adjusted to free the stack.
StackDealloc { StackDealloc {
offset: CodeOffset, /// Size to deallocate.
size: u32, size: u32,
}, },
/// The alternative register was assigned as frame pointer base.
SetFramePointer { SetFramePointer {
offset: CodeOffset, /// The specified register.
reg: Reg, reg: Reg,
}, },
RememberState { /// Restores a frame pointer base to default register.
offset: CodeOffset, RestoreFramePointer,
}, /// Saves the state.
RestoreState { RememberState,
offset: CodeOffset, /// Restores the state.
}, RestoreState,
} }
/// Unwind information as generated by a backend.
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct UnwindInfo<Reg> { pub struct UnwindInfo<Reg> {
pub(crate) prologue_size: CodeOffset, /// Size of the prologue.
pub(crate) prologue_unwind_codes: Vec<UnwindCode<Reg>>, pub prologue_size: CodeOffset,
pub(crate) epilogues_unwind_codes: Vec<Vec<UnwindCode<Reg>>>, /// Unwind codes for prologue.
pub(crate) function_size: CodeOffset, pub prologue_unwind_codes: Vec<(CodeOffset, UnwindCode<Reg>)>,
pub(crate) word_size: u8, /// Unwind codes for epilogues.
pub epilogues_unwind_codes: Vec<Vec<(CodeOffset, UnwindCode<Reg>)>>,
/// Entire function size.
pub function_size: CodeOffset,
/// Platform word size in bytes.
pub word_size: u8,
/// Initial stack pointer offset.
pub initial_sp_offset: u8,
} }
} }

View File

@@ -98,8 +98,8 @@ impl Into<gimli::write::CallFrameInstruction> for CallFrameInstruction {
pub(crate) trait RegisterMapper<Reg> { pub(crate) trait RegisterMapper<Reg> {
/// Maps Reg. /// Maps Reg.
fn map(&self, reg: Reg) -> Result<Register, RegisterMappingError>; fn map(&self, reg: Reg) -> Result<Register, RegisterMappingError>;
/// Gets RSP in gimli's index space. /// Gets stack pointer register.
fn rsp(&self) -> Register; fn sp(&self) -> Register;
} }
/// Represents unwind information for a single System V ABI function. /// Represents unwind information for a single System V ABI function.
@@ -118,9 +118,9 @@ impl UnwindInfo {
map_reg: &'b dyn RegisterMapper<Reg>, map_reg: &'b dyn RegisterMapper<Reg>,
) -> CodegenResult<Self> { ) -> CodegenResult<Self> {
use input::UnwindCode; use input::UnwindCode;
let mut builder = InstructionBuilder::new(unwind.word_size, map_reg); let mut builder = InstructionBuilder::new(unwind.initial_sp_offset, map_reg);
for c in unwind.prologue_unwind_codes.iter().chain( for (offset, c) in unwind.prologue_unwind_codes.iter().chain(
unwind unwind
.epilogues_unwind_codes .epilogues_unwind_codes
.iter() .iter()
@@ -128,38 +128,36 @@ impl UnwindInfo {
.flatten(), .flatten(),
) { ) {
match c { match c {
UnwindCode::SaveRegister { UnwindCode::SaveRegister { reg, stack_offset } => {
offset,
reg,
stack_offset: 0,
} => {
builder builder
.save_reg(*offset, *reg) .save_reg(*offset, *reg, *stack_offset)
.map_err(CodegenError::RegisterMappingError)?; .map_err(CodegenError::RegisterMappingError)?;
} }
UnwindCode::StackAlloc { offset, size } => { UnwindCode::StackAlloc { size } => {
builder.adjust_sp_down_imm(*offset, *size as i64); builder.adjust_sp_down_imm(*offset, *size as i64);
} }
UnwindCode::StackDealloc { offset, size } => { UnwindCode::StackDealloc { size } => {
builder.adjust_sp_up_imm(*offset, *size as i64); builder.adjust_sp_up_imm(*offset, *size as i64);
} }
UnwindCode::RestoreRegister { offset, reg } => { UnwindCode::RestoreRegister { reg } => {
builder builder
.restore_reg(*offset, *reg) .restore_reg(*offset, *reg)
.map_err(CodegenError::RegisterMappingError)?; .map_err(CodegenError::RegisterMappingError)?;
} }
UnwindCode::SetFramePointer { offset, reg } => { UnwindCode::SetFramePointer { reg } => {
builder builder
.set_cfa_reg(*offset, *reg) .set_cfa_reg(*offset, *reg)
.map_err(CodegenError::RegisterMappingError)?; .map_err(CodegenError::RegisterMappingError)?;
} }
UnwindCode::RememberState { offset } => { UnwindCode::RestoreFramePointer => {
builder.restore_cfa(*offset);
}
UnwindCode::RememberState => {
builder.remember_state(*offset); builder.remember_state(*offset);
} }
UnwindCode::RestoreState { offset } => { UnwindCode::RestoreState => {
builder.restore_state(*offset); builder.restore_state(*offset);
} }
_ => {}
} }
} }
@@ -190,9 +188,9 @@ struct InstructionBuilder<'a, Reg: PartialEq + Copy> {
} }
impl<'a, Reg: PartialEq + Copy> InstructionBuilder<'a, Reg> { impl<'a, Reg: PartialEq + Copy> InstructionBuilder<'a, Reg> {
fn new(word_size: u8, map_reg: &'a (dyn RegisterMapper<Reg> + 'a)) -> Self { fn new(sp_offset: u8, map_reg: &'a (dyn RegisterMapper<Reg> + 'a)) -> Self {
Self { Self {
sp_offset: word_size as i32, // CFA offset starts at word size offset to account for the return address on stack sp_offset: sp_offset as i32, // CFA offset starts at the specified offset to account for the return address on stack
saved_state: None, saved_state: None,
frame_register: None, frame_register: None,
map_reg, map_reg,
@@ -200,11 +198,19 @@ impl<'a, Reg: PartialEq + Copy> InstructionBuilder<'a, Reg> {
} }
} }
fn save_reg(&mut self, offset: u32, reg: Reg) -> Result<(), RegisterMappingError> { fn save_reg(
&mut self,
offset: u32,
reg: Reg,
stack_offset: u32,
) -> Result<(), RegisterMappingError> {
// Pushes in the prologue are register saves, so record an offset of the save // Pushes in the prologue are register saves, so record an offset of the save
self.instructions.push(( self.instructions.push((
offset, offset,
CallFrameInstruction::Offset(self.map_reg.map(reg)?, -self.sp_offset), CallFrameInstruction::Offset(
self.map_reg.map(reg)?,
stack_offset as i32 - self.sp_offset,
),
)); ));
Ok(()) Ok(())
@@ -270,15 +276,16 @@ impl<'a, Reg: PartialEq + Copy> InstructionBuilder<'a, Reg> {
Ok(()) Ok(())
} }
fn restore_reg(&mut self, offset: u32, reg: Reg) -> Result<(), RegisterMappingError> { fn restore_cfa(&mut self, offset: u32) {
// Update the CFA if this is the restore of the frame pointer register. // Restore SP and its offset.
if Some(reg) == self.frame_register {
self.frame_register = None;
self.instructions.push(( self.instructions.push((
offset, offset,
CallFrameInstruction::Cfa(self.map_reg.rsp(), self.sp_offset), CallFrameInstruction::Cfa(self.map_reg.sp(), self.sp_offset),
)); ));
self.frame_register = None;
} }
fn restore_reg(&mut self, offset: u32, reg: Reg) -> Result<(), RegisterMappingError> {
// Pops in the epilogue are register restores, so record a "same value" for the register // Pops in the epilogue are register restores, so record a "same value" for the register
self.instructions.push(( self.instructions.push((
offset, offset,

View File

@@ -226,13 +226,9 @@ impl UnwindInfo {
let word_size: u32 = unwind.word_size.into(); let word_size: u32 = unwind.word_size.into();
let mut unwind_codes = Vec::new(); let mut unwind_codes = Vec::new();
for c in unwind.prologue_unwind_codes.iter() { for (offset, c) in unwind.prologue_unwind_codes.iter() {
match c { match c {
InputUnwindCode::SaveRegister { InputUnwindCode::SaveRegister { reg, stack_offset } => {
offset,
reg,
stack_offset,
} => {
let reg = MR::map(*reg); let reg = MR::map(*reg);
let offset = ensure_unwind_offset(*offset)?; let offset = ensure_unwind_offset(*offset)?;
match reg { match reg {
@@ -269,7 +265,7 @@ impl UnwindInfo {
} }
} }
} }
InputUnwindCode::StackAlloc { offset, size } => { InputUnwindCode::StackAlloc { size } => {
unwind_codes.push(UnwindCode::StackAlloc { unwind_codes.push(UnwindCode::StackAlloc {
offset: ensure_unwind_offset(*offset)?, offset: ensure_unwind_offset(*offset)?,
size: *size, size: *size,

View File

@@ -1,36 +1,125 @@
use super::Inst; use crate::isa::unwind::input::UnwindInfo;
use crate::isa::unwind::UnwindInfo; use crate::isa::x64::inst::{
use crate::machinst::{UnwindInfoContext, UnwindInfoGenerator, UnwindInfoKind}; args::{AluRmiROpcode, Amode, RegMemImm, SyntheticAmode},
regs, Inst,
};
use crate::machinst::{UnwindInfoContext, UnwindInfoGenerator};
use crate::result::CodegenResult; use crate::result::CodegenResult;
use alloc::vec::Vec;
use regalloc::Reg;
#[cfg(feature = "unwind")] #[cfg(feature = "unwind")]
pub use self::systemv::create_cie; pub(crate) mod systemv;
#[cfg(feature = "unwind")]
mod systemv;
pub struct X64UnwindInfo; pub struct X64UnwindInfo;
impl UnwindInfoGenerator<Inst> for X64UnwindInfo { impl UnwindInfoGenerator<Inst> for X64UnwindInfo {
#[allow(unused_variables)]
fn create_unwind_info( fn create_unwind_info(
context: UnwindInfoContext<Inst>, context: UnwindInfoContext<Inst>,
kind: UnwindInfoKind, ) -> CodegenResult<Option<UnwindInfo<Reg>>> {
) -> CodegenResult<Option<UnwindInfo>> { use crate::isa::unwind::input::{self, UnwindCode};
// Assumption: RBP is being used as the frame pointer for both calling conventions let mut codes = Vec::new();
// 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; const WORD_SIZE: u8 = 8;
systemv::create_unwind_info(context, WORD_SIZE)?.map(UnwindInfo::SystemV)
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,
},
));
} }
#[cfg(feature = "unwind")] Inst::MovRR { src, dst, .. } => {
UnwindInfoKind::Windows => { if *src == regs::rsp() {
//TODO winx64::create_unwind_info(context)?.map(|u| UnwindInfo::WindowsX64(u)) codes.push((offset, UnwindCode::SetFramePointer { reg: dst.to_reg() }));
panic!();
} }
UnwindInfoKind::None => None, }
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 }));
}
_ => {}
}
}
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,
}))
} }
} }

View File

@@ -1,13 +1,8 @@
//! Unwind information for System V ABI (x86-64). //! Unwind information for System V ABI (x86-64).
use crate::isa::unwind::input;
use crate::isa::unwind::systemv::{RegisterMappingError, UnwindInfo}; 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 crate::result::CodegenResult;
use alloc::vec::Vec;
use gimli::{write::CommonInformationEntry, Encoding, Format, Register, X86_64}; use gimli::{write::CommonInformationEntry, Encoding, Format, Register, X86_64};
use regalloc::{Reg, RegClass}; use regalloc::{Reg, RegClass};
@@ -88,113 +83,15 @@ pub fn map_reg(reg: Reg) -> Result<Register, RegisterMappingError> {
} }
pub(crate) fn create_unwind_info( pub(crate) fn create_unwind_info(
context: UnwindInfoContext<Inst>, unwind: input::UnwindInfo<Reg>,
word_size: u8,
) -> CodegenResult<Option<UnwindInfo>> { ) -> 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; struct RegisterMapper;
impl crate::isa::unwind::systemv::RegisterMapper<Reg> for RegisterMapper { impl crate::isa::unwind::systemv::RegisterMapper<Reg> for RegisterMapper {
fn map(&self, reg: Reg) -> Result<u16, RegisterMappingError> { fn map(&self, reg: Reg) -> Result<u16, RegisterMappingError> {
Ok(map_reg(reg)?.0) Ok(map_reg(reg)?.0)
} }
fn rsp(&self) -> u16 { fn sp(&self) -> u16 {
map_reg(regs::rsp()).unwrap().0 X86_64::RSP.0
} }
} }
let map = RegisterMapper; let map = RegisterMapper;

View File

@@ -103,10 +103,29 @@ impl MachBackend for X64Backend {
IntCC::UnsignedGreaterThanOrEqual 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")] #[cfg(feature = "unwind")]
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> { fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
// By default, an ISA cannot create a System V CIE Some(inst::unwind::systemv::create_cie())
Some(inst::unwind::create_cie())
} }
} }

View File

@@ -81,7 +81,7 @@ pub(crate) fn create_unwind_info(
unwind_codes = epilogues_unwind_codes.last_mut().unwrap(); unwind_codes = epilogues_unwind_codes.last_mut().unwrap();
if !is_last_block { if !is_last_block {
unwind_codes.push(UnwindCode::RememberState { offset }); unwind_codes.push((offset, UnwindCode::RememberState));
} }
} else if in_epilogue { } else if in_epilogue {
unwind_codes = epilogues_unwind_codes.last_mut().unwrap(); unwind_codes = epilogues_unwind_codes.last_mut().unwrap();
@@ -95,15 +95,19 @@ pub(crate) fn create_unwind_info(
match opcode { match opcode {
Opcode::X86Push => { Opcode::X86Push => {
let reg = func.locations[arg].unwrap_reg(); let reg = func.locations[arg].unwrap_reg();
unwind_codes.push(UnwindCode::StackAlloc { unwind_codes.push((
offset, offset,
UnwindCode::StackAlloc {
size: word_size.into(), size: word_size.into(),
}); },
unwind_codes.push(UnwindCode::SaveRegister { ));
unwind_codes.push((
offset, offset,
UnwindCode::SaveRegister {
reg, reg,
stack_offset: 0, stack_offset: 0,
}); },
));
} }
Opcode::AdjustSpDown => { Opcode::AdjustSpDown => {
let stack_size = let stack_size =
@@ -111,10 +115,8 @@ pub(crate) fn create_unwind_info(
// This is used when calling a stack check function // This is used when calling a stack check function
// We need to track the assignment to RAX which has the size of the stack // We need to track the assignment to RAX which has the size of the stack
unwind_codes.push(UnwindCode::StackAlloc { unwind_codes
offset, .push((offset, UnwindCode::StackAlloc { size: stack_size }));
size: stack_size,
});
} }
_ => {} _ => {}
} }
@@ -138,10 +140,8 @@ pub(crate) fn create_unwind_info(
stack_size = Some(imm as u32); stack_size = Some(imm as u32);
unwind_codes.push(UnwindCode::StackAlloc { unwind_codes
offset, .push((offset, UnwindCode::StackAlloc { size: imm as u32 }));
size: imm as u32,
});
} }
Opcode::AdjustSpUpImm => { Opcode::AdjustSpUpImm => {
let imm: i64 = imm.into(); let imm: i64 = imm.into();
@@ -149,10 +149,8 @@ pub(crate) fn create_unwind_info(
stack_size = Some(imm as u32); stack_size = Some(imm as u32);
unwind_codes.push(UnwindCode::StackDealloc { unwind_codes
offset, .push((offset, UnwindCode::StackDealloc { size: imm as u32 }));
size: imm as u32,
});
} }
_ => {} _ => {}
} }
@@ -170,18 +168,20 @@ pub(crate) fn create_unwind_info(
// Note: the stack_offset here is relative to an adjusted SP // Note: the stack_offset here is relative to an adjusted SP
if dst == (RU::rsp as RegUnit) && FPR.contains(src) { if dst == (RU::rsp as RegUnit) && FPR.contains(src) {
let stack_offset: i32 = stack_offset.into(); let stack_offset: i32 = stack_offset.into();
unwind_codes.push(UnwindCode::SaveRegister { unwind_codes.push((
offset, offset,
UnwindCode::SaveRegister {
reg: src, reg: src,
stack_offset: stack_offset as u32, stack_offset: stack_offset as u32,
}); },
));
} }
} }
} }
InstructionData::CopySpecial { src, dst, .. } if frame_register.is_none() => { InstructionData::CopySpecial { src, dst, .. } if frame_register.is_none() => {
// Check for change in CFA register (RSP is always the starting CFA) // Check for change in CFA register (RSP is always the starting CFA)
if src == (RU::rsp as RegUnit) { if src == (RU::rsp as RegUnit) {
unwind_codes.push(UnwindCode::SetFramePointer { offset, reg: dst }); unwind_codes.push((offset, UnwindCode::SetFramePointer { reg: dst }));
frame_register = Some(dst); frame_register = Some(dst);
} }
} }
@@ -203,18 +203,25 @@ pub(crate) fn create_unwind_info(
let offset = epilogue_pop_offsets[i]; let offset = epilogue_pop_offsets[i];
let reg = func.locations[*arg].unwrap_reg(); let reg = func.locations[*arg].unwrap_reg();
unwind_codes.push(UnwindCode::RestoreRegister { offset, reg }); unwind_codes.push((offset, UnwindCode::RestoreRegister { reg }));
unwind_codes.push(UnwindCode::StackDealloc { unwind_codes.push((
offset, offset,
UnwindCode::StackDealloc {
size: word_size.into(), size: word_size.into(),
}); },
));
if Some(reg) == frame_register {
unwind_codes.push((offset, UnwindCode::RestoreFramePointer));
// Keep frame_register assigned for next epilogue.
}
} }
epilogue_pop_offsets.clear(); epilogue_pop_offsets.clear();
// TODO ensure unwind codes sorted by offsets ? // TODO ensure unwind codes sorted by offsets ?
if !is_last_block { if !is_last_block {
unwind_codes.push(UnwindCode::RestoreState { offset }); unwind_codes.push((offset, UnwindCode::RestoreState));
} }
in_epilogue = false; in_epilogue = false;
@@ -232,6 +239,7 @@ pub(crate) fn create_unwind_info(
epilogues_unwind_codes, epilogues_unwind_codes,
function_size, function_size,
word_size, word_size,
initial_sp_offset: word_size,
})) }))
} }
@@ -271,37 +279,36 @@ mod tests {
UnwindInfo { UnwindInfo {
prologue_size: 9, prologue_size: 9,
prologue_unwind_codes: vec![ prologue_unwind_codes: vec![
UnwindCode::StackAlloc { offset: 2, size: 8 }, (2, UnwindCode::StackAlloc { size: 8 }),
(
2,
UnwindCode::SaveRegister { UnwindCode::SaveRegister {
offset: 2,
reg: RU::rbp.into(), reg: RU::rbp.into(),
stack_offset: 0, stack_offset: 0,
},
UnwindCode::SetFramePointer {
offset: 5,
reg: RU::rbp.into(),
},
UnwindCode::StackAlloc {
offset: 9,
size: 64
} }
),
(
5,
UnwindCode::SetFramePointer {
reg: RU::rbp.into(),
}
),
(9, UnwindCode::StackAlloc { size: 64 })
], ],
epilogues_unwind_codes: vec![vec![ epilogues_unwind_codes: vec![vec![
UnwindCode::StackDealloc { (13, UnwindCode::StackDealloc { size: 64 }),
offset: 13, (
size: 64 15,
},
UnwindCode::RestoreRegister { UnwindCode::RestoreRegister {
offset: 15,
reg: RU::rbp.into() reg: RU::rbp.into()
},
UnwindCode::StackDealloc {
offset: 15,
size: 8
} }
),
(15, UnwindCode::StackDealloc { size: 8 }),
(15, UnwindCode::RestoreFramePointer)
]], ]],
function_size: 16, function_size: 16,
word_size: 8, word_size: 8,
initial_sp_offset: 8,
} }
); );
} }
@@ -329,37 +336,36 @@ mod tests {
UnwindInfo { UnwindInfo {
prologue_size: 27, prologue_size: 27,
prologue_unwind_codes: vec![ prologue_unwind_codes: vec![
UnwindCode::StackAlloc { offset: 2, size: 8 }, (2, UnwindCode::StackAlloc { size: 8 }),
(
2,
UnwindCode::SaveRegister { UnwindCode::SaveRegister {
offset: 2,
reg: RU::rbp.into(), reg: RU::rbp.into(),
stack_offset: 0, stack_offset: 0,
},
UnwindCode::SetFramePointer {
offset: 5,
reg: RU::rbp.into(),
},
UnwindCode::StackAlloc {
offset: 27,
size: 10000
} }
),
(
5,
UnwindCode::SetFramePointer {
reg: RU::rbp.into(),
}
),
(27, UnwindCode::StackAlloc { size: 10000 })
], ],
epilogues_unwind_codes: vec![vec![ epilogues_unwind_codes: vec![vec![
UnwindCode::StackDealloc { (34, UnwindCode::StackDealloc { size: 10000 }),
offset: 34, (
size: 10000 36,
},
UnwindCode::RestoreRegister { UnwindCode::RestoreRegister {
offset: 36,
reg: RU::rbp.into() reg: RU::rbp.into()
},
UnwindCode::StackDealloc {
offset: 36,
size: 8
} }
),
(36, UnwindCode::StackDealloc { size: 8 }),
(36, UnwindCode::RestoreFramePointer)
]], ]],
function_size: 37, function_size: 37,
word_size: 8, word_size: 8,
initial_sp_offset: 8,
} }
); );
} }
@@ -387,37 +393,36 @@ mod tests {
UnwindInfo { UnwindInfo {
prologue_size: 27, prologue_size: 27,
prologue_unwind_codes: vec![ prologue_unwind_codes: vec![
UnwindCode::StackAlloc { offset: 2, size: 8 }, (2, UnwindCode::StackAlloc { size: 8 }),
(
2,
UnwindCode::SaveRegister { UnwindCode::SaveRegister {
offset: 2,
reg: RU::rbp.into(), reg: RU::rbp.into(),
stack_offset: 0, stack_offset: 0,
},
UnwindCode::SetFramePointer {
offset: 5,
reg: RU::rbp.into(),
},
UnwindCode::StackAlloc {
offset: 27,
size: 1000000
} }
),
(
5,
UnwindCode::SetFramePointer {
reg: RU::rbp.into(),
}
),
(27, UnwindCode::StackAlloc { size: 1000000 })
], ],
epilogues_unwind_codes: vec![vec![ epilogues_unwind_codes: vec![vec![
UnwindCode::StackDealloc { (34, UnwindCode::StackDealloc { size: 1000000 }),
offset: 34, (
size: 1000000 36,
},
UnwindCode::RestoreRegister { UnwindCode::RestoreRegister {
offset: 36,
reg: RU::rbp.into() reg: RU::rbp.into()
},
UnwindCode::StackDealloc {
offset: 36,
size: 8
} }
),
(36, UnwindCode::StackDealloc { size: 8 }),
(36, UnwindCode::RestoreFramePointer)
]], ]],
function_size: 37, function_size: 37,
word_size: 8, word_size: 8,
initial_sp_offset: 8,
} }
); );
} }
@@ -458,43 +463,48 @@ mod tests {
UnwindInfo { UnwindInfo {
prologue_size: 5, prologue_size: 5,
prologue_unwind_codes: vec![ prologue_unwind_codes: vec![
UnwindCode::StackAlloc { offset: 2, size: 8 }, (2, UnwindCode::StackAlloc { size: 8 }),
(
2,
UnwindCode::SaveRegister { UnwindCode::SaveRegister {
offset: 2,
reg: RU::rbp.into(), reg: RU::rbp.into(),
stack_offset: 0, stack_offset: 0,
}, }
),
(
5,
UnwindCode::SetFramePointer { UnwindCode::SetFramePointer {
offset: 5,
reg: RU::rbp.into() reg: RU::rbp.into()
} }
)
], ],
epilogues_unwind_codes: vec![ epilogues_unwind_codes: vec![
vec![ vec![
UnwindCode::RememberState { offset: 12 }, (12, UnwindCode::RememberState),
(
12,
UnwindCode::RestoreRegister { UnwindCode::RestoreRegister {
offset: 12,
reg: RU::rbp.into() reg: RU::rbp.into()
}, }
UnwindCode::StackDealloc { ),
offset: 12, (12, UnwindCode::StackDealloc { size: 8 }),
size: 8 (12, UnwindCode::RestoreFramePointer),
}, (13, UnwindCode::RestoreState)
UnwindCode::RestoreState { offset: 13 }
], ],
vec![ vec![
(
15,
UnwindCode::RestoreRegister { UnwindCode::RestoreRegister {
offset: 15,
reg: RU::rbp.into() reg: RU::rbp.into()
},
UnwindCode::StackDealloc {
offset: 15,
size: 8
} }
),
(15, UnwindCode::StackDealloc { size: 8 }),
(15, UnwindCode::RestoreFramePointer)
] ]
], ],
function_size: 16, function_size: 16,
word_size: 8, word_size: 8,
initial_sp_offset: 8,
} }
); );
} }

View File

@@ -118,7 +118,7 @@ pub(crate) fn create_unwind_info(
fn map(&self, reg: RegUnit) -> Result<u16, RegisterMappingError> { fn map(&self, reg: RegUnit) -> Result<u16, RegisterMappingError> {
Ok(map_reg(self.0, reg)?.0) Ok(map_reg(self.0, reg)?.0)
} }
fn rsp(&self) -> u16 { fn sp(&self) -> u16 {
X86_64::RSP.0 X86_64::RSP.0
} }
} }
@@ -165,7 +165,7 @@ mod tests {
_ => panic!("expected unwind information"), _ => panic!("expected unwind information"),
}; };
assert_eq!(format!("{:?}", fde), "FrameDescriptionEntry { address: Constant(1234), length: 16, lsda: None, instructions: [(2, CfaOffset(16)), (2, Offset(Register(6), -16)), (5, CfaRegister(Register(6))), (15, Cfa(Register(7), 8)), (15, SameValue(Register(6)))] }"); assert_eq!(format!("{:?}", fde), "FrameDescriptionEntry { address: Constant(1234), length: 16, lsda: None, instructions: [(2, CfaOffset(16)), (2, Offset(Register(6), -16)), (5, CfaRegister(Register(6))), (15, SameValue(Register(6))), (15, Cfa(Register(7), 8))] }");
} }
fn create_function(call_conv: CallConv, stack_slot: Option<StackSlotData>) -> Function { fn create_function(call_conv: CallConv, stack_slot: Option<StackSlotData>) -> Function {
@@ -205,7 +205,7 @@ mod tests {
_ => panic!("expected unwind information"), _ => panic!("expected unwind information"),
}; };
assert_eq!(format!("{:?}", fde), "FrameDescriptionEntry { address: Constant(4321), length: 16, lsda: None, instructions: [(2, CfaOffset(16)), (2, Offset(Register(6), -16)), (5, CfaRegister(Register(6))), (12, RememberState), (12, Cfa(Register(7), 8)), (12, SameValue(Register(6))), (13, RestoreState), (15, Cfa(Register(7), 8)), (15, SameValue(Register(6)))] }"); assert_eq!(format!("{:?}", fde), "FrameDescriptionEntry { address: Constant(4321), length: 16, lsda: None, instructions: [(2, CfaOffset(16)), (2, Offset(Register(6), -16)), (5, CfaRegister(Register(6))), (12, RememberState), (12, SameValue(Register(6))), (12, Cfa(Register(7), 8)), (13, RestoreState), (15, SameValue(Register(6))), (15, Cfa(Register(7), 8))] }");
} }
fn create_multi_return_function(call_conv: CallConv) -> Function { fn create_multi_return_function(call_conv: CallConv) -> Function {

View File

@@ -129,7 +129,7 @@ impl TargetIsa for TargetIsaAdapter {
self.backend.unsigned_sub_overflow_condition() self.backend.unsigned_sub_overflow_condition()
} }
#[cfg(feature = "unwind")] #[cfg(all(feature = "unwind", feature = "x64"))]
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> { fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
self.backend.create_systemv_cie() self.backend.create_systemv_cie()
} }

View File

@@ -99,7 +99,7 @@
use crate::binemit::{CodeInfo, CodeOffset, StackMap}; use crate::binemit::{CodeInfo, CodeOffset, StackMap};
use crate::ir::condcodes::IntCC; use crate::ir::condcodes::IntCC;
use crate::ir::{Function, Type}; use crate::ir::{Function, Type};
use crate::isa::unwind; use crate::isa::unwind::input as unwind_input;
use crate::result::CodegenResult; use crate::result::CodegenResult;
use crate::settings::Flags; use crate::settings::Flags;
@@ -314,7 +314,7 @@ pub struct MachCompileResult {
/// Disassembly, if requested. /// Disassembly, if requested.
pub disasm: Option<String>, pub disasm: Option<String>,
/// Unwind info. /// Unwind info.
pub unwind_info: Option<unwind::UnwindInfo>, pub unwind_info: Option<unwind_input::UnwindInfo<Reg>>,
} }
impl MachCompileResult { impl MachCompileResult {
@@ -360,6 +360,17 @@ pub trait MachBackend {
/// Condition that will be true when an IsubIfcout overflows. /// Condition that will be true when an IsubIfcout overflows.
fn unsigned_sub_overflow_condition(&self) -> IntCC; fn unsigned_sub_overflow_condition(&self) -> IntCC;
/// Produces unwind info based on backend results.
#[cfg(feature = "unwind")]
fn emit_unwind_info(
&self,
_result: &MachCompileResult,
_kind: UnwindInfoKind,
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
// By default, an backend cannot produce unwind info.
Ok(None)
}
/// Machine-specific condcode info needed by TargetIsa. /// Machine-specific condcode info needed by TargetIsa.
/// Creates a new System V Common Information Entry for the ISA. /// Creates a new System V Common Information Entry for the ISA.
#[cfg(feature = "unwind")] #[cfg(feature = "unwind")]
@@ -403,6 +414,5 @@ pub trait UnwindInfoGenerator<I: MachInstEmit> {
/// emitted instructions. /// emitted instructions.
fn create_unwind_info( fn create_unwind_info(
context: UnwindInfoContext<I>, context: UnwindInfoContext<I>,
kind: UnwindInfoKind, ) -> CodegenResult<Option<unwind_input::UnwindInfo<Reg>>>;
) -> CodegenResult<Option<unwind::UnwindInfo>>;
} }

View File

@@ -560,7 +560,7 @@ impl<I: VCodeInst> VCode<I> {
/// Generates unwind info. /// Generates unwind info.
pub fn unwind_info( pub fn unwind_info(
&self, &self,
) -> crate::result::CodegenResult<Option<crate::isa::unwind::UnwindInfo>> { ) -> crate::result::CodegenResult<Option<crate::isa::unwind::input::UnwindInfo<Reg>>> {
let layout = &self.insts_layout.borrow(); let layout = &self.insts_layout.borrow();
let (prologue, epilogues) = self.prologue_epilogue_ranges.as_ref().unwrap(); let (prologue, epilogues) = self.prologue_epilogue_ranges.as_ref().unwrap();
let context = UnwindInfoContext { let context = UnwindInfoContext {
@@ -570,7 +570,7 @@ impl<I: VCodeInst> VCode<I> {
prologue: prologue.clone(), prologue: prologue.clone(),
epilogues, epilogues,
}; };
I::UnwindInfo::create_unwind_info(context, self.abi.unwind_info_kind()) I::UnwindInfo::create_unwind_info(context)
} }
/// Get the IR block for a BlockIndex, if one exists. /// Get the IR block for a BlockIndex, if one exists.

View File

@@ -37,8 +37,8 @@ block0:
; nextln: DW_CFA_advance_loc (3) ; nextln: DW_CFA_advance_loc (3)
; nextln: DW_CFA_def_cfa_register (r6) ; nextln: DW_CFA_def_cfa_register (r6)
; nextln: DW_CFA_advance_loc (1) ; nextln: DW_CFA_advance_loc (1)
; nextln: DW_CFA_def_cfa (r7, 8)
; nextln: DW_CFA_same_value (r6) ; nextln: DW_CFA_same_value (r6)
; nextln: DW_CFA_def_cfa (r7, 8)
; nextln: DW_CFA_nop ; nextln: DW_CFA_nop
; check a function with medium-sized stack alloc ; check a function with medium-sized stack alloc
@@ -76,8 +76,8 @@ block0:
; nextln: DW_CFA_advance_loc (3) ; nextln: DW_CFA_advance_loc (3)
; nextln: DW_CFA_def_cfa_register (r6) ; nextln: DW_CFA_def_cfa_register (r6)
; nextln: DW_CFA_advance_loc (21) ; nextln: DW_CFA_advance_loc (21)
; nextln: DW_CFA_def_cfa (r7, 8)
; nextln: DW_CFA_same_value (r6) ; nextln: DW_CFA_same_value (r6)
; nextln: DW_CFA_def_cfa (r7, 8)
; nextln: DW_CFA_nop ; nextln: DW_CFA_nop
; check a function with large-sized stack alloc ; check a function with large-sized stack alloc
@@ -115,8 +115,8 @@ block0:
; nextln: DW_CFA_advance_loc (3) ; nextln: DW_CFA_advance_loc (3)
; nextln: DW_CFA_def_cfa_register (r6) ; nextln: DW_CFA_def_cfa_register (r6)
; nextln: DW_CFA_advance_loc (21) ; nextln: DW_CFA_advance_loc (21)
; nextln: DW_CFA_def_cfa (r7, 8)
; nextln: DW_CFA_same_value (r6) ; nextln: DW_CFA_same_value (r6)
; nextln: DW_CFA_def_cfa (r7, 8)
; nextln: DW_CFA_nop ; nextln: DW_CFA_nop
; nextln: ; nextln:
@@ -200,7 +200,6 @@ block0(v0: i64, v1: i64):
; nextln: DW_CFA_advance_loc (1) ; nextln: DW_CFA_advance_loc (1)
; nextln: DW_CFA_same_value (r3) ; nextln: DW_CFA_same_value (r3)
; nextln: DW_CFA_advance_loc (1) ; nextln: DW_CFA_advance_loc (1)
; nextln: DW_CFA_def_cfa (r7, 8)
; nextln: DW_CFA_same_value (r6) ; nextln: DW_CFA_same_value (r6)
; nextln: DW_CFA_nop ; nextln: DW_CFA_def_cfa (r7, 8)
; nextln: DW_CFA_nop ; nextln: DW_CFA_nop