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:
@@ -253,13 +253,16 @@ impl Context {
|
||||
&self,
|
||||
isa: &dyn TargetIsa,
|
||||
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
|
||||
if self.mach_compile_result.is_some() {
|
||||
return Ok(self
|
||||
.mach_compile_result
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.unwind_info
|
||||
.clone());
|
||||
if let Some(backend) = isa.get_mach_backend() {
|
||||
use crate::isa::CallConv;
|
||||
use crate::machinst::UnwindInfoKind;
|
||||
let unwind_info_kind = match self.func.signature.call_conv {
|
||||
CallConv::Fast | CallConv::Cold | CallConv::SystemV => UnwindInfoKind::SystemV,
|
||||
CallConv::WindowsFastcall => UnwindInfoKind::Windows,
|
||||
_ => 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)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::*;
|
||||
use crate::isa::unwind::UnwindInfo;
|
||||
use crate::isa::unwind::input::UnwindInfo;
|
||||
use crate::result::CodegenResult;
|
||||
|
||||
pub struct AArch64UnwindInfo;
|
||||
@@ -7,8 +7,7 @@ pub struct AArch64UnwindInfo;
|
||||
impl UnwindInfoGenerator<Inst> for AArch64UnwindInfo {
|
||||
fn create_unwind_info(
|
||||
_context: UnwindInfoContext<Inst>,
|
||||
_kind: UnwindInfoKind,
|
||||
) -> CodegenResult<Option<UnwindInfo>> {
|
||||
) -> CodegenResult<Option<UnwindInfo<Reg>>> {
|
||||
// TODO
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::*;
|
||||
use crate::isa::unwind::UnwindInfo;
|
||||
use crate::isa::unwind::input::UnwindInfo;
|
||||
use crate::result::CodegenResult;
|
||||
|
||||
pub struct Arm32UnwindInfo;
|
||||
@@ -7,8 +7,7 @@ pub struct Arm32UnwindInfo;
|
||||
impl UnwindInfoGenerator<Inst> for Arm32UnwindInfo {
|
||||
fn create_unwind_info(
|
||||
_context: UnwindInfoContext<Inst>,
|
||||
_kind: UnwindInfoKind,
|
||||
) -> CodegenResult<Option<UnwindInfo>> {
|
||||
) -> CodegenResult<Option<UnwindInfo<Reg>>> {
|
||||
// TODO
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
@@ -21,51 +21,68 @@ pub enum 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 alloc::vec::Vec;
|
||||
#[cfg(feature = "enable-serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Elementary operation in the unwind operations.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[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 {
|
||||
offset: CodeOffset,
|
||||
/// The saved register.
|
||||
reg: Reg,
|
||||
/// The specified offset relative to the stack pointer.
|
||||
stack_offset: u32,
|
||||
},
|
||||
/// Defines that a register is as defined before call.
|
||||
RestoreRegister {
|
||||
offset: CodeOffset,
|
||||
/// The restored register.
|
||||
reg: Reg,
|
||||
},
|
||||
/// The stack pointer was adjusted to allocate the stack.
|
||||
StackAlloc {
|
||||
offset: CodeOffset,
|
||||
/// Size to allocate.
|
||||
size: u32,
|
||||
},
|
||||
/// The stack pointer was adjusted to free the stack.
|
||||
StackDealloc {
|
||||
offset: CodeOffset,
|
||||
/// Size to deallocate.
|
||||
size: u32,
|
||||
},
|
||||
/// The alternative register was assigned as frame pointer base.
|
||||
SetFramePointer {
|
||||
offset: CodeOffset,
|
||||
/// The specified register.
|
||||
reg: Reg,
|
||||
},
|
||||
RememberState {
|
||||
offset: CodeOffset,
|
||||
},
|
||||
RestoreState {
|
||||
offset: CodeOffset,
|
||||
},
|
||||
/// Restores a frame pointer base to default register.
|
||||
RestoreFramePointer,
|
||||
/// Saves the state.
|
||||
RememberState,
|
||||
/// Restores the state.
|
||||
RestoreState,
|
||||
}
|
||||
|
||||
/// Unwind information as generated by a backend.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
pub struct UnwindInfo<Reg> {
|
||||
pub(crate) prologue_size: CodeOffset,
|
||||
pub(crate) prologue_unwind_codes: Vec<UnwindCode<Reg>>,
|
||||
pub(crate) epilogues_unwind_codes: Vec<Vec<UnwindCode<Reg>>>,
|
||||
pub(crate) function_size: CodeOffset,
|
||||
pub(crate) word_size: u8,
|
||||
/// Size of the prologue.
|
||||
pub prologue_size: CodeOffset,
|
||||
/// Unwind codes for prologue.
|
||||
pub prologue_unwind_codes: Vec<(CodeOffset, UnwindCode<Reg>)>,
|
||||
/// 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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,8 +98,8 @@ impl Into<gimli::write::CallFrameInstruction> for CallFrameInstruction {
|
||||
pub(crate) trait RegisterMapper<Reg> {
|
||||
/// Maps Reg.
|
||||
fn map(&self, reg: Reg) -> Result<Register, RegisterMappingError>;
|
||||
/// Gets RSP in gimli's index space.
|
||||
fn rsp(&self) -> Register;
|
||||
/// Gets stack pointer register.
|
||||
fn sp(&self) -> Register;
|
||||
}
|
||||
|
||||
/// Represents unwind information for a single System V ABI function.
|
||||
@@ -118,9 +118,9 @@ impl UnwindInfo {
|
||||
map_reg: &'b dyn RegisterMapper<Reg>,
|
||||
) -> CodegenResult<Self> {
|
||||
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
|
||||
.epilogues_unwind_codes
|
||||
.iter()
|
||||
@@ -128,38 +128,36 @@ impl UnwindInfo {
|
||||
.flatten(),
|
||||
) {
|
||||
match c {
|
||||
UnwindCode::SaveRegister {
|
||||
offset,
|
||||
reg,
|
||||
stack_offset: 0,
|
||||
} => {
|
||||
UnwindCode::SaveRegister { reg, stack_offset } => {
|
||||
builder
|
||||
.save_reg(*offset, *reg)
|
||||
.save_reg(*offset, *reg, *stack_offset)
|
||||
.map_err(CodegenError::RegisterMappingError)?;
|
||||
}
|
||||
UnwindCode::StackAlloc { offset, size } => {
|
||||
UnwindCode::StackAlloc { size } => {
|
||||
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);
|
||||
}
|
||||
UnwindCode::RestoreRegister { offset, reg } => {
|
||||
UnwindCode::RestoreRegister { reg } => {
|
||||
builder
|
||||
.restore_reg(*offset, *reg)
|
||||
.map_err(CodegenError::RegisterMappingError)?;
|
||||
}
|
||||
UnwindCode::SetFramePointer { offset, reg } => {
|
||||
UnwindCode::SetFramePointer { reg } => {
|
||||
builder
|
||||
.set_cfa_reg(*offset, *reg)
|
||||
.map_err(CodegenError::RegisterMappingError)?;
|
||||
}
|
||||
UnwindCode::RememberState { offset } => {
|
||||
UnwindCode::RestoreFramePointer => {
|
||||
builder.restore_cfa(*offset);
|
||||
}
|
||||
UnwindCode::RememberState => {
|
||||
builder.remember_state(*offset);
|
||||
}
|
||||
UnwindCode::RestoreState { offset } => {
|
||||
UnwindCode::RestoreState => {
|
||||
builder.restore_state(*offset);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,9 +188,9 @@ struct InstructionBuilder<'a, Reg: PartialEq + Copy> {
|
||||
}
|
||||
|
||||
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 {
|
||||
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,
|
||||
frame_register: None,
|
||||
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
|
||||
self.instructions.push((
|
||||
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(())
|
||||
@@ -270,15 +276,16 @@ impl<'a, Reg: PartialEq + Copy> InstructionBuilder<'a, Reg> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn restore_cfa(&mut self, offset: u32) {
|
||||
// Restore SP and its offset.
|
||||
self.instructions.push((
|
||||
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> {
|
||||
// Update the CFA if this is the restore of the frame pointer register.
|
||||
if Some(reg) == self.frame_register {
|
||||
self.frame_register = None;
|
||||
self.instructions.push((
|
||||
offset,
|
||||
CallFrameInstruction::Cfa(self.map_reg.rsp(), self.sp_offset),
|
||||
));
|
||||
}
|
||||
// Pops in the epilogue are register restores, so record a "same value" for the register
|
||||
self.instructions.push((
|
||||
offset,
|
||||
|
||||
@@ -226,13 +226,9 @@ impl UnwindInfo {
|
||||
|
||||
let word_size: u32 = unwind.word_size.into();
|
||||
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 {
|
||||
InputUnwindCode::SaveRegister {
|
||||
offset,
|
||||
reg,
|
||||
stack_offset,
|
||||
} => {
|
||||
InputUnwindCode::SaveRegister { reg, stack_offset } => {
|
||||
let reg = MR::map(*reg);
|
||||
let offset = ensure_unwind_offset(*offset)?;
|
||||
match reg {
|
||||
@@ -269,7 +265,7 @@ impl UnwindInfo {
|
||||
}
|
||||
}
|
||||
}
|
||||
InputUnwindCode::StackAlloc { offset, size } => {
|
||||
InputUnwindCode::StackAlloc { size } => {
|
||||
unwind_codes.push(UnwindCode::StackAlloc {
|
||||
offset: ensure_unwind_offset(*offset)?,
|
||||
size: *size,
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ pub(crate) fn create_unwind_info(
|
||||
unwind_codes = epilogues_unwind_codes.last_mut().unwrap();
|
||||
|
||||
if !is_last_block {
|
||||
unwind_codes.push(UnwindCode::RememberState { offset });
|
||||
unwind_codes.push((offset, UnwindCode::RememberState));
|
||||
}
|
||||
} else if in_epilogue {
|
||||
unwind_codes = epilogues_unwind_codes.last_mut().unwrap();
|
||||
@@ -95,15 +95,19 @@ pub(crate) fn create_unwind_info(
|
||||
match opcode {
|
||||
Opcode::X86Push => {
|
||||
let reg = func.locations[arg].unwrap_reg();
|
||||
unwind_codes.push(UnwindCode::StackAlloc {
|
||||
unwind_codes.push((
|
||||
offset,
|
||||
size: word_size.into(),
|
||||
});
|
||||
unwind_codes.push(UnwindCode::SaveRegister {
|
||||
UnwindCode::StackAlloc {
|
||||
size: word_size.into(),
|
||||
},
|
||||
));
|
||||
unwind_codes.push((
|
||||
offset,
|
||||
reg,
|
||||
stack_offset: 0,
|
||||
});
|
||||
UnwindCode::SaveRegister {
|
||||
reg,
|
||||
stack_offset: 0,
|
||||
},
|
||||
));
|
||||
}
|
||||
Opcode::AdjustSpDown => {
|
||||
let stack_size =
|
||||
@@ -111,10 +115,8 @@ pub(crate) fn create_unwind_info(
|
||||
|
||||
// This is used when calling a stack check function
|
||||
// We need to track the assignment to RAX which has the size of the stack
|
||||
unwind_codes.push(UnwindCode::StackAlloc {
|
||||
offset,
|
||||
size: stack_size,
|
||||
});
|
||||
unwind_codes
|
||||
.push((offset, UnwindCode::StackAlloc { size: stack_size }));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -138,10 +140,8 @@ pub(crate) fn create_unwind_info(
|
||||
|
||||
stack_size = Some(imm as u32);
|
||||
|
||||
unwind_codes.push(UnwindCode::StackAlloc {
|
||||
offset,
|
||||
size: imm as u32,
|
||||
});
|
||||
unwind_codes
|
||||
.push((offset, UnwindCode::StackAlloc { size: imm as u32 }));
|
||||
}
|
||||
Opcode::AdjustSpUpImm => {
|
||||
let imm: i64 = imm.into();
|
||||
@@ -149,10 +149,8 @@ pub(crate) fn create_unwind_info(
|
||||
|
||||
stack_size = Some(imm as u32);
|
||||
|
||||
unwind_codes.push(UnwindCode::StackDealloc {
|
||||
offset,
|
||||
size: imm as u32,
|
||||
});
|
||||
unwind_codes
|
||||
.push((offset, UnwindCode::StackDealloc { 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
|
||||
if dst == (RU::rsp as RegUnit) && FPR.contains(src) {
|
||||
let stack_offset: i32 = stack_offset.into();
|
||||
unwind_codes.push(UnwindCode::SaveRegister {
|
||||
unwind_codes.push((
|
||||
offset,
|
||||
reg: src,
|
||||
stack_offset: stack_offset as u32,
|
||||
});
|
||||
UnwindCode::SaveRegister {
|
||||
reg: src,
|
||||
stack_offset: stack_offset as u32,
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
InstructionData::CopySpecial { src, dst, .. } if frame_register.is_none() => {
|
||||
// Check for change in CFA register (RSP is always the starting CFA)
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -203,18 +203,25 @@ pub(crate) fn create_unwind_info(
|
||||
let offset = epilogue_pop_offsets[i];
|
||||
|
||||
let reg = func.locations[*arg].unwrap_reg();
|
||||
unwind_codes.push(UnwindCode::RestoreRegister { offset, reg });
|
||||
unwind_codes.push(UnwindCode::StackDealloc {
|
||||
unwind_codes.push((offset, UnwindCode::RestoreRegister { reg }));
|
||||
unwind_codes.push((
|
||||
offset,
|
||||
size: word_size.into(),
|
||||
});
|
||||
UnwindCode::StackDealloc {
|
||||
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();
|
||||
|
||||
// TODO ensure unwind codes sorted by offsets ?
|
||||
|
||||
if !is_last_block {
|
||||
unwind_codes.push(UnwindCode::RestoreState { offset });
|
||||
unwind_codes.push((offset, UnwindCode::RestoreState));
|
||||
}
|
||||
|
||||
in_epilogue = false;
|
||||
@@ -232,6 +239,7 @@ pub(crate) fn create_unwind_info(
|
||||
epilogues_unwind_codes,
|
||||
function_size,
|
||||
word_size,
|
||||
initial_sp_offset: word_size,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -271,37 +279,36 @@ mod tests {
|
||||
UnwindInfo {
|
||||
prologue_size: 9,
|
||||
prologue_unwind_codes: vec![
|
||||
UnwindCode::StackAlloc { offset: 2, size: 8 },
|
||||
UnwindCode::SaveRegister {
|
||||
offset: 2,
|
||||
reg: RU::rbp.into(),
|
||||
stack_offset: 0,
|
||||
},
|
||||
UnwindCode::SetFramePointer {
|
||||
offset: 5,
|
||||
reg: RU::rbp.into(),
|
||||
},
|
||||
UnwindCode::StackAlloc {
|
||||
offset: 9,
|
||||
size: 64
|
||||
}
|
||||
(2, UnwindCode::StackAlloc { size: 8 }),
|
||||
(
|
||||
2,
|
||||
UnwindCode::SaveRegister {
|
||||
reg: RU::rbp.into(),
|
||||
stack_offset: 0,
|
||||
}
|
||||
),
|
||||
(
|
||||
5,
|
||||
UnwindCode::SetFramePointer {
|
||||
reg: RU::rbp.into(),
|
||||
}
|
||||
),
|
||||
(9, UnwindCode::StackAlloc { size: 64 })
|
||||
],
|
||||
epilogues_unwind_codes: vec![vec![
|
||||
UnwindCode::StackDealloc {
|
||||
offset: 13,
|
||||
size: 64
|
||||
},
|
||||
UnwindCode::RestoreRegister {
|
||||
offset: 15,
|
||||
reg: RU::rbp.into()
|
||||
},
|
||||
UnwindCode::StackDealloc {
|
||||
offset: 15,
|
||||
size: 8
|
||||
}
|
||||
(13, UnwindCode::StackDealloc { size: 64 }),
|
||||
(
|
||||
15,
|
||||
UnwindCode::RestoreRegister {
|
||||
reg: RU::rbp.into()
|
||||
}
|
||||
),
|
||||
(15, UnwindCode::StackDealloc { size: 8 }),
|
||||
(15, UnwindCode::RestoreFramePointer)
|
||||
]],
|
||||
function_size: 16,
|
||||
word_size: 8,
|
||||
initial_sp_offset: 8,
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -329,37 +336,36 @@ mod tests {
|
||||
UnwindInfo {
|
||||
prologue_size: 27,
|
||||
prologue_unwind_codes: vec![
|
||||
UnwindCode::StackAlloc { offset: 2, size: 8 },
|
||||
UnwindCode::SaveRegister {
|
||||
offset: 2,
|
||||
reg: RU::rbp.into(),
|
||||
stack_offset: 0,
|
||||
},
|
||||
UnwindCode::SetFramePointer {
|
||||
offset: 5,
|
||||
reg: RU::rbp.into(),
|
||||
},
|
||||
UnwindCode::StackAlloc {
|
||||
offset: 27,
|
||||
size: 10000
|
||||
}
|
||||
(2, UnwindCode::StackAlloc { size: 8 }),
|
||||
(
|
||||
2,
|
||||
UnwindCode::SaveRegister {
|
||||
reg: RU::rbp.into(),
|
||||
stack_offset: 0,
|
||||
}
|
||||
),
|
||||
(
|
||||
5,
|
||||
UnwindCode::SetFramePointer {
|
||||
reg: RU::rbp.into(),
|
||||
}
|
||||
),
|
||||
(27, UnwindCode::StackAlloc { size: 10000 })
|
||||
],
|
||||
epilogues_unwind_codes: vec![vec![
|
||||
UnwindCode::StackDealloc {
|
||||
offset: 34,
|
||||
size: 10000
|
||||
},
|
||||
UnwindCode::RestoreRegister {
|
||||
offset: 36,
|
||||
reg: RU::rbp.into()
|
||||
},
|
||||
UnwindCode::StackDealloc {
|
||||
offset: 36,
|
||||
size: 8
|
||||
}
|
||||
(34, UnwindCode::StackDealloc { size: 10000 }),
|
||||
(
|
||||
36,
|
||||
UnwindCode::RestoreRegister {
|
||||
reg: RU::rbp.into()
|
||||
}
|
||||
),
|
||||
(36, UnwindCode::StackDealloc { size: 8 }),
|
||||
(36, UnwindCode::RestoreFramePointer)
|
||||
]],
|
||||
function_size: 37,
|
||||
word_size: 8,
|
||||
initial_sp_offset: 8,
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -387,37 +393,36 @@ mod tests {
|
||||
UnwindInfo {
|
||||
prologue_size: 27,
|
||||
prologue_unwind_codes: vec![
|
||||
UnwindCode::StackAlloc { offset: 2, size: 8 },
|
||||
UnwindCode::SaveRegister {
|
||||
offset: 2,
|
||||
reg: RU::rbp.into(),
|
||||
stack_offset: 0,
|
||||
},
|
||||
UnwindCode::SetFramePointer {
|
||||
offset: 5,
|
||||
reg: RU::rbp.into(),
|
||||
},
|
||||
UnwindCode::StackAlloc {
|
||||
offset: 27,
|
||||
size: 1000000
|
||||
}
|
||||
(2, UnwindCode::StackAlloc { size: 8 }),
|
||||
(
|
||||
2,
|
||||
UnwindCode::SaveRegister {
|
||||
reg: RU::rbp.into(),
|
||||
stack_offset: 0,
|
||||
}
|
||||
),
|
||||
(
|
||||
5,
|
||||
UnwindCode::SetFramePointer {
|
||||
reg: RU::rbp.into(),
|
||||
}
|
||||
),
|
||||
(27, UnwindCode::StackAlloc { size: 1000000 })
|
||||
],
|
||||
epilogues_unwind_codes: vec![vec![
|
||||
UnwindCode::StackDealloc {
|
||||
offset: 34,
|
||||
size: 1000000
|
||||
},
|
||||
UnwindCode::RestoreRegister {
|
||||
offset: 36,
|
||||
reg: RU::rbp.into()
|
||||
},
|
||||
UnwindCode::StackDealloc {
|
||||
offset: 36,
|
||||
size: 8
|
||||
}
|
||||
(34, UnwindCode::StackDealloc { size: 1000000 }),
|
||||
(
|
||||
36,
|
||||
UnwindCode::RestoreRegister {
|
||||
reg: RU::rbp.into()
|
||||
}
|
||||
),
|
||||
(36, UnwindCode::StackDealloc { size: 8 }),
|
||||
(36, UnwindCode::RestoreFramePointer)
|
||||
]],
|
||||
function_size: 37,
|
||||
word_size: 8,
|
||||
initial_sp_offset: 8,
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -458,43 +463,48 @@ mod tests {
|
||||
UnwindInfo {
|
||||
prologue_size: 5,
|
||||
prologue_unwind_codes: vec![
|
||||
UnwindCode::StackAlloc { offset: 2, size: 8 },
|
||||
UnwindCode::SaveRegister {
|
||||
offset: 2,
|
||||
reg: RU::rbp.into(),
|
||||
stack_offset: 0,
|
||||
},
|
||||
UnwindCode::SetFramePointer {
|
||||
offset: 5,
|
||||
reg: RU::rbp.into()
|
||||
}
|
||||
(2, UnwindCode::StackAlloc { size: 8 }),
|
||||
(
|
||||
2,
|
||||
UnwindCode::SaveRegister {
|
||||
reg: RU::rbp.into(),
|
||||
stack_offset: 0,
|
||||
}
|
||||
),
|
||||
(
|
||||
5,
|
||||
UnwindCode::SetFramePointer {
|
||||
reg: RU::rbp.into()
|
||||
}
|
||||
)
|
||||
],
|
||||
epilogues_unwind_codes: vec![
|
||||
vec![
|
||||
UnwindCode::RememberState { offset: 12 },
|
||||
UnwindCode::RestoreRegister {
|
||||
offset: 12,
|
||||
reg: RU::rbp.into()
|
||||
},
|
||||
UnwindCode::StackDealloc {
|
||||
offset: 12,
|
||||
size: 8
|
||||
},
|
||||
UnwindCode::RestoreState { offset: 13 }
|
||||
(12, UnwindCode::RememberState),
|
||||
(
|
||||
12,
|
||||
UnwindCode::RestoreRegister {
|
||||
reg: RU::rbp.into()
|
||||
}
|
||||
),
|
||||
(12, UnwindCode::StackDealloc { size: 8 }),
|
||||
(12, UnwindCode::RestoreFramePointer),
|
||||
(13, UnwindCode::RestoreState)
|
||||
],
|
||||
vec![
|
||||
UnwindCode::RestoreRegister {
|
||||
offset: 15,
|
||||
reg: RU::rbp.into()
|
||||
},
|
||||
UnwindCode::StackDealloc {
|
||||
offset: 15,
|
||||
size: 8
|
||||
}
|
||||
(
|
||||
15,
|
||||
UnwindCode::RestoreRegister {
|
||||
reg: RU::rbp.into()
|
||||
}
|
||||
),
|
||||
(15, UnwindCode::StackDealloc { size: 8 }),
|
||||
(15, UnwindCode::RestoreFramePointer)
|
||||
]
|
||||
],
|
||||
function_size: 16,
|
||||
word_size: 8,
|
||||
initial_sp_offset: 8,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ pub(crate) fn create_unwind_info(
|
||||
fn map(&self, reg: RegUnit) -> Result<u16, RegisterMappingError> {
|
||||
Ok(map_reg(self.0, reg)?.0)
|
||||
}
|
||||
fn rsp(&self) -> u16 {
|
||||
fn sp(&self) -> u16 {
|
||||
X86_64::RSP.0
|
||||
}
|
||||
}
|
||||
@@ -165,7 +165,7 @@ mod tests {
|
||||
_ => 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 {
|
||||
@@ -205,7 +205,7 @@ mod tests {
|
||||
_ => 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 {
|
||||
|
||||
@@ -129,7 +129,7 @@ impl TargetIsa for TargetIsaAdapter {
|
||||
self.backend.unsigned_sub_overflow_condition()
|
||||
}
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
#[cfg(all(feature = "unwind", feature = "x64"))]
|
||||
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
|
||||
self.backend.create_systemv_cie()
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
use crate::binemit::{CodeInfo, CodeOffset, StackMap};
|
||||
use crate::ir::condcodes::IntCC;
|
||||
use crate::ir::{Function, Type};
|
||||
use crate::isa::unwind;
|
||||
use crate::isa::unwind::input as unwind_input;
|
||||
use crate::result::CodegenResult;
|
||||
use crate::settings::Flags;
|
||||
|
||||
@@ -314,7 +314,7 @@ pub struct MachCompileResult {
|
||||
/// Disassembly, if requested.
|
||||
pub disasm: Option<String>,
|
||||
/// Unwind info.
|
||||
pub unwind_info: Option<unwind::UnwindInfo>,
|
||||
pub unwind_info: Option<unwind_input::UnwindInfo<Reg>>,
|
||||
}
|
||||
|
||||
impl MachCompileResult {
|
||||
@@ -360,6 +360,17 @@ pub trait MachBackend {
|
||||
/// Condition that will be true when an IsubIfcout overflows.
|
||||
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.
|
||||
/// Creates a new System V Common Information Entry for the ISA.
|
||||
#[cfg(feature = "unwind")]
|
||||
@@ -403,6 +414,5 @@ pub trait UnwindInfoGenerator<I: MachInstEmit> {
|
||||
/// emitted instructions.
|
||||
fn create_unwind_info(
|
||||
context: UnwindInfoContext<I>,
|
||||
kind: UnwindInfoKind,
|
||||
) -> CodegenResult<Option<unwind::UnwindInfo>>;
|
||||
) -> CodegenResult<Option<unwind_input::UnwindInfo<Reg>>>;
|
||||
}
|
||||
|
||||
@@ -560,7 +560,7 @@ impl<I: VCodeInst> VCode<I> {
|
||||
/// Generates unwind info.
|
||||
pub fn unwind_info(
|
||||
&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 (prologue, epilogues) = self.prologue_epilogue_ranges.as_ref().unwrap();
|
||||
let context = UnwindInfoContext {
|
||||
@@ -570,7 +570,7 @@ impl<I: VCodeInst> VCode<I> {
|
||||
prologue: prologue.clone(),
|
||||
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.
|
||||
|
||||
@@ -37,8 +37,8 @@ block0:
|
||||
; nextln: DW_CFA_advance_loc (3)
|
||||
; nextln: DW_CFA_def_cfa_register (r6)
|
||||
; nextln: DW_CFA_advance_loc (1)
|
||||
; nextln: DW_CFA_def_cfa (r7, 8)
|
||||
; nextln: DW_CFA_same_value (r6)
|
||||
; nextln: DW_CFA_def_cfa (r7, 8)
|
||||
; nextln: DW_CFA_nop
|
||||
|
||||
; check a function with medium-sized stack alloc
|
||||
@@ -76,8 +76,8 @@ block0:
|
||||
; nextln: DW_CFA_advance_loc (3)
|
||||
; nextln: DW_CFA_def_cfa_register (r6)
|
||||
; nextln: DW_CFA_advance_loc (21)
|
||||
; nextln: DW_CFA_def_cfa (r7, 8)
|
||||
; nextln: DW_CFA_same_value (r6)
|
||||
; nextln: DW_CFA_def_cfa (r7, 8)
|
||||
; nextln: DW_CFA_nop
|
||||
|
||||
; check a function with large-sized stack alloc
|
||||
@@ -115,8 +115,8 @@ block0:
|
||||
; nextln: DW_CFA_advance_loc (3)
|
||||
; nextln: DW_CFA_def_cfa_register (r6)
|
||||
; nextln: DW_CFA_advance_loc (21)
|
||||
; nextln: DW_CFA_def_cfa (r7, 8)
|
||||
; nextln: DW_CFA_same_value (r6)
|
||||
; nextln: DW_CFA_def_cfa (r7, 8)
|
||||
; nextln: DW_CFA_nop
|
||||
; nextln:
|
||||
|
||||
@@ -200,7 +200,6 @@ block0(v0: i64, v1: i64):
|
||||
; 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_same_value (r6)
|
||||
; nextln: DW_CFA_nop
|
||||
; nextln: DW_CFA_def_cfa (r7, 8)
|
||||
; nextln: DW_CFA_nop
|
||||
|
||||
Reference in New Issue
Block a user