This end result was previously enacted by carrying a `SourceLoc` on every load/store, which was somewhat cumbersome, and only indirectly encoded metadata about a memory reference (can it trap) by its presence or absence. We have a type for this -- `MemFlags` -- that tells us everything we might want to know about a load or store, and we should plumb it through to code emission instead. This PR attaches a `MemFlags` to an `Amode` on x64, and puts it on load and store `Inst` variants on aarch64. These two choices seem to factor things out in the nicest way: there are relatively few load/store insts on aarch64 but many addressing modes, while the opposite is true on x64.
126 lines
4.3 KiB
Rust
126 lines
4.3 KiB
Rust
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(crate) mod systemv;
|
|
|
|
pub struct X64UnwindInfo;
|
|
|
|
impl UnwindInfoGenerator<Inst> for X64UnwindInfo {
|
|
fn create_unwind_info(
|
|
context: UnwindInfoContext<Inst>,
|
|
) -> 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 }));
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
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,
|
|
}))
|
|
}
|
|
}
|