machinst x64: New backend unwind (#2266)
Addresses unwind for experimental x64 backend. The preliminary code enables backtrace on SystemV call convension.
This commit is contained in:
@@ -155,6 +155,9 @@ pub trait ABICallee {
|
||||
from_slot: SpillSlot,
|
||||
ty: Option<Type>,
|
||||
) -> Self::I;
|
||||
|
||||
/// Desired unwind info type.
|
||||
fn unwind_info_kind(&self) -> UnwindInfoKind;
|
||||
}
|
||||
|
||||
/// Trait implemented by an object that tracks ABI-related state and can
|
||||
|
||||
@@ -1034,6 +1034,18 @@ impl<M: ABIMachineSpec> ABICallee for ABICalleeImpl<M> {
|
||||
let ty = ty_from_ty_hint_or_reg_class::<M>(to_reg.to_reg().to_reg(), ty);
|
||||
self.load_spillslot(from_slot, ty, to_reg.map(|r| r.to_reg()))
|
||||
}
|
||||
|
||||
fn unwind_info_kind(&self) -> UnwindInfoKind {
|
||||
match self.sig.call_conv {
|
||||
#[cfg(feature = "unwind")]
|
||||
isa::CallConv::Fast | isa::CallConv::Cold | isa::CallConv::SystemV => {
|
||||
UnwindInfoKind::SystemV
|
||||
}
|
||||
#[cfg(feature = "unwind")]
|
||||
isa::CallConv::WindowsFastcall => UnwindInfoKind::Windows,
|
||||
_ => UnwindInfoKind::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn abisig_to_uses_and_defs<M: ABIMachineSpec>(sig: &ABISig) -> (Vec<Reg>, Vec<Writable<Reg>>) {
|
||||
|
||||
@@ -129,6 +129,11 @@ impl TargetIsa for TargetIsaAdapter {
|
||||
self.backend.unsigned_sub_overflow_condition()
|
||||
}
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
|
||||
self.backend.create_systemv_cie()
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self as &dyn Any
|
||||
}
|
||||
|
||||
@@ -99,12 +99,14 @@
|
||||
use crate::binemit::{CodeInfo, CodeOffset, StackMap};
|
||||
use crate::ir::condcodes::IntCC;
|
||||
use crate::ir::{Function, Type};
|
||||
use crate::isa::unwind;
|
||||
use crate::result::CodegenResult;
|
||||
use crate::settings::Flags;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec::Vec;
|
||||
use core::fmt::Debug;
|
||||
use core::ops::Range;
|
||||
use regalloc::RegUsageCollector;
|
||||
use regalloc::{
|
||||
RealReg, RealRegUniverse, Reg, RegClass, RegUsageMapper, SpillSlot, VirtualReg, Writable,
|
||||
@@ -277,6 +279,8 @@ pub trait MachInstEmit: MachInst {
|
||||
type State: MachInstEmitState<Self>;
|
||||
/// Constant information used in `emit` invocations.
|
||||
type Info: MachInstEmitInfo;
|
||||
/// Unwind info generator.
|
||||
type UnwindInfo: UnwindInfoGenerator<Self>;
|
||||
/// Emit the instruction.
|
||||
fn emit(&self, code: &mut MachBuffer<Self>, info: &Self::Info, state: &mut Self::State);
|
||||
/// Pretty-print the instruction.
|
||||
@@ -309,6 +313,8 @@ pub struct MachCompileResult {
|
||||
pub frame_size: u32,
|
||||
/// Disassembly, if requested.
|
||||
pub disasm: Option<String>,
|
||||
/// Unwind info.
|
||||
pub unwind_info: Option<unwind::UnwindInfo>,
|
||||
}
|
||||
|
||||
impl MachCompileResult {
|
||||
@@ -353,4 +359,50 @@ pub trait MachBackend {
|
||||
/// Machine-specific condcode info needed by TargetIsa.
|
||||
/// Condition that will be true when an IsubIfcout overflows.
|
||||
fn unsigned_sub_overflow_condition(&self) -> IntCC;
|
||||
|
||||
/// Machine-specific condcode info needed by TargetIsa.
|
||||
/// Creates a new System V Common Information Entry for the ISA.
|
||||
#[cfg(feature = "unwind")]
|
||||
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
|
||||
// By default, an ISA cannot create a System V CIE
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Expected unwind info type.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub enum UnwindInfoKind {
|
||||
/// No unwind info.
|
||||
None,
|
||||
/// SystemV CIE/FDE unwind info.
|
||||
#[cfg(feature = "unwind")]
|
||||
SystemV,
|
||||
/// Windows X64 Unwind info
|
||||
#[cfg(feature = "unwind")]
|
||||
Windows,
|
||||
}
|
||||
|
||||
/// Input data for UnwindInfoGenerator.
|
||||
pub struct UnwindInfoContext<'a, Inst: MachInstEmit> {
|
||||
/// Function instructions.
|
||||
pub insts: &'a [Inst],
|
||||
/// Instruction layout: end offsets
|
||||
pub insts_layout: &'a [CodeOffset],
|
||||
/// Length of the function.
|
||||
pub len: CodeOffset,
|
||||
/// Prologue range.
|
||||
pub prologue: Range<u32>,
|
||||
/// Epilogue ranges.
|
||||
pub epilogues: &'a [Range<u32>],
|
||||
}
|
||||
|
||||
/// UnwindInfo generator/helper.
|
||||
pub trait UnwindInfoGenerator<I: MachInstEmit> {
|
||||
/// Creates unwind info based on function signature and
|
||||
/// emitted instructions.
|
||||
fn create_unwind_info(
|
||||
context: UnwindInfoContext<I>,
|
||||
kind: UnwindInfoKind,
|
||||
) -> CodegenResult<Option<unwind::UnwindInfo>>;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ use regalloc::{
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::{borrow::Cow, vec::Vec};
|
||||
use std::cell::RefCell;
|
||||
use std::fmt;
|
||||
use std::iter;
|
||||
use std::string::String;
|
||||
@@ -39,6 +40,8 @@ use std::string::String;
|
||||
pub type InsnIndex = u32;
|
||||
/// Index referring to a basic block in VCode.
|
||||
pub type BlockIndex = u32;
|
||||
/// Range of an instructions in VCode.
|
||||
pub type InsnRange = core::ops::Range<InsnIndex>;
|
||||
|
||||
/// VCodeInst wraps all requirements for a MachInst to be in VCode: it must be
|
||||
/// a `MachInst` and it must be able to emit itself at least to a `SizeCodeSink`.
|
||||
@@ -101,6 +104,12 @@ pub struct VCode<I: VCodeInst> {
|
||||
/// These are used to generate actual stack maps at emission. Filled in
|
||||
/// post-regalloc.
|
||||
safepoint_slots: Vec<Vec<SpillSlot>>,
|
||||
|
||||
/// Ranges for prologue and epilogue instructions.
|
||||
prologue_epilogue_ranges: Option<(InsnRange, Box<[InsnRange]>)>,
|
||||
|
||||
/// Instruction end offsets
|
||||
insts_layout: RefCell<(Vec<u32>, u32)>,
|
||||
}
|
||||
|
||||
/// A builder for a VCode function body. This builder is designed for the
|
||||
@@ -292,6 +301,8 @@ impl<I: VCodeInst> VCode<I> {
|
||||
emit_info,
|
||||
safepoint_insns: vec![],
|
||||
safepoint_slots: vec![],
|
||||
prologue_epilogue_ranges: None,
|
||||
insts_layout: RefCell::new((vec![], 0)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,6 +364,10 @@ impl<I: VCodeInst> VCode<I> {
|
||||
let mut final_safepoint_insns = vec![];
|
||||
let mut safept_idx = 0;
|
||||
|
||||
let mut prologue_start = None;
|
||||
let mut prologue_end = None;
|
||||
let mut epilogue_islands = vec![];
|
||||
|
||||
assert!(result.target_map.elems().len() == self.num_blocks());
|
||||
for block in 0..self.num_blocks() {
|
||||
let start = result.target_map.elems()[block].get() as usize;
|
||||
@@ -365,11 +380,13 @@ impl<I: VCodeInst> VCode<I> {
|
||||
let final_start = final_insns.len() as InsnIndex;
|
||||
|
||||
if block == self.entry {
|
||||
prologue_start = Some(final_insns.len() as InsnIndex);
|
||||
// Start with the prologue.
|
||||
let prologue = self.abi.gen_prologue();
|
||||
let len = prologue.len();
|
||||
final_insns.extend(prologue.into_iter());
|
||||
final_srclocs.extend(iter::repeat(SourceLoc::default()).take(len));
|
||||
prologue_end = Some(final_insns.len() as InsnIndex);
|
||||
}
|
||||
|
||||
for i in start..end {
|
||||
@@ -395,10 +412,12 @@ impl<I: VCodeInst> VCode<I> {
|
||||
// with the epilogue.
|
||||
let is_ret = insn.is_term() == MachTerminator::Ret;
|
||||
if is_ret {
|
||||
let epilogue_start = final_insns.len() as InsnIndex;
|
||||
let epilogue = self.abi.gen_epilogue();
|
||||
let len = epilogue.len();
|
||||
final_insns.extend(epilogue.into_iter());
|
||||
final_srclocs.extend(iter::repeat(srcloc).take(len));
|
||||
epilogue_islands.push(epilogue_start..final_insns.len() as InsnIndex);
|
||||
} else {
|
||||
final_insns.push(insn.clone());
|
||||
final_srclocs.push(srcloc);
|
||||
@@ -430,6 +449,11 @@ impl<I: VCodeInst> VCode<I> {
|
||||
// for the machine backend during emission so that it can do
|
||||
// target-specific translations of slot numbers to stack offsets.
|
||||
self.safepoint_slots = result.stackmaps;
|
||||
|
||||
self.prologue_epilogue_ranges = Some((
|
||||
prologue_start.unwrap()..prologue_end.unwrap(),
|
||||
epilogue_islands.into_boxed_slice(),
|
||||
));
|
||||
}
|
||||
|
||||
/// Emit the instructions to a `MachBuffer`, containing fixed-up code and external
|
||||
@@ -444,6 +468,8 @@ impl<I: VCodeInst> VCode<I> {
|
||||
|
||||
buffer.reserve_labels_for_blocks(self.num_blocks() as BlockIndex); // first N MachLabels are simply block indices.
|
||||
|
||||
let mut insts_layout = vec![0; self.insts.len()];
|
||||
|
||||
let mut safepoint_idx = 0;
|
||||
let mut cur_srcloc = None;
|
||||
for block in 0..self.num_blocks() {
|
||||
@@ -482,6 +508,8 @@ impl<I: VCodeInst> VCode<I> {
|
||||
}
|
||||
|
||||
self.insts[iix as usize].emit(&mut buffer, &self.emit_info, &mut state);
|
||||
|
||||
insts_layout[iix as usize] = buffer.cur_offset();
|
||||
}
|
||||
|
||||
if cur_srcloc.is_some() {
|
||||
@@ -502,9 +530,27 @@ impl<I: VCodeInst> VCode<I> {
|
||||
}
|
||||
}
|
||||
|
||||
*self.insts_layout.borrow_mut() = (insts_layout, buffer.cur_offset());
|
||||
|
||||
buffer
|
||||
}
|
||||
|
||||
/// Generates unwind info.
|
||||
pub fn unwind_info(
|
||||
&self,
|
||||
) -> crate::result::CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
|
||||
let layout = &self.insts_layout.borrow();
|
||||
let (prologue, epilogues) = self.prologue_epilogue_ranges.as_ref().unwrap();
|
||||
let context = UnwindInfoContext {
|
||||
insts: &self.insts,
|
||||
insts_layout: &layout.0,
|
||||
len: layout.1,
|
||||
prologue: prologue.clone(),
|
||||
epilogues,
|
||||
};
|
||||
I::UnwindInfo::create_unwind_info(context, self.abi.unwind_info_kind())
|
||||
}
|
||||
|
||||
/// Get the IR block for a BlockIndex, if one exists.
|
||||
pub fn bindex_to_bb(&self, block: BlockIndex) -> Option<ir::Block> {
|
||||
self.block_order.lowered_order()[block as usize].orig_block()
|
||||
|
||||
Reference in New Issue
Block a user