use crate::isa::unwind::input;
use crate::result::{CodegenError, CodegenResult};
use alloc::vec::Vec;
use gimli::write::{Address, FrameDescriptionEntry};
use thiserror::Error;
#[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize};
type Register = u16;
#[allow(missing_docs)]
#[derive(Error, Debug, PartialEq, Eq)]
pub enum RegisterMappingError {
#[error("unable to find bank for register info")]
MissingBank,
#[error("register mapping is currently only implemented for x86_64")]
UnsupportedArchitecture,
#[error("unsupported register bank: {0}")]
UnsupportedRegisterBank(&'static str),
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub(crate) enum CallFrameInstruction {
Cfa(Register, i32),
CfaRegister(Register),
CfaOffset(i32),
Restore(Register),
Undefined(Register),
SameValue(Register),
Offset(Register, i32),
ValOffset(Register, i32),
Register(Register, Register),
RememberState,
RestoreState,
ArgsSize(u32),
}
impl From<gimli::write::CallFrameInstruction> for CallFrameInstruction {
fn from(cfi: gimli::write::CallFrameInstruction) -> Self {
use gimli::write::CallFrameInstruction;
match cfi {
CallFrameInstruction::Cfa(reg, offset) => Self::Cfa(reg.0, offset),
CallFrameInstruction::CfaRegister(reg) => Self::CfaRegister(reg.0),
CallFrameInstruction::CfaOffset(offset) => Self::CfaOffset(offset),
CallFrameInstruction::Restore(reg) => Self::Restore(reg.0),
CallFrameInstruction::Undefined(reg) => Self::Undefined(reg.0),
CallFrameInstruction::SameValue(reg) => Self::SameValue(reg.0),
CallFrameInstruction::Offset(reg, offset) => Self::Offset(reg.0, offset),
CallFrameInstruction::ValOffset(reg, offset) => Self::ValOffset(reg.0, offset),
CallFrameInstruction::Register(reg1, reg2) => Self::Register(reg1.0, reg2.0),
CallFrameInstruction::RememberState => Self::RememberState,
CallFrameInstruction::RestoreState => Self::RestoreState,
CallFrameInstruction::ArgsSize(size) => Self::ArgsSize(size),
_ => {
panic!("CallFrameInstruction with Expression not supported");
}
}
}
}
impl Into<gimli::write::CallFrameInstruction> for CallFrameInstruction {
fn into(self) -> gimli::write::CallFrameInstruction {
use gimli::{write::CallFrameInstruction, Register};
match self {
Self::Cfa(reg, offset) => CallFrameInstruction::Cfa(Register(reg), offset),
Self::CfaRegister(reg) => CallFrameInstruction::CfaRegister(Register(reg)),
Self::CfaOffset(offset) => CallFrameInstruction::CfaOffset(offset),
Self::Restore(reg) => CallFrameInstruction::Restore(Register(reg)),
Self::Undefined(reg) => CallFrameInstruction::Undefined(Register(reg)),
Self::SameValue(reg) => CallFrameInstruction::SameValue(Register(reg)),
Self::Offset(reg, offset) => CallFrameInstruction::Offset(Register(reg), offset),
Self::ValOffset(reg, offset) => CallFrameInstruction::ValOffset(Register(reg), offset),
Self::Register(reg1, reg2) => {
CallFrameInstruction::Register(Register(reg1), Register(reg2))
}
Self::RememberState => CallFrameInstruction::RememberState,
Self::RestoreState => CallFrameInstruction::RestoreState,
Self::ArgsSize(size) => CallFrameInstruction::ArgsSize(size),
}
}
}
pub(crate) trait RegisterMapper<Reg> {
fn map(&self, reg: Reg) -> Result<Register, RegisterMappingError>;
fn sp(&self) -> Register;
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct UnwindInfo {
instructions: Vec<(u32, CallFrameInstruction)>,
len: u32,
}
impl UnwindInfo {
pub(crate) fn build<'b, Reg: PartialEq + Copy>(
unwind: input::UnwindInfo<Reg>,
map_reg: &'b dyn RegisterMapper<Reg>,
) -> CodegenResult<Self> {
use input::UnwindCode;
let mut builder = InstructionBuilder::new(unwind.initial_sp_offset, map_reg);
for (offset, c) in unwind.prologue_unwind_codes.iter().chain(
unwind
.epilogues_unwind_codes
.iter()
.map(|c| c.iter())
.flatten(),
) {
match c {
UnwindCode::SaveRegister { reg, stack_offset } => {
builder
.save_reg(*offset, *reg, *stack_offset)
.map_err(CodegenError::RegisterMappingError)?;
}
UnwindCode::StackAlloc { size } => {
builder.adjust_sp_down_imm(*offset, *size as i64);
}
UnwindCode::StackDealloc { size } => {
builder.adjust_sp_up_imm(*offset, *size as i64);
}
UnwindCode::RestoreRegister { reg } => {
builder
.restore_reg(*offset, *reg)
.map_err(CodegenError::RegisterMappingError)?;
}
UnwindCode::SetFramePointer { reg } => {
builder
.set_cfa_reg(*offset, *reg)
.map_err(CodegenError::RegisterMappingError)?;
}
UnwindCode::RestoreFramePointer => {
builder.restore_cfa(*offset);
}
UnwindCode::RememberState => {
builder.remember_state(*offset);
}
UnwindCode::RestoreState => {
builder.restore_state(*offset);
}
}
}
let instructions = builder.instructions;
let len = unwind.function_size;
Ok(Self { instructions, len })
}
pub fn to_fde(&self, address: Address) -> gimli::write::FrameDescriptionEntry {
let mut fde = FrameDescriptionEntry::new(address, self.len);
for (offset, inst) in &self.instructions {
fde.add_instruction(*offset, inst.clone().into());
}
fde
}
}
struct InstructionBuilder<'a, Reg: PartialEq + Copy> {
sp_offset: i32,
frame_register: Option<Reg>,
saved_state: Option<(i32, Option<Reg>)>,
map_reg: &'a dyn RegisterMapper<Reg>,
instructions: Vec<(u32, CallFrameInstruction)>,
}
impl<'a, Reg: PartialEq + Copy> InstructionBuilder<'a, Reg> {
fn new(sp_offset: u8, map_reg: &'a (dyn RegisterMapper<Reg> + 'a)) -> Self {
Self {
sp_offset: sp_offset as i32,
saved_state: None,
frame_register: None,
map_reg,
instructions: Vec::new(),
}
}
fn save_reg(
&mut self,
offset: u32,
reg: Reg,
stack_offset: u32,
) -> Result<(), RegisterMappingError> {
self.instructions.push((
offset,
CallFrameInstruction::Offset(
self.map_reg.map(reg)?,
stack_offset as i32 - self.sp_offset,
),
));
Ok(())
}
fn adjust_sp_down_imm(&mut self, offset: u32, imm: i64) {
assert!(imm <= core::u32::MAX as i64);
self.sp_offset += imm as i32;
if self.frame_register.is_some() {
return;
}
self.instructions
.push((offset, CallFrameInstruction::CfaOffset(self.sp_offset)));
}
fn adjust_sp_up_imm(&mut self, offset: u32, imm: i64) {
assert!(imm <= core::u32::MAX as i64);
self.sp_offset -= imm as i32;
if self.frame_register.is_some() {
return;
}
let cfa_inst_ofs = {
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 {
*o = self.sp_offset;
} else {
self.instructions
.push((offset, CallFrameInstruction::CfaOffset(self.sp_offset)));
}
}
fn set_cfa_reg(&mut self, offset: u32, reg: Reg) -> Result<(), RegisterMappingError> {
self.instructions.push((
offset,
CallFrameInstruction::CfaRegister(self.map_reg.map(reg)?),
));
self.frame_register = Some(reg);
Ok(())
}
fn restore_cfa(&mut self, offset: u32) {
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> {
self.instructions.push((
offset,
CallFrameInstruction::SameValue(self.map_reg.map(reg)?),
));
Ok(())
}
fn remember_state(&mut self, offset: u32) {
self.saved_state = Some((self.sp_offset, self.frame_register));
self.instructions
.push((offset, CallFrameInstruction::RememberState));
}
fn restore_state(&mut self, offset: u32) {
let (sp_offset, frame_register) = self.saved_state.take().unwrap();
self.sp_offset = sp_offset;
self.frame_register = frame_register;
self.instructions
.push((offset, CallFrameInstruction::RestoreState));
}
}