Refactor UnwindInfo codes and frame_register (#2307)
* Refactor UnwindInfo codes and frame_register * use isa word_size * fix filetests * Add comment about UnwindCode::PushRegister
This commit is contained in:
@@ -27,16 +27,12 @@ pub(crate) mod input {
|
|||||||
SaveRegister {
|
SaveRegister {
|
||||||
offset: CodeOffset,
|
offset: CodeOffset,
|
||||||
reg: Reg,
|
reg: Reg,
|
||||||
|
stack_offset: u32,
|
||||||
},
|
},
|
||||||
RestoreRegister {
|
RestoreRegister {
|
||||||
offset: CodeOffset,
|
offset: CodeOffset,
|
||||||
reg: Reg,
|
reg: Reg,
|
||||||
},
|
},
|
||||||
SaveXmmRegister {
|
|
||||||
offset: CodeOffset,
|
|
||||||
reg: Reg,
|
|
||||||
stack_offset: u32,
|
|
||||||
},
|
|
||||||
StackAlloc {
|
StackAlloc {
|
||||||
offset: CodeOffset,
|
offset: CodeOffset,
|
||||||
size: u32,
|
size: u32,
|
||||||
@@ -64,5 +60,6 @@ pub(crate) mod input {
|
|||||||
pub(crate) prologue_unwind_codes: Vec<UnwindCode<Reg>>,
|
pub(crate) prologue_unwind_codes: Vec<UnwindCode<Reg>>,
|
||||||
pub(crate) epilogues_unwind_codes: Vec<Vec<UnwindCode<Reg>>>,
|
pub(crate) epilogues_unwind_codes: Vec<Vec<UnwindCode<Reg>>>,
|
||||||
pub(crate) function_size: CodeOffset,
|
pub(crate) function_size: CodeOffset,
|
||||||
|
pub(crate) word_size: u8,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,12 +115,10 @@ pub struct UnwindInfo {
|
|||||||
impl UnwindInfo {
|
impl UnwindInfo {
|
||||||
pub(crate) fn build<'b>(
|
pub(crate) fn build<'b>(
|
||||||
unwind: input::UnwindInfo<RegUnit>,
|
unwind: input::UnwindInfo<RegUnit>,
|
||||||
word_size: u8,
|
|
||||||
frame_register: Option<RegUnit>,
|
|
||||||
map_reg: &'b dyn RegisterMapper,
|
map_reg: &'b dyn RegisterMapper,
|
||||||
) -> CodegenResult<Self> {
|
) -> CodegenResult<Self> {
|
||||||
use input::UnwindCode;
|
use input::UnwindCode;
|
||||||
let mut builder = InstructionBuilder::new(word_size, frame_register, map_reg);
|
let mut builder = InstructionBuilder::new(unwind.word_size, map_reg);
|
||||||
|
|
||||||
for c in unwind.prologue_unwind_codes.iter().chain(
|
for c in unwind.prologue_unwind_codes.iter().chain(
|
||||||
unwind
|
unwind
|
||||||
@@ -130,9 +128,13 @@ impl UnwindInfo {
|
|||||||
.flatten(),
|
.flatten(),
|
||||||
) {
|
) {
|
||||||
match c {
|
match c {
|
||||||
UnwindCode::SaveRegister { offset, reg } => {
|
UnwindCode::SaveRegister {
|
||||||
|
offset,
|
||||||
|
reg,
|
||||||
|
stack_offset: 0,
|
||||||
|
} => {
|
||||||
builder
|
builder
|
||||||
.push_reg(*offset, *reg)
|
.save_reg(*offset, *reg)
|
||||||
.map_err(CodegenError::RegisterMappingError)?;
|
.map_err(CodegenError::RegisterMappingError)?;
|
||||||
}
|
}
|
||||||
UnwindCode::StackAlloc { offset, size } => {
|
UnwindCode::StackAlloc { offset, size } => {
|
||||||
@@ -143,7 +145,7 @@ impl UnwindInfo {
|
|||||||
}
|
}
|
||||||
UnwindCode::RestoreRegister { offset, reg } => {
|
UnwindCode::RestoreRegister { offset, reg } => {
|
||||||
builder
|
builder
|
||||||
.pop_reg(*offset, *reg)
|
.restore_reg(*offset, *reg)
|
||||||
.map_err(CodegenError::RegisterMappingError)?;
|
.map_err(CodegenError::RegisterMappingError)?;
|
||||||
}
|
}
|
||||||
UnwindCode::SetFramePointer { offset, reg } => {
|
UnwindCode::SetFramePointer { offset, reg } => {
|
||||||
@@ -180,46 +182,29 @@ impl UnwindInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct InstructionBuilder<'a> {
|
struct InstructionBuilder<'a> {
|
||||||
word_size: u8,
|
sp_offset: i32,
|
||||||
cfa_offset: i32,
|
|
||||||
saved_state: Option<i32>,
|
|
||||||
frame_register: Option<RegUnit>,
|
frame_register: Option<RegUnit>,
|
||||||
|
saved_state: Option<(i32, Option<RegUnit>)>,
|
||||||
map_reg: &'a dyn RegisterMapper,
|
map_reg: &'a dyn RegisterMapper,
|
||||||
instructions: Vec<(u32, CallFrameInstruction)>,
|
instructions: Vec<(u32, CallFrameInstruction)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> InstructionBuilder<'a> {
|
impl<'a> InstructionBuilder<'a> {
|
||||||
fn new(
|
fn new(word_size: u8, map_reg: &'a (dyn RegisterMapper + 'a)) -> Self {
|
||||||
word_size: u8,
|
|
||||||
frame_register: Option<RegUnit>,
|
|
||||||
map_reg: &'a (dyn RegisterMapper + 'a),
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
word_size,
|
sp_offset: word_size as i32, // CFA offset starts at word size offset to account for the return address on stack
|
||||||
cfa_offset: word_size as i32, // CFA offset starts at word size offset to account for the return address on stack
|
|
||||||
saved_state: None,
|
saved_state: None,
|
||||||
frame_register,
|
frame_register: None,
|
||||||
map_reg,
|
map_reg,
|
||||||
instructions: Vec::new(),
|
instructions: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_reg(&mut self, offset: u32, reg: RegUnit) -> Result<(), RegisterMappingError> {
|
fn save_reg(&mut self, offset: u32, reg: RegUnit) -> Result<(), RegisterMappingError> {
|
||||||
self.cfa_offset += self.word_size as i32;
|
|
||||||
// Update the CFA if this is the save of the frame pointer register or if a frame pointer isn't being used
|
|
||||||
// When using a frame pointer, we only need to update the CFA to account for the push of the frame pointer itself
|
|
||||||
if match self.frame_register {
|
|
||||||
Some(fp) => reg == fp,
|
|
||||||
None => true,
|
|
||||||
} {
|
|
||||||
self.instructions
|
|
||||||
.push((offset, CallFrameInstruction::CfaOffset(self.cfa_offset)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.cfa_offset),
|
CallFrameInstruction::Offset(self.map_reg.map(reg)?, -self.sp_offset),
|
||||||
));
|
));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -228,27 +213,52 @@ impl<'a> InstructionBuilder<'a> {
|
|||||||
fn adjust_sp_down_imm(&mut self, offset: u32, imm: i64) {
|
fn adjust_sp_down_imm(&mut self, offset: u32, imm: i64) {
|
||||||
assert!(imm <= core::u32::MAX as i64);
|
assert!(imm <= core::u32::MAX as i64);
|
||||||
|
|
||||||
|
self.sp_offset += imm as i32;
|
||||||
|
|
||||||
// Don't adjust the CFA if we're using a frame pointer
|
// Don't adjust the CFA if we're using a frame pointer
|
||||||
if self.frame_register.is_some() {
|
if self.frame_register.is_some() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.cfa_offset += imm as i32;
|
|
||||||
self.instructions
|
self.instructions
|
||||||
.push((offset, CallFrameInstruction::CfaOffset(self.cfa_offset)));
|
.push((offset, CallFrameInstruction::CfaOffset(self.sp_offset)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn adjust_sp_up_imm(&mut self, offset: u32, imm: i64) {
|
fn adjust_sp_up_imm(&mut self, offset: u32, imm: i64) {
|
||||||
assert!(imm <= core::u32::MAX as i64);
|
assert!(imm <= core::u32::MAX as i64);
|
||||||
|
|
||||||
|
self.sp_offset -= imm as i32;
|
||||||
|
|
||||||
// Don't adjust the CFA if we're using a frame pointer
|
// Don't adjust the CFA if we're using a frame pointer
|
||||||
if self.frame_register.is_some() {
|
if self.frame_register.is_some() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.cfa_offset -= imm as i32;
|
let cfa_inst_ofs = {
|
||||||
self.instructions
|
// Scan to find and merge with CFA instruction with the same offset.
|
||||||
.push((offset, CallFrameInstruction::CfaOffset(self.cfa_offset)));
|
let mut it = self.instructions.iter_mut();
|
||||||
|
loop {
|
||||||
|
match it.next_back() {
|
||||||
|
Some((i_offset, i)) if *i_offset == offset => {
|
||||||
|
if let CallFrameInstruction::Cfa(_, o) = i {
|
||||||
|
break Some(o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
break None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(o) = cfa_inst_ofs {
|
||||||
|
// Update previous CFA instruction.
|
||||||
|
*o = self.sp_offset;
|
||||||
|
} else {
|
||||||
|
// Add just CFA offset instruction.
|
||||||
|
self.instructions
|
||||||
|
.push((offset, CallFrameInstruction::CfaOffset(self.sp_offset)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_cfa_reg(&mut self, offset: u32, reg: RegUnit) -> Result<(), RegisterMappingError> {
|
fn set_cfa_reg(&mut self, offset: u32, reg: RegUnit) -> Result<(), RegisterMappingError> {
|
||||||
@@ -256,48 +266,39 @@ impl<'a> InstructionBuilder<'a> {
|
|||||||
offset,
|
offset,
|
||||||
CallFrameInstruction::CfaRegister(self.map_reg.map(reg)?),
|
CallFrameInstruction::CfaRegister(self.map_reg.map(reg)?),
|
||||||
));
|
));
|
||||||
|
self.frame_register = Some(reg);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop_reg(&mut self, offset: u32, reg: RegUnit) -> Result<(), RegisterMappingError> {
|
fn restore_reg(&mut self, offset: u32, reg: RegUnit) -> Result<(), RegisterMappingError> {
|
||||||
self.cfa_offset -= self.word_size as i32;
|
// Update the CFA if this is the restore of the frame pointer register.
|
||||||
|
if Some(reg) == self.frame_register {
|
||||||
// Update the CFA if this is the restore of the frame pointer register or if a frame pointer isn't being used
|
self.frame_register = None;
|
||||||
match self.frame_register {
|
self.instructions.push((
|
||||||
Some(fp) => {
|
offset,
|
||||||
if reg == fp {
|
CallFrameInstruction::Cfa(self.map_reg.rsp(), self.sp_offset),
|
||||||
self.instructions.push((
|
));
|
||||||
offset,
|
}
|
||||||
CallFrameInstruction::Cfa(self.map_reg.rsp(), self.cfa_offset),
|
// Pops in the epilogue are register restores, so record a "same value" for the register
|
||||||
));
|
self.instructions.push((
|
||||||
}
|
offset,
|
||||||
}
|
CallFrameInstruction::SameValue(self.map_reg.map(reg)?),
|
||||||
None => {
|
));
|
||||||
self.instructions
|
|
||||||
.push((offset, CallFrameInstruction::CfaOffset(self.cfa_offset)));
|
|
||||||
|
|
||||||
// Pops in the epilogue are register restores, so record a "same value" for the register
|
|
||||||
// This isn't necessary when using a frame pointer as the CFA doesn't change for CSR restores
|
|
||||||
self.instructions.push((
|
|
||||||
offset,
|
|
||||||
CallFrameInstruction::SameValue(self.map_reg.map(reg)?),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remember_state(&mut self, offset: u32) {
|
fn remember_state(&mut self, offset: u32) {
|
||||||
self.saved_state = Some(self.cfa_offset);
|
self.saved_state = Some((self.sp_offset, self.frame_register));
|
||||||
|
|
||||||
self.instructions
|
self.instructions
|
||||||
.push((offset, CallFrameInstruction::RememberState));
|
.push((offset, CallFrameInstruction::RememberState));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn restore_state(&mut self, offset: u32) {
|
fn restore_state(&mut self, offset: u32) {
|
||||||
let cfa_offset = self.saved_state.take().unwrap();
|
let (sp_offset, frame_register) = self.saved_state.take().unwrap();
|
||||||
self.cfa_offset = cfa_offset;
|
self.sp_offset = sp_offset;
|
||||||
|
self.frame_register = frame_register;
|
||||||
|
|
||||||
self.instructions
|
self.instructions
|
||||||
.push((offset, CallFrameInstruction::RestoreState));
|
.push((offset, CallFrameInstruction::RestoreState));
|
||||||
|
|||||||
@@ -137,10 +137,15 @@ impl UnwindCode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) enum MappedRegister {
|
||||||
|
Int(u8),
|
||||||
|
Xmm(u8),
|
||||||
|
}
|
||||||
|
|
||||||
/// Maps UnwindInfo register to Windows x64 unwind data.
|
/// Maps UnwindInfo register to Windows x64 unwind data.
|
||||||
pub(crate) trait RegisterMapper {
|
pub(crate) trait RegisterMapper {
|
||||||
/// Maps RegUnit.
|
/// Maps RegUnit.
|
||||||
fn map(reg: RegUnit) -> u8;
|
fn map(reg: RegUnit) -> MappedRegister;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents Windows x64 unwind information.
|
/// Represents Windows x64 unwind information.
|
||||||
@@ -219,14 +224,50 @@ impl UnwindInfo {
|
|||||||
) -> CodegenResult<Self> {
|
) -> CodegenResult<Self> {
|
||||||
use crate::isa::unwind::input::UnwindCode as InputUnwindCode;
|
use crate::isa::unwind::input::UnwindCode as InputUnwindCode;
|
||||||
|
|
||||||
|
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 c in unwind.prologue_unwind_codes.iter() {
|
||||||
match c {
|
match c {
|
||||||
InputUnwindCode::SaveRegister { offset, reg } => {
|
InputUnwindCode::SaveRegister {
|
||||||
unwind_codes.push(UnwindCode::PushRegister {
|
offset,
|
||||||
offset: ensure_unwind_offset(*offset)?,
|
reg,
|
||||||
reg: MR::map(*reg),
|
stack_offset,
|
||||||
});
|
} => {
|
||||||
|
let reg = MR::map(*reg);
|
||||||
|
let offset = ensure_unwind_offset(*offset)?;
|
||||||
|
match reg {
|
||||||
|
MappedRegister::Int(reg) => {
|
||||||
|
// Attempt to convert sequence of the `InputUnwindCode`:
|
||||||
|
// `StackAlloc { size = word_size }`, `SaveRegister { stack_offset: 0 }`
|
||||||
|
// to the shorter `UnwindCode::PushRegister`.
|
||||||
|
let push_reg_sequence = if let Some(UnwindCode::StackAlloc {
|
||||||
|
offset: alloc_offset,
|
||||||
|
size,
|
||||||
|
}) = unwind_codes.last()
|
||||||
|
{
|
||||||
|
*size == word_size && offset == *alloc_offset && *stack_offset == 0
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
if push_reg_sequence {
|
||||||
|
*unwind_codes.last_mut().unwrap() =
|
||||||
|
UnwindCode::PushRegister { offset, reg };
|
||||||
|
} else {
|
||||||
|
// TODO add `UnwindCode::SaveRegister` to handle multiple register
|
||||||
|
// pushes with single `UnwindCode::StackAlloc`.
|
||||||
|
return Err(CodegenError::Unsupported(
|
||||||
|
"Unsupported UnwindCode::PushRegister sequence".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MappedRegister::Xmm(reg) => {
|
||||||
|
unwind_codes.push(UnwindCode::SaveXmm {
|
||||||
|
offset,
|
||||||
|
reg,
|
||||||
|
stack_offset: *stack_offset,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
InputUnwindCode::StackAlloc { offset, size } => {
|
InputUnwindCode::StackAlloc { offset, size } => {
|
||||||
unwind_codes.push(UnwindCode::StackAlloc {
|
unwind_codes.push(UnwindCode::StackAlloc {
|
||||||
@@ -234,17 +275,6 @@ impl UnwindInfo {
|
|||||||
size: *size,
|
size: *size,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
InputUnwindCode::SaveXmmRegister {
|
|
||||||
offset,
|
|
||||||
reg,
|
|
||||||
stack_offset,
|
|
||||||
} => {
|
|
||||||
unwind_codes.push(UnwindCode::SaveXmm {
|
|
||||||
offset: ensure_unwind_offset(*offset)?,
|
|
||||||
reg: MR::map(*reg),
|
|
||||||
stack_offset: *stack_offset,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1083,8 +1083,7 @@ pub fn create_unwind_info(
|
|||||||
// In the future, we should be omitting frame pointer as an optimization, so this will change
|
// In the future, we should be omitting frame pointer as an optimization, so this will change
|
||||||
Ok(match func.signature.call_conv {
|
Ok(match func.signature.call_conv {
|
||||||
CallConv::Fast | CallConv::Cold | CallConv::SystemV => {
|
CallConv::Fast | CallConv::Cold | CallConv::SystemV => {
|
||||||
super::unwind::systemv::create_unwind_info(func, isa, Some(RU::rbp.into()))?
|
super::unwind::systemv::create_unwind_info(func, isa)?.map(|u| UnwindInfo::SystemV(u))
|
||||||
.map(|u| UnwindInfo::SystemV(u))
|
|
||||||
}
|
}
|
||||||
CallConv::WindowsFastcall => {
|
CallConv::WindowsFastcall => {
|
||||||
super::unwind::winx64::create_unwind_info(func, isa)?.map(|u| UnwindInfo::WindowsX64(u))
|
super::unwind::winx64::create_unwind_info(func, isa)?.map(|u| UnwindInfo::WindowsX64(u))
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ use crate::isa::unwind::input::{UnwindCode, UnwindInfo};
|
|||||||
pub(crate) fn create_unwind_info(
|
pub(crate) fn create_unwind_info(
|
||||||
func: &Function,
|
func: &Function,
|
||||||
isa: &dyn TargetIsa,
|
isa: &dyn TargetIsa,
|
||||||
frame_register: Option<RegUnit>,
|
|
||||||
) -> CodegenResult<Option<UnwindInfo<RegUnit>>> {
|
) -> CodegenResult<Option<UnwindInfo<RegUnit>>> {
|
||||||
// Find last block based on max offset.
|
// Find last block based on max offset.
|
||||||
let last_block = func
|
let last_block = func
|
||||||
@@ -36,10 +35,13 @@ pub(crate) fn create_unwind_info(
|
|||||||
.map(|(i, b)| (*b, *i))
|
.map(|(i, b)| (*b, *i))
|
||||||
.collect::<HashMap<_, _>>();
|
.collect::<HashMap<_, _>>();
|
||||||
|
|
||||||
|
let word_size = isa.pointer_bytes();
|
||||||
|
|
||||||
let mut stack_size = None;
|
let mut stack_size = None;
|
||||||
let mut prologue_size = 0;
|
let mut prologue_size = 0;
|
||||||
let mut prologue_unwind_codes = Vec::new();
|
let mut prologue_unwind_codes = Vec::new();
|
||||||
let mut epilogues_unwind_codes = Vec::new();
|
let mut epilogues_unwind_codes = Vec::new();
|
||||||
|
let mut frame_register: Option<RegUnit> = None;
|
||||||
|
|
||||||
// Process only entry block and blocks with epilogues.
|
// Process only entry block and blocks with epilogues.
|
||||||
let mut blocks = func
|
let mut blocks = func
|
||||||
@@ -93,7 +95,15 @@ 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::SaveRegister { offset, reg });
|
unwind_codes.push(UnwindCode::StackAlloc {
|
||||||
|
offset,
|
||||||
|
size: word_size.into(),
|
||||||
|
});
|
||||||
|
unwind_codes.push(UnwindCode::SaveRegister {
|
||||||
|
offset,
|
||||||
|
reg,
|
||||||
|
stack_offset: 0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
Opcode::AdjustSpDown => {
|
Opcode::AdjustSpDown => {
|
||||||
let stack_size =
|
let stack_size =
|
||||||
@@ -160,7 +170,7 @@ 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::SaveXmmRegister {
|
unwind_codes.push(UnwindCode::SaveRegister {
|
||||||
offset,
|
offset,
|
||||||
reg: src,
|
reg: src,
|
||||||
stack_offset: stack_offset as u32,
|
stack_offset: stack_offset as u32,
|
||||||
@@ -168,12 +178,11 @@ pub(crate) fn create_unwind_info(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InstructionData::CopySpecial { src, dst, .. } => {
|
InstructionData::CopySpecial { src, dst, .. } if frame_register.is_none() => {
|
||||||
if let Some(fp) = frame_register {
|
// 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) && dst == fp {
|
unwind_codes.push(UnwindCode::SetFramePointer { offset, reg: dst });
|
||||||
unwind_codes.push(UnwindCode::SetFramePointer { offset, reg: dst });
|
frame_register = Some(dst);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InstructionData::NullAry { opcode } => match opcode {
|
InstructionData::NullAry { opcode } => match opcode {
|
||||||
@@ -195,9 +204,15 @@ pub(crate) fn create_unwind_info(
|
|||||||
|
|
||||||
let reg = func.locations[*arg].unwrap_reg();
|
let reg = func.locations[*arg].unwrap_reg();
|
||||||
unwind_codes.push(UnwindCode::RestoreRegister { offset, reg });
|
unwind_codes.push(UnwindCode::RestoreRegister { offset, reg });
|
||||||
|
unwind_codes.push(UnwindCode::StackDealloc {
|
||||||
|
offset,
|
||||||
|
size: word_size.into(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
epilogue_pop_offsets.clear();
|
epilogue_pop_offsets.clear();
|
||||||
|
|
||||||
|
// TODO ensure unwind codes sorted by offsets ?
|
||||||
|
|
||||||
if !is_last_block {
|
if !is_last_block {
|
||||||
unwind_codes.push(UnwindCode::RestoreState { offset });
|
unwind_codes.push(UnwindCode::RestoreState { offset });
|
||||||
}
|
}
|
||||||
@@ -216,6 +231,7 @@ pub(crate) fn create_unwind_info(
|
|||||||
prologue_unwind_codes,
|
prologue_unwind_codes,
|
||||||
epilogues_unwind_codes,
|
epilogues_unwind_codes,
|
||||||
function_size,
|
function_size,
|
||||||
|
word_size,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,7 +262,7 @@ mod tests {
|
|||||||
|
|
||||||
context.compile(&*isa).expect("expected compilation");
|
context.compile(&*isa).expect("expected compilation");
|
||||||
|
|
||||||
let unwind = create_unwind_info(&context.func, &*isa, None)
|
let unwind = create_unwind_info(&context.func, &*isa)
|
||||||
.expect("can create unwind info")
|
.expect("can create unwind info")
|
||||||
.expect("expected unwind info");
|
.expect("expected unwind info");
|
||||||
|
|
||||||
@@ -255,9 +271,15 @@ mod tests {
|
|||||||
UnwindInfo {
|
UnwindInfo {
|
||||||
prologue_size: 9,
|
prologue_size: 9,
|
||||||
prologue_unwind_codes: vec![
|
prologue_unwind_codes: vec![
|
||||||
|
UnwindCode::StackAlloc { offset: 2, size: 8 },
|
||||||
UnwindCode::SaveRegister {
|
UnwindCode::SaveRegister {
|
||||||
offset: 2,
|
offset: 2,
|
||||||
reg: RU::rbp.into(),
|
reg: RU::rbp.into(),
|
||||||
|
stack_offset: 0,
|
||||||
|
},
|
||||||
|
UnwindCode::SetFramePointer {
|
||||||
|
offset: 5,
|
||||||
|
reg: RU::rbp.into(),
|
||||||
},
|
},
|
||||||
UnwindCode::StackAlloc {
|
UnwindCode::StackAlloc {
|
||||||
offset: 9,
|
offset: 9,
|
||||||
@@ -272,9 +294,14 @@ mod tests {
|
|||||||
UnwindCode::RestoreRegister {
|
UnwindCode::RestoreRegister {
|
||||||
offset: 15,
|
offset: 15,
|
||||||
reg: RU::rbp.into()
|
reg: RU::rbp.into()
|
||||||
|
},
|
||||||
|
UnwindCode::StackDealloc {
|
||||||
|
offset: 15,
|
||||||
|
size: 8
|
||||||
}
|
}
|
||||||
]],
|
]],
|
||||||
function_size: 16,
|
function_size: 16,
|
||||||
|
word_size: 8,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -293,7 +320,7 @@ mod tests {
|
|||||||
|
|
||||||
context.compile(&*isa).expect("expected compilation");
|
context.compile(&*isa).expect("expected compilation");
|
||||||
|
|
||||||
let unwind = create_unwind_info(&context.func, &*isa, None)
|
let unwind = create_unwind_info(&context.func, &*isa)
|
||||||
.expect("can create unwind info")
|
.expect("can create unwind info")
|
||||||
.expect("expected unwind info");
|
.expect("expected unwind info");
|
||||||
|
|
||||||
@@ -302,9 +329,15 @@ mod tests {
|
|||||||
UnwindInfo {
|
UnwindInfo {
|
||||||
prologue_size: 27,
|
prologue_size: 27,
|
||||||
prologue_unwind_codes: vec![
|
prologue_unwind_codes: vec![
|
||||||
|
UnwindCode::StackAlloc { offset: 2, size: 8 },
|
||||||
UnwindCode::SaveRegister {
|
UnwindCode::SaveRegister {
|
||||||
offset: 2,
|
offset: 2,
|
||||||
reg: RU::rbp.into(),
|
reg: RU::rbp.into(),
|
||||||
|
stack_offset: 0,
|
||||||
|
},
|
||||||
|
UnwindCode::SetFramePointer {
|
||||||
|
offset: 5,
|
||||||
|
reg: RU::rbp.into(),
|
||||||
},
|
},
|
||||||
UnwindCode::StackAlloc {
|
UnwindCode::StackAlloc {
|
||||||
offset: 27,
|
offset: 27,
|
||||||
@@ -319,9 +352,14 @@ mod tests {
|
|||||||
UnwindCode::RestoreRegister {
|
UnwindCode::RestoreRegister {
|
||||||
offset: 36,
|
offset: 36,
|
||||||
reg: RU::rbp.into()
|
reg: RU::rbp.into()
|
||||||
|
},
|
||||||
|
UnwindCode::StackDealloc {
|
||||||
|
offset: 36,
|
||||||
|
size: 8
|
||||||
}
|
}
|
||||||
]],
|
]],
|
||||||
function_size: 37,
|
function_size: 37,
|
||||||
|
word_size: 8,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -340,7 +378,7 @@ mod tests {
|
|||||||
|
|
||||||
context.compile(&*isa).expect("expected compilation");
|
context.compile(&*isa).expect("expected compilation");
|
||||||
|
|
||||||
let unwind = create_unwind_info(&context.func, &*isa, None)
|
let unwind = create_unwind_info(&context.func, &*isa)
|
||||||
.expect("can create unwind info")
|
.expect("can create unwind info")
|
||||||
.expect("expected unwind info");
|
.expect("expected unwind info");
|
||||||
|
|
||||||
@@ -349,9 +387,15 @@ mod tests {
|
|||||||
UnwindInfo {
|
UnwindInfo {
|
||||||
prologue_size: 27,
|
prologue_size: 27,
|
||||||
prologue_unwind_codes: vec![
|
prologue_unwind_codes: vec![
|
||||||
|
UnwindCode::StackAlloc { offset: 2, size: 8 },
|
||||||
UnwindCode::SaveRegister {
|
UnwindCode::SaveRegister {
|
||||||
offset: 2,
|
offset: 2,
|
||||||
reg: RU::rbp.into(),
|
reg: RU::rbp.into(),
|
||||||
|
stack_offset: 0,
|
||||||
|
},
|
||||||
|
UnwindCode::SetFramePointer {
|
||||||
|
offset: 5,
|
||||||
|
reg: RU::rbp.into(),
|
||||||
},
|
},
|
||||||
UnwindCode::StackAlloc {
|
UnwindCode::StackAlloc {
|
||||||
offset: 27,
|
offset: 27,
|
||||||
@@ -366,9 +410,14 @@ mod tests {
|
|||||||
UnwindCode::RestoreRegister {
|
UnwindCode::RestoreRegister {
|
||||||
offset: 36,
|
offset: 36,
|
||||||
reg: RU::rbp.into()
|
reg: RU::rbp.into()
|
||||||
|
},
|
||||||
|
UnwindCode::StackDealloc {
|
||||||
|
offset: 36,
|
||||||
|
size: 8
|
||||||
}
|
}
|
||||||
]],
|
]],
|
||||||
function_size: 37,
|
function_size: 37,
|
||||||
|
word_size: 8,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -400,7 +449,7 @@ mod tests {
|
|||||||
|
|
||||||
context.compile(&*isa).expect("expected compilation");
|
context.compile(&*isa).expect("expected compilation");
|
||||||
|
|
||||||
let unwind = create_unwind_info(&context.func, &*isa, Some(RU::rbp.into()))
|
let unwind = create_unwind_info(&context.func, &*isa)
|
||||||
.expect("can create unwind info")
|
.expect("can create unwind info")
|
||||||
.expect("expected unwind info");
|
.expect("expected unwind info");
|
||||||
|
|
||||||
@@ -409,9 +458,11 @@ mod tests {
|
|||||||
UnwindInfo {
|
UnwindInfo {
|
||||||
prologue_size: 5,
|
prologue_size: 5,
|
||||||
prologue_unwind_codes: vec![
|
prologue_unwind_codes: vec![
|
||||||
|
UnwindCode::StackAlloc { offset: 2, size: 8 },
|
||||||
UnwindCode::SaveRegister {
|
UnwindCode::SaveRegister {
|
||||||
offset: 2,
|
offset: 2,
|
||||||
reg: RU::rbp.into()
|
reg: RU::rbp.into(),
|
||||||
|
stack_offset: 0,
|
||||||
},
|
},
|
||||||
UnwindCode::SetFramePointer {
|
UnwindCode::SetFramePointer {
|
||||||
offset: 5,
|
offset: 5,
|
||||||
@@ -425,14 +476,25 @@ mod tests {
|
|||||||
offset: 12,
|
offset: 12,
|
||||||
reg: RU::rbp.into()
|
reg: RU::rbp.into()
|
||||||
},
|
},
|
||||||
|
UnwindCode::StackDealloc {
|
||||||
|
offset: 12,
|
||||||
|
size: 8
|
||||||
|
},
|
||||||
UnwindCode::RestoreState { offset: 13 }
|
UnwindCode::RestoreState { offset: 13 }
|
||||||
],
|
],
|
||||||
vec![UnwindCode::RestoreRegister {
|
vec![
|
||||||
offset: 15,
|
UnwindCode::RestoreRegister {
|
||||||
reg: RU::rbp.into()
|
offset: 15,
|
||||||
}]
|
reg: RU::rbp.into()
|
||||||
|
},
|
||||||
|
UnwindCode::StackDealloc {
|
||||||
|
offset: 15,
|
||||||
|
size: 8
|
||||||
|
}
|
||||||
|
]
|
||||||
],
|
],
|
||||||
function_size: 16,
|
function_size: 16,
|
||||||
|
word_size: 8,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,7 +95,6 @@ pub fn map_reg(isa: &dyn TargetIsa, reg: RegUnit) -> Result<Register, RegisterMa
|
|||||||
pub(crate) fn create_unwind_info(
|
pub(crate) fn create_unwind_info(
|
||||||
func: &Function,
|
func: &Function,
|
||||||
isa: &dyn TargetIsa,
|
isa: &dyn TargetIsa,
|
||||||
frame_register: Option<RegUnit>,
|
|
||||||
) -> CodegenResult<Option<UnwindInfo>> {
|
) -> CodegenResult<Option<UnwindInfo>> {
|
||||||
// Only System V-like calling conventions are supported
|
// Only System V-like calling conventions are supported
|
||||||
match func.signature.call_conv {
|
match func.signature.call_conv {
|
||||||
@@ -106,9 +105,8 @@ pub(crate) fn create_unwind_info(
|
|||||||
if func.prologue_end.is_none() || isa.name() != "x86" || isa.pointer_bits() != 64 {
|
if func.prologue_end.is_none() || isa.name() != "x86" || isa.pointer_bits() != 64 {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
const WORD_SIZE: u8 = 8; // bytes
|
|
||||||
|
|
||||||
let unwind = match super::create_unwind_info(func, isa, frame_register)? {
|
let unwind = match super::create_unwind_info(func, isa)? {
|
||||||
Some(u) => u,
|
Some(u) => u,
|
||||||
None => {
|
None => {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
@@ -126,12 +124,7 @@ pub(crate) fn create_unwind_info(
|
|||||||
}
|
}
|
||||||
let map = RegisterMapper(isa);
|
let map = RegisterMapper(isa);
|
||||||
|
|
||||||
Ok(Some(UnwindInfo::build(
|
Ok(Some(UnwindInfo::build(unwind, &map)?))
|
||||||
unwind,
|
|
||||||
WORD_SIZE,
|
|
||||||
frame_register,
|
|
||||||
&map,
|
|
||||||
)?))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -172,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))] }");
|
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)))] }");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_function(call_conv: CallConv, stack_slot: Option<StackSlotData>) -> Function {
|
fn create_function(call_conv: CallConv, stack_slot: Option<StackSlotData>) -> Function {
|
||||||
@@ -212,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)), (13, RestoreState), (15, Cfa(Register(7), 8))] }");
|
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)))] }");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_multi_return_function(call_conv: CallConv) -> Function {
|
fn create_multi_return_function(call_conv: CallConv) -> Function {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ pub(crate) fn create_unwind_info(
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let unwind = match super::create_unwind_info(func, isa, None)? {
|
let unwind = match super::create_unwind_info(func, isa)? {
|
||||||
Some(u) => u,
|
Some(u) => u,
|
||||||
None => {
|
None => {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
@@ -27,12 +27,12 @@ pub(crate) fn create_unwind_info(
|
|||||||
struct RegisterMapper;
|
struct RegisterMapper;
|
||||||
|
|
||||||
impl crate::isa::unwind::winx64::RegisterMapper for RegisterMapper {
|
impl crate::isa::unwind::winx64::RegisterMapper for RegisterMapper {
|
||||||
fn map(reg: RegUnit) -> u8 {
|
fn map(reg: RegUnit) -> crate::isa::unwind::winx64::MappedRegister {
|
||||||
|
use crate::isa::unwind::winx64::MappedRegister;
|
||||||
if GPR.contains(reg) {
|
if GPR.contains(reg) {
|
||||||
GPR.index_of(reg) as u8
|
MappedRegister::Int(GPR.index_of(reg) as u8)
|
||||||
} else if FPR.contains(reg) {
|
} else if FPR.contains(reg) {
|
||||||
// XMM register
|
MappedRegister::Xmm(reg as u8)
|
||||||
reg as u8
|
|
||||||
} else {
|
} else {
|
||||||
panic!()
|
panic!()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ block0:
|
|||||||
; 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_def_cfa (r7, 8)
|
||||||
; nextln: DW_CFA_nop
|
; nextln: DW_CFA_same_value (r6)
|
||||||
; nextln: DW_CFA_nop
|
; nextln: DW_CFA_nop
|
||||||
|
|
||||||
; check a function with medium-sized stack alloc
|
; check a function with medium-sized stack alloc
|
||||||
@@ -77,7 +77,7 @@ block0:
|
|||||||
; 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_def_cfa (r7, 8)
|
||||||
; nextln: DW_CFA_nop
|
; nextln: DW_CFA_same_value (r6)
|
||||||
; nextln: DW_CFA_nop
|
; nextln: DW_CFA_nop
|
||||||
|
|
||||||
; check a function with large-sized stack alloc
|
; check a function with large-sized stack alloc
|
||||||
@@ -116,7 +116,7 @@ block0:
|
|||||||
; 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_def_cfa (r7, 8)
|
||||||
; nextln: DW_CFA_nop
|
; nextln: DW_CFA_same_value (r6)
|
||||||
; nextln: DW_CFA_nop
|
; nextln: DW_CFA_nop
|
||||||
; nextln:
|
; nextln:
|
||||||
|
|
||||||
@@ -169,7 +169,7 @@ block0(v0: i64, v1: i64):
|
|||||||
; nextln:
|
; nextln:
|
||||||
; nextln:
|
; nextln:
|
||||||
; nextln: 0x00000018: FDE
|
; nextln: 0x00000018: FDE
|
||||||
; nextln: length: 0x00000034
|
; nextln: length: 0x00000044
|
||||||
; nextln: CIE_pointer: 0x00000000
|
; nextln: CIE_pointer: 0x00000000
|
||||||
; nextln: start_addr: 0x0000000000000000
|
; nextln: start_addr: 0x0000000000000000
|
||||||
; nextln: range_size: 0x0000000000000074 (end_addr = 0x0000000000000074)
|
; nextln: range_size: 0x0000000000000074 (end_addr = 0x0000000000000074)
|
||||||
@@ -189,9 +189,18 @@ block0(v0: i64, v1: i64):
|
|||||||
; nextln: DW_CFA_offset (r14, 6)
|
; nextln: DW_CFA_offset (r14, 6)
|
||||||
; nextln: DW_CFA_advance_loc (2)
|
; nextln: DW_CFA_advance_loc (2)
|
||||||
; nextln: DW_CFA_offset (r15, 7)
|
; nextln: DW_CFA_offset (r15, 7)
|
||||||
; nextln: DW_CFA_advance_loc (102)
|
; nextln: DW_CFA_advance_loc (94)
|
||||||
|
; nextln: DW_CFA_same_value (r15)
|
||||||
|
; nextln: DW_CFA_advance_loc (2)
|
||||||
|
; nextln: DW_CFA_same_value (r14)
|
||||||
|
; nextln: DW_CFA_advance_loc (2)
|
||||||
|
; nextln: DW_CFA_same_value (r13)
|
||||||
|
; nextln: DW_CFA_advance_loc (2)
|
||||||
|
; nextln: DW_CFA_same_value (r12)
|
||||||
|
; nextln: DW_CFA_advance_loc (1)
|
||||||
|
; nextln: DW_CFA_same_value (r3)
|
||||||
|
; nextln: DW_CFA_advance_loc (1)
|
||||||
; nextln: DW_CFA_def_cfa (r7, 8)
|
; nextln: DW_CFA_def_cfa (r7, 8)
|
||||||
; nextln: DW_CFA_nop
|
; nextln: DW_CFA_same_value (r6)
|
||||||
; nextln: DW_CFA_nop
|
|
||||||
; nextln: DW_CFA_nop
|
; nextln: DW_CFA_nop
|
||||||
; nextln: DW_CFA_nop
|
; nextln: DW_CFA_nop
|
||||||
|
|||||||
Reference in New Issue
Block a user