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:
@@ -253,6 +253,14 @@ impl Context {
|
|||||||
&self,
|
&self,
|
||||||
isa: &dyn TargetIsa,
|
isa: &dyn TargetIsa,
|
||||||
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
|
) -> 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());
|
||||||
|
}
|
||||||
isa.create_unwind_info(&self.func)
|
isa.create_unwind_info(&self.func)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -481,6 +481,7 @@ impl MachInstEmitInfo for EmitInfo {
|
|||||||
impl MachInstEmit for Inst {
|
impl MachInstEmit for Inst {
|
||||||
type State = EmitState;
|
type State = EmitState;
|
||||||
type Info = EmitInfo;
|
type Info = EmitInfo;
|
||||||
|
type UnwindInfo = super::unwind::AArch64UnwindInfo;
|
||||||
|
|
||||||
fn emit(&self, sink: &mut MachBuffer<Inst>, emit_info: &Self::Info, state: &mut EmitState) {
|
fn emit(&self, sink: &mut MachBuffer<Inst>, emit_info: &Self::Info, state: &mut EmitState) {
|
||||||
// N.B.: we *must* not exceed the "worst-case size" used to compute
|
// N.B.: we *must* not exceed the "worst-case size" used to compute
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ pub mod args;
|
|||||||
pub use self::args::*;
|
pub use self::args::*;
|
||||||
pub mod emit;
|
pub mod emit;
|
||||||
pub use self::emit::*;
|
pub use self::emit::*;
|
||||||
|
pub mod unwind;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod emit_tests;
|
mod emit_tests;
|
||||||
|
|||||||
15
cranelift/codegen/src/isa/aarch64/inst/unwind.rs
Normal file
15
cranelift/codegen/src/isa/aarch64/inst/unwind.rs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
use super::*;
|
||||||
|
use crate::isa::unwind::UnwindInfo;
|
||||||
|
use crate::result::CodegenResult;
|
||||||
|
|
||||||
|
pub struct AArch64UnwindInfo;
|
||||||
|
|
||||||
|
impl UnwindInfoGenerator<Inst> for AArch64UnwindInfo {
|
||||||
|
fn create_unwind_info(
|
||||||
|
_context: UnwindInfoContext<Inst>,
|
||||||
|
_kind: UnwindInfoKind,
|
||||||
|
) -> CodegenResult<Option<UnwindInfo>> {
|
||||||
|
// TODO
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -77,6 +77,7 @@ impl MachBackend for AArch64Backend {
|
|||||||
buffer,
|
buffer,
|
||||||
frame_size,
|
frame_size,
|
||||||
disasm,
|
disasm,
|
||||||
|
unwind_info: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -274,6 +274,7 @@ impl MachInstEmitInfo for EmitInfo {
|
|||||||
impl MachInstEmit for Inst {
|
impl MachInstEmit for Inst {
|
||||||
type Info = EmitInfo;
|
type Info = EmitInfo;
|
||||||
type State = EmitState;
|
type State = EmitState;
|
||||||
|
type UnwindInfo = super::unwind::Arm32UnwindInfo;
|
||||||
|
|
||||||
fn emit(&self, sink: &mut MachBuffer<Inst>, emit_info: &Self::Info, state: &mut EmitState) {
|
fn emit(&self, sink: &mut MachBuffer<Inst>, emit_info: &Self::Info, state: &mut EmitState) {
|
||||||
let start_off = sink.cur_offset();
|
let start_off = sink.cur_offset();
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ mod emit;
|
|||||||
pub use self::emit::*;
|
pub use self::emit::*;
|
||||||
mod regs;
|
mod regs;
|
||||||
pub use self::regs::*;
|
pub use self::regs::*;
|
||||||
|
pub mod unwind;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod emit_tests;
|
mod emit_tests;
|
||||||
|
|||||||
15
cranelift/codegen/src/isa/arm32/inst/unwind.rs
Normal file
15
cranelift/codegen/src/isa/arm32/inst/unwind.rs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
use super::*;
|
||||||
|
use crate::isa::unwind::UnwindInfo;
|
||||||
|
use crate::result::CodegenResult;
|
||||||
|
|
||||||
|
pub struct Arm32UnwindInfo;
|
||||||
|
|
||||||
|
impl UnwindInfoGenerator<Inst> for Arm32UnwindInfo {
|
||||||
|
fn create_unwind_info(
|
||||||
|
_context: UnwindInfoContext<Inst>,
|
||||||
|
_kind: UnwindInfoKind,
|
||||||
|
) -> CodegenResult<Option<UnwindInfo>> {
|
||||||
|
// TODO
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -73,6 +73,7 @@ impl MachBackend for Arm32Backend {
|
|||||||
buffer,
|
buffer,
|
||||||
frame_size,
|
frame_size,
|
||||||
disasm,
|
disasm,
|
||||||
|
unwind_info: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,7 +87,6 @@ mod arm32;
|
|||||||
#[cfg(feature = "arm64")]
|
#[cfg(feature = "arm64")]
|
||||||
pub(crate) mod aarch64;
|
pub(crate) mod aarch64;
|
||||||
|
|
||||||
#[cfg(feature = "unwind")]
|
|
||||||
pub mod unwind;
|
pub mod unwind;
|
||||||
|
|
||||||
mod call_conv;
|
mod call_conv;
|
||||||
|
|||||||
@@ -2,16 +2,22 @@
|
|||||||
#[cfg(feature = "enable-serde")]
|
#[cfg(feature = "enable-serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[cfg(feature = "unwind")]
|
||||||
pub mod systemv;
|
pub mod systemv;
|
||||||
|
|
||||||
|
#[cfg(feature = "unwind")]
|
||||||
pub mod winx64;
|
pub mod winx64;
|
||||||
|
|
||||||
/// Represents unwind information for a single function.
|
/// Represents unwind information for a single function.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum UnwindInfo {
|
pub enum UnwindInfo {
|
||||||
/// Windows x64 ABI unwind information.
|
/// Windows x64 ABI unwind information.
|
||||||
|
#[cfg(feature = "unwind")]
|
||||||
WindowsX64(winx64::UnwindInfo),
|
WindowsX64(winx64::UnwindInfo),
|
||||||
/// System V ABI unwind information.
|
/// System V ABI unwind information.
|
||||||
|
#[cfg(feature = "unwind")]
|
||||||
SystemV(systemv::UnwindInfo),
|
SystemV(systemv::UnwindInfo),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! System V ABI unwind information.
|
//! System V ABI unwind information.
|
||||||
|
|
||||||
use crate::isa::{unwind::input, RegUnit};
|
use crate::isa::unwind::input;
|
||||||
use crate::result::{CodegenError, CodegenResult};
|
use crate::result::{CodegenError, CodegenResult};
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use gimli::write::{Address, FrameDescriptionEntry};
|
use gimli::write::{Address, FrameDescriptionEntry};
|
||||||
@@ -95,9 +95,9 @@ impl Into<gimli::write::CallFrameInstruction> for CallFrameInstruction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Maps UnwindInfo register to gimli's index space.
|
/// Maps UnwindInfo register to gimli's index space.
|
||||||
pub(crate) trait RegisterMapper {
|
pub(crate) trait RegisterMapper<Reg> {
|
||||||
/// Maps RegUnit.
|
/// Maps Reg.
|
||||||
fn map(&self, reg: RegUnit) -> Result<Register, RegisterMappingError>;
|
fn map(&self, reg: Reg) -> Result<Register, RegisterMappingError>;
|
||||||
/// Gets RSP in gimli's index space.
|
/// Gets RSP in gimli's index space.
|
||||||
fn rsp(&self) -> Register;
|
fn rsp(&self) -> Register;
|
||||||
}
|
}
|
||||||
@@ -113,9 +113,9 @@ pub struct UnwindInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl UnwindInfo {
|
impl UnwindInfo {
|
||||||
pub(crate) fn build<'b>(
|
pub(crate) fn build<'b, Reg: PartialEq + Copy>(
|
||||||
unwind: input::UnwindInfo<RegUnit>,
|
unwind: input::UnwindInfo<Reg>,
|
||||||
map_reg: &'b dyn RegisterMapper,
|
map_reg: &'b dyn RegisterMapper<Reg>,
|
||||||
) -> CodegenResult<Self> {
|
) -> CodegenResult<Self> {
|
||||||
use input::UnwindCode;
|
use input::UnwindCode;
|
||||||
let mut builder = InstructionBuilder::new(unwind.word_size, map_reg);
|
let mut builder = InstructionBuilder::new(unwind.word_size, map_reg);
|
||||||
@@ -181,16 +181,16 @@ impl UnwindInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct InstructionBuilder<'a> {
|
struct InstructionBuilder<'a, Reg: PartialEq + Copy> {
|
||||||
sp_offset: i32,
|
sp_offset: i32,
|
||||||
frame_register: Option<RegUnit>,
|
frame_register: Option<Reg>,
|
||||||
saved_state: Option<(i32, Option<RegUnit>)>,
|
saved_state: Option<(i32, Option<Reg>)>,
|
||||||
map_reg: &'a dyn RegisterMapper,
|
map_reg: &'a dyn RegisterMapper<Reg>,
|
||||||
instructions: Vec<(u32, CallFrameInstruction)>,
|
instructions: Vec<(u32, CallFrameInstruction)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> InstructionBuilder<'a> {
|
impl<'a, Reg: PartialEq + Copy> InstructionBuilder<'a, Reg> {
|
||||||
fn new(word_size: u8, map_reg: &'a (dyn RegisterMapper + 'a)) -> Self {
|
fn new(word_size: u8, map_reg: &'a (dyn RegisterMapper<Reg> + 'a)) -> Self {
|
||||||
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: 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,
|
||||||
@@ -200,7 +200,7 @@ impl<'a> InstructionBuilder<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save_reg(&mut self, offset: u32, reg: RegUnit) -> Result<(), RegisterMappingError> {
|
fn save_reg(&mut self, offset: u32, reg: Reg) -> Result<(), RegisterMappingError> {
|
||||||
// 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,
|
||||||
@@ -261,7 +261,7 @@ impl<'a> InstructionBuilder<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_cfa_reg(&mut self, offset: u32, reg: RegUnit) -> Result<(), RegisterMappingError> {
|
fn set_cfa_reg(&mut self, offset: u32, reg: Reg) -> Result<(), RegisterMappingError> {
|
||||||
self.instructions.push((
|
self.instructions.push((
|
||||||
offset,
|
offset,
|
||||||
CallFrameInstruction::CfaRegister(self.map_reg.map(reg)?),
|
CallFrameInstruction::CfaRegister(self.map_reg.map(reg)?),
|
||||||
@@ -270,7 +270,7 @@ impl<'a> InstructionBuilder<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn restore_reg(&mut self, offset: u32, reg: RegUnit) -> Result<(), RegisterMappingError> {
|
fn restore_reg(&mut self, offset: u32, reg: Reg) -> Result<(), RegisterMappingError> {
|
||||||
// Update the CFA if this is the restore of the frame pointer register.
|
// Update the CFA if this is the restore of the frame pointer register.
|
||||||
if Some(reg) == self.frame_register {
|
if Some(reg) == self.frame_register {
|
||||||
self.frame_register = None;
|
self.frame_register = None;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ mod emit;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod emit_tests;
|
mod emit_tests;
|
||||||
pub mod regs;
|
pub mod regs;
|
||||||
|
pub mod unwind;
|
||||||
|
|
||||||
use args::*;
|
use args::*;
|
||||||
use regs::{create_reg_universe_systemv, show_ireg_sized};
|
use regs::{create_reg_universe_systemv, show_ireg_sized};
|
||||||
@@ -2706,6 +2707,7 @@ impl MachInstEmitInfo for EmitInfo {
|
|||||||
impl MachInstEmit for Inst {
|
impl MachInstEmit for Inst {
|
||||||
type State = EmitState;
|
type State = EmitState;
|
||||||
type Info = EmitInfo;
|
type Info = EmitInfo;
|
||||||
|
type UnwindInfo = unwind::X64UnwindInfo;
|
||||||
|
|
||||||
fn emit(&self, sink: &mut MachBuffer<Inst>, info: &Self::Info, state: &mut Self::State) {
|
fn emit(&self, sink: &mut MachBuffer<Inst>, info: &Self::Info, state: &mut Self::State) {
|
||||||
emit::emit(self, sink, info, state);
|
emit::emit(self, sink, info, state);
|
||||||
|
|||||||
36
cranelift/codegen/src/isa/x64/inst/unwind.rs
Normal file
36
cranelift/codegen/src/isa/x64/inst/unwind.rs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
use super::Inst;
|
||||||
|
use crate::isa::unwind::UnwindInfo;
|
||||||
|
use crate::machinst::{UnwindInfoContext, UnwindInfoGenerator, UnwindInfoKind};
|
||||||
|
use crate::result::CodegenResult;
|
||||||
|
|
||||||
|
#[cfg(feature = "unwind")]
|
||||||
|
pub use self::systemv::create_cie;
|
||||||
|
|
||||||
|
#[cfg(feature = "unwind")]
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
#[cfg(feature = "unwind")]
|
||||||
|
UnwindInfoKind::Windows => {
|
||||||
|
//TODO winx64::create_unwind_info(context)?.map(|u| UnwindInfo::WindowsX64(u))
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
UnwindInfoKind::None => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
307
cranelift/codegen/src/isa/x64/inst/unwind/systemv.rs
Normal file
307
cranelift/codegen/src/isa/x64/inst/unwind/systemv.rs
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
//! Unwind information for System V ABI (x86-64).
|
||||||
|
|
||||||
|
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};
|
||||||
|
|
||||||
|
/// Creates a new x86-64 common information entry (CIE).
|
||||||
|
pub fn create_cie() -> CommonInformationEntry {
|
||||||
|
use gimli::write::CallFrameInstruction;
|
||||||
|
|
||||||
|
let mut entry = CommonInformationEntry::new(
|
||||||
|
Encoding {
|
||||||
|
address_size: 8,
|
||||||
|
format: Format::Dwarf32,
|
||||||
|
version: 1,
|
||||||
|
},
|
||||||
|
1, // Code alignment factor
|
||||||
|
-8, // Data alignment factor
|
||||||
|
X86_64::RA,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Every frame will start with the call frame address (CFA) at RSP+8
|
||||||
|
// It is +8 to account for the push of the return address by the call instruction
|
||||||
|
entry.add_instruction(CallFrameInstruction::Cfa(X86_64::RSP, 8));
|
||||||
|
|
||||||
|
// Every frame will start with the return address at RSP (CFA-8 = RSP+8-8 = RSP)
|
||||||
|
entry.add_instruction(CallFrameInstruction::Offset(X86_64::RA, -8));
|
||||||
|
|
||||||
|
entry
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Map Cranelift registers to their corresponding Gimli registers.
|
||||||
|
pub fn map_reg(reg: Reg) -> Result<Register, RegisterMappingError> {
|
||||||
|
// Mapping from https://github.com/bytecodealliance/cranelift/pull/902 by @iximeow
|
||||||
|
const X86_GP_REG_MAP: [gimli::Register; 16] = [
|
||||||
|
X86_64::RAX,
|
||||||
|
X86_64::RCX,
|
||||||
|
X86_64::RDX,
|
||||||
|
X86_64::RBX,
|
||||||
|
X86_64::RSP,
|
||||||
|
X86_64::RBP,
|
||||||
|
X86_64::RSI,
|
||||||
|
X86_64::RDI,
|
||||||
|
X86_64::R8,
|
||||||
|
X86_64::R9,
|
||||||
|
X86_64::R10,
|
||||||
|
X86_64::R11,
|
||||||
|
X86_64::R12,
|
||||||
|
X86_64::R13,
|
||||||
|
X86_64::R14,
|
||||||
|
X86_64::R15,
|
||||||
|
];
|
||||||
|
const X86_XMM_REG_MAP: [gimli::Register; 16] = [
|
||||||
|
X86_64::XMM0,
|
||||||
|
X86_64::XMM1,
|
||||||
|
X86_64::XMM2,
|
||||||
|
X86_64::XMM3,
|
||||||
|
X86_64::XMM4,
|
||||||
|
X86_64::XMM5,
|
||||||
|
X86_64::XMM6,
|
||||||
|
X86_64::XMM7,
|
||||||
|
X86_64::XMM8,
|
||||||
|
X86_64::XMM9,
|
||||||
|
X86_64::XMM10,
|
||||||
|
X86_64::XMM11,
|
||||||
|
X86_64::XMM12,
|
||||||
|
X86_64::XMM13,
|
||||||
|
X86_64::XMM14,
|
||||||
|
X86_64::XMM15,
|
||||||
|
];
|
||||||
|
|
||||||
|
match reg.get_class() {
|
||||||
|
RegClass::I64 => {
|
||||||
|
// x86 GP registers have a weird mapping to DWARF registers, so we use a
|
||||||
|
// lookup table.
|
||||||
|
Ok(X86_GP_REG_MAP[reg.get_hw_encoding() as usize])
|
||||||
|
}
|
||||||
|
RegClass::V128 => Ok(X86_XMM_REG_MAP[reg.get_hw_encoding() as usize]),
|
||||||
|
_ => Err(RegisterMappingError::UnsupportedRegisterBank("class?")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn create_unwind_info(
|
||||||
|
context: UnwindInfoContext<Inst>,
|
||||||
|
word_size: u8,
|
||||||
|
) -> 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let map = RegisterMapper;
|
||||||
|
|
||||||
|
Ok(Some(UnwindInfo::build(unwind, &map)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::cursor::{Cursor, FuncCursor};
|
||||||
|
use crate::ir::{
|
||||||
|
types, AbiParam, ExternalName, Function, InstBuilder, Signature, StackSlotData,
|
||||||
|
StackSlotKind,
|
||||||
|
};
|
||||||
|
use crate::isa::{lookup, CallConv};
|
||||||
|
use crate::settings::{builder, Flags};
|
||||||
|
use crate::Context;
|
||||||
|
use gimli::write::Address;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use target_lexicon::triple;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_simple_func() {
|
||||||
|
let isa = lookup(triple!("x86_64"))
|
||||||
|
.expect("expect x86 ISA")
|
||||||
|
.finish(Flags::new(builder()));
|
||||||
|
|
||||||
|
let mut context = Context::for_function(create_function(
|
||||||
|
CallConv::SystemV,
|
||||||
|
Some(StackSlotData::new(StackSlotKind::ExplicitSlot, 64)),
|
||||||
|
));
|
||||||
|
|
||||||
|
context.compile(&*isa).expect("expected compilation");
|
||||||
|
|
||||||
|
let fde = match context
|
||||||
|
.create_unwind_info(isa.as_ref())
|
||||||
|
.expect("can create unwind info")
|
||||||
|
{
|
||||||
|
Some(crate::isa::unwind::UnwindInfo::SystemV(info)) => {
|
||||||
|
info.to_fde(Address::Constant(1234))
|
||||||
|
}
|
||||||
|
_ => panic!("expected unwind information"),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(format!("{:?}", fde), "FrameDescriptionEntry { address: Constant(1234), length: 13, lsda: None, instructions: [(1, CfaOffset(16)), (1, Offset(Register(6), -16)), (4, CfaRegister(Register(6)))] }");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_function(call_conv: CallConv, stack_slot: Option<StackSlotData>) -> Function {
|
||||||
|
let mut func =
|
||||||
|
Function::with_name_signature(ExternalName::user(0, 0), Signature::new(call_conv));
|
||||||
|
|
||||||
|
let block0 = func.dfg.make_block();
|
||||||
|
let mut pos = FuncCursor::new(&mut func);
|
||||||
|
pos.insert_block(block0);
|
||||||
|
pos.ins().return_(&[]);
|
||||||
|
|
||||||
|
if let Some(stack_slot) = stack_slot {
|
||||||
|
func.stack_slots.push(stack_slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
func
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_multi_return_func() {
|
||||||
|
let isa = lookup(triple!("x86_64"))
|
||||||
|
.expect("expect x86 ISA")
|
||||||
|
.finish(Flags::new(builder()));
|
||||||
|
|
||||||
|
let mut context = Context::for_function(create_multi_return_function(CallConv::SystemV));
|
||||||
|
|
||||||
|
context.compile(&*isa).expect("expected compilation");
|
||||||
|
|
||||||
|
let fde = match context
|
||||||
|
.create_unwind_info(isa.as_ref())
|
||||||
|
.expect("can create unwind info")
|
||||||
|
{
|
||||||
|
Some(crate::isa::unwind::UnwindInfo::SystemV(info)) => {
|
||||||
|
info.to_fde(Address::Constant(4321))
|
||||||
|
}
|
||||||
|
_ => panic!("expected unwind information"),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(format!("{:?}", fde), "FrameDescriptionEntry { address: Constant(4321), length: 23, lsda: None, instructions: [(1, CfaOffset(16)), (1, Offset(Register(6), -16)), (4, CfaRegister(Register(6))), (16, RememberState), (18, RestoreState)] }");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_multi_return_function(call_conv: CallConv) -> Function {
|
||||||
|
let mut sig = Signature::new(call_conv);
|
||||||
|
sig.params.push(AbiParam::new(types::I32));
|
||||||
|
let mut func = Function::with_name_signature(ExternalName::user(0, 0), sig);
|
||||||
|
|
||||||
|
let block0 = func.dfg.make_block();
|
||||||
|
let v0 = func.dfg.append_block_param(block0, types::I32);
|
||||||
|
let block1 = func.dfg.make_block();
|
||||||
|
let block2 = func.dfg.make_block();
|
||||||
|
|
||||||
|
let mut pos = FuncCursor::new(&mut func);
|
||||||
|
pos.insert_block(block0);
|
||||||
|
pos.ins().brnz(v0, block2, &[]);
|
||||||
|
pos.ins().jump(block1, &[]);
|
||||||
|
|
||||||
|
pos.insert_block(block1);
|
||||||
|
pos.ins().return_(&[]);
|
||||||
|
|
||||||
|
pos.insert_block(block2);
|
||||||
|
pos.ins().return_(&[]);
|
||||||
|
|
||||||
|
func
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -59,6 +59,7 @@ impl MachBackend for X64Backend {
|
|||||||
let buffer = vcode.emit();
|
let buffer = vcode.emit();
|
||||||
let buffer = buffer.finish();
|
let buffer = buffer.finish();
|
||||||
let frame_size = vcode.frame_size();
|
let frame_size = vcode.frame_size();
|
||||||
|
let unwind_info = vcode.unwind_info()?;
|
||||||
|
|
||||||
let disasm = if want_disasm {
|
let disasm = if want_disasm {
|
||||||
Some(vcode.show_rru(Some(&create_reg_universe_systemv(flags))))
|
Some(vcode.show_rru(Some(&create_reg_universe_systemv(flags))))
|
||||||
@@ -70,6 +71,7 @@ impl MachBackend for X64Backend {
|
|||||||
buffer,
|
buffer,
|
||||||
frame_size,
|
frame_size,
|
||||||
disasm,
|
disasm,
|
||||||
|
unwind_info,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,6 +102,12 @@ impl MachBackend for X64Backend {
|
|||||||
// underflow of a subtract (carry is borrow for subtract).
|
// underflow of a subtract (carry is borrow for subtract).
|
||||||
IntCC::UnsignedGreaterThanOrEqual
|
IntCC::UnsignedGreaterThanOrEqual
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new `isa::Builder`.
|
/// Create a new `isa::Builder`.
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ pub(crate) fn create_unwind_info(
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct RegisterMapper<'a, 'b>(&'a (dyn TargetIsa + 'b));
|
struct RegisterMapper<'a, 'b>(&'a (dyn TargetIsa + 'b));
|
||||||
impl<'a, 'b> crate::isa::unwind::systemv::RegisterMapper for RegisterMapper<'a, 'b> {
|
impl<'a, 'b> crate::isa::unwind::systemv::RegisterMapper<RegUnit> for RegisterMapper<'a, 'b> {
|
||||||
fn map(&self, reg: RegUnit) -> Result<u16, RegisterMappingError> {
|
fn map(&self, reg: RegUnit) -> Result<u16, RegisterMappingError> {
|
||||||
Ok(map_reg(self.0, reg)?.0)
|
Ok(map_reg(self.0, reg)?.0)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,6 +155,9 @@ pub trait ABICallee {
|
|||||||
from_slot: SpillSlot,
|
from_slot: SpillSlot,
|
||||||
ty: Option<Type>,
|
ty: Option<Type>,
|
||||||
) -> Self::I;
|
) -> Self::I;
|
||||||
|
|
||||||
|
/// Desired unwind info type.
|
||||||
|
fn unwind_info_kind(&self) -> UnwindInfoKind;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait implemented by an object that tracks ABI-related state and can
|
/// 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);
|
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()))
|
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>>) {
|
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()
|
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 {
|
fn as_any(&self) -> &dyn Any {
|
||||||
self as &dyn Any
|
self as &dyn Any
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,12 +99,14 @@
|
|||||||
use crate::binemit::{CodeInfo, CodeOffset, StackMap};
|
use crate::binemit::{CodeInfo, CodeOffset, StackMap};
|
||||||
use crate::ir::condcodes::IntCC;
|
use crate::ir::condcodes::IntCC;
|
||||||
use crate::ir::{Function, Type};
|
use crate::ir::{Function, Type};
|
||||||
|
use crate::isa::unwind;
|
||||||
use crate::result::CodegenResult;
|
use crate::result::CodegenResult;
|
||||||
use crate::settings::Flags;
|
use crate::settings::Flags;
|
||||||
|
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
|
use core::ops::Range;
|
||||||
use regalloc::RegUsageCollector;
|
use regalloc::RegUsageCollector;
|
||||||
use regalloc::{
|
use regalloc::{
|
||||||
RealReg, RealRegUniverse, Reg, RegClass, RegUsageMapper, SpillSlot, VirtualReg, Writable,
|
RealReg, RealRegUniverse, Reg, RegClass, RegUsageMapper, SpillSlot, VirtualReg, Writable,
|
||||||
@@ -277,6 +279,8 @@ pub trait MachInstEmit: MachInst {
|
|||||||
type State: MachInstEmitState<Self>;
|
type State: MachInstEmitState<Self>;
|
||||||
/// Constant information used in `emit` invocations.
|
/// Constant information used in `emit` invocations.
|
||||||
type Info: MachInstEmitInfo;
|
type Info: MachInstEmitInfo;
|
||||||
|
/// Unwind info generator.
|
||||||
|
type UnwindInfo: UnwindInfoGenerator<Self>;
|
||||||
/// Emit the instruction.
|
/// Emit the instruction.
|
||||||
fn emit(&self, code: &mut MachBuffer<Self>, info: &Self::Info, state: &mut Self::State);
|
fn emit(&self, code: &mut MachBuffer<Self>, info: &Self::Info, state: &mut Self::State);
|
||||||
/// Pretty-print the instruction.
|
/// Pretty-print the instruction.
|
||||||
@@ -309,6 +313,8 @@ pub struct MachCompileResult {
|
|||||||
pub frame_size: u32,
|
pub frame_size: u32,
|
||||||
/// Disassembly, if requested.
|
/// Disassembly, if requested.
|
||||||
pub disasm: Option<String>,
|
pub disasm: Option<String>,
|
||||||
|
/// Unwind info.
|
||||||
|
pub unwind_info: Option<unwind::UnwindInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MachCompileResult {
|
impl MachCompileResult {
|
||||||
@@ -353,4 +359,50 @@ pub trait MachBackend {
|
|||||||
/// Machine-specific condcode info needed by TargetIsa.
|
/// Machine-specific condcode info needed by TargetIsa.
|
||||||
/// Condition that will be true when an IsubIfcout overflows.
|
/// Condition that will be true when an IsubIfcout overflows.
|
||||||
fn unsigned_sub_overflow_condition(&self) -> IntCC;
|
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::boxed::Box;
|
||||||
use alloc::{borrow::Cow, vec::Vec};
|
use alloc::{borrow::Cow, vec::Vec};
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
@@ -39,6 +40,8 @@ use std::string::String;
|
|||||||
pub type InsnIndex = u32;
|
pub type InsnIndex = u32;
|
||||||
/// Index referring to a basic block in VCode.
|
/// Index referring to a basic block in VCode.
|
||||||
pub type BlockIndex = u32;
|
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
|
/// 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`.
|
/// 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
|
/// These are used to generate actual stack maps at emission. Filled in
|
||||||
/// post-regalloc.
|
/// post-regalloc.
|
||||||
safepoint_slots: Vec<Vec<SpillSlot>>,
|
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
|
/// A builder for a VCode function body. This builder is designed for the
|
||||||
@@ -292,6 +301,8 @@ impl<I: VCodeInst> VCode<I> {
|
|||||||
emit_info,
|
emit_info,
|
||||||
safepoint_insns: vec![],
|
safepoint_insns: vec![],
|
||||||
safepoint_slots: 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 final_safepoint_insns = vec![];
|
||||||
let mut safept_idx = 0;
|
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());
|
assert!(result.target_map.elems().len() == self.num_blocks());
|
||||||
for block in 0..self.num_blocks() {
|
for block in 0..self.num_blocks() {
|
||||||
let start = result.target_map.elems()[block].get() as usize;
|
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;
|
let final_start = final_insns.len() as InsnIndex;
|
||||||
|
|
||||||
if block == self.entry {
|
if block == self.entry {
|
||||||
|
prologue_start = Some(final_insns.len() as InsnIndex);
|
||||||
// Start with the prologue.
|
// Start with the prologue.
|
||||||
let prologue = self.abi.gen_prologue();
|
let prologue = self.abi.gen_prologue();
|
||||||
let len = prologue.len();
|
let len = prologue.len();
|
||||||
final_insns.extend(prologue.into_iter());
|
final_insns.extend(prologue.into_iter());
|
||||||
final_srclocs.extend(iter::repeat(SourceLoc::default()).take(len));
|
final_srclocs.extend(iter::repeat(SourceLoc::default()).take(len));
|
||||||
|
prologue_end = Some(final_insns.len() as InsnIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in start..end {
|
for i in start..end {
|
||||||
@@ -395,10 +412,12 @@ impl<I: VCodeInst> VCode<I> {
|
|||||||
// with the epilogue.
|
// with the epilogue.
|
||||||
let is_ret = insn.is_term() == MachTerminator::Ret;
|
let is_ret = insn.is_term() == MachTerminator::Ret;
|
||||||
if is_ret {
|
if is_ret {
|
||||||
|
let epilogue_start = final_insns.len() as InsnIndex;
|
||||||
let epilogue = self.abi.gen_epilogue();
|
let epilogue = self.abi.gen_epilogue();
|
||||||
let len = epilogue.len();
|
let len = epilogue.len();
|
||||||
final_insns.extend(epilogue.into_iter());
|
final_insns.extend(epilogue.into_iter());
|
||||||
final_srclocs.extend(iter::repeat(srcloc).take(len));
|
final_srclocs.extend(iter::repeat(srcloc).take(len));
|
||||||
|
epilogue_islands.push(epilogue_start..final_insns.len() as InsnIndex);
|
||||||
} else {
|
} else {
|
||||||
final_insns.push(insn.clone());
|
final_insns.push(insn.clone());
|
||||||
final_srclocs.push(srcloc);
|
final_srclocs.push(srcloc);
|
||||||
@@ -430,6 +449,11 @@ impl<I: VCodeInst> VCode<I> {
|
|||||||
// for the machine backend during emission so that it can do
|
// for the machine backend during emission so that it can do
|
||||||
// target-specific translations of slot numbers to stack offsets.
|
// target-specific translations of slot numbers to stack offsets.
|
||||||
self.safepoint_slots = result.stackmaps;
|
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
|
/// 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.
|
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 safepoint_idx = 0;
|
||||||
let mut cur_srcloc = None;
|
let mut cur_srcloc = None;
|
||||||
for block in 0..self.num_blocks() {
|
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);
|
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() {
|
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
|
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.
|
/// Get the IR block for a BlockIndex, if one exists.
|
||||||
pub fn bindex_to_bb(&self, block: BlockIndex) -> Option<ir::Block> {
|
pub fn bindex_to_bb(&self, block: BlockIndex) -> Option<ir::Block> {
|
||||||
self.block_order.lowered_order()[block as usize].orig_block()
|
self.block_order.lowered_order()[block as usize].orig_block()
|
||||||
|
|||||||
@@ -61,6 +61,9 @@ impl SubTest for TestUnwind {
|
|||||||
table.write_eh_frame(&mut eh_frame).unwrap();
|
table.write_eh_frame(&mut eh_frame).unwrap();
|
||||||
systemv::dump(&mut text, &eh_frame.0.into_vec(), isa.pointer_bytes())
|
systemv::dump(&mut text, &eh_frame.0.into_vec(), isa.pointer_bytes())
|
||||||
}
|
}
|
||||||
|
Some(ui) => {
|
||||||
|
anyhow::bail!("Unexpected unwind info type: {:?}", ui);
|
||||||
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ impl Drop for GcOnDrop {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg_attr(feature = "experimental_x64", ignore)] // TODO #2079 investigate.
|
||||||
fn smoke_test_gc() -> anyhow::Result<()> {
|
fn smoke_test_gc() -> anyhow::Result<()> {
|
||||||
let (store, module) = ref_types_module(
|
let (store, module) = ref_types_module(
|
||||||
r#"
|
r#"
|
||||||
@@ -120,6 +121,7 @@ fn wasm_dropping_refs() -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg_attr(feature = "experimental_x64", ignore)] // TODO #2079 investigate.
|
||||||
fn many_live_refs() -> anyhow::Result<()> {
|
fn many_live_refs() -> anyhow::Result<()> {
|
||||||
let mut wat = r#"
|
let mut wat = r#"
|
||||||
(module
|
(module
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ fn test_trap_return() -> Result<()> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642)
|
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642)
|
||||||
#[cfg_attr(feature = "experimental_x64", ignore)] // FIXME(#2078)
|
#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079)
|
||||||
fn test_trap_trace() -> Result<()> {
|
fn test_trap_trace() -> Result<()> {
|
||||||
let store = Store::default();
|
let store = Store::default();
|
||||||
let wat = r#"
|
let wat = r#"
|
||||||
@@ -75,7 +75,7 @@ fn test_trap_trace() -> Result<()> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642)
|
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642)
|
||||||
#[cfg_attr(feature = "experimental_x64", ignore)] // FIXME(#2078)
|
#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079)
|
||||||
fn test_trap_trace_cb() -> Result<()> {
|
fn test_trap_trace_cb() -> Result<()> {
|
||||||
let store = Store::default();
|
let store = Store::default();
|
||||||
let wat = r#"
|
let wat = r#"
|
||||||
@@ -112,7 +112,7 @@ fn test_trap_trace_cb() -> Result<()> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642)
|
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642)
|
||||||
#[cfg_attr(feature = "experimental_x64", ignore)] // FIXME(#2078)
|
#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079)
|
||||||
fn test_trap_stack_overflow() -> Result<()> {
|
fn test_trap_stack_overflow() -> Result<()> {
|
||||||
let store = Store::default();
|
let store = Store::default();
|
||||||
let wat = r#"
|
let wat = r#"
|
||||||
@@ -145,7 +145,7 @@ fn test_trap_stack_overflow() -> Result<()> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642)
|
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642)
|
||||||
#[cfg_attr(feature = "experimental_x64", ignore)] // FIXME(#2078)
|
#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079)
|
||||||
fn trap_display_pretty() -> Result<()> {
|
fn trap_display_pretty() -> Result<()> {
|
||||||
let store = Store::default();
|
let store = Store::default();
|
||||||
let wat = r#"
|
let wat = r#"
|
||||||
@@ -178,7 +178,7 @@ wasm backtrace:
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642)
|
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642)
|
||||||
#[cfg_attr(feature = "experimental_x64", ignore)] // FIXME(#2078)
|
#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079)
|
||||||
fn trap_display_multi_module() -> Result<()> {
|
fn trap_display_multi_module() -> Result<()> {
|
||||||
let store = Store::default();
|
let store = Store::default();
|
||||||
let wat = r#"
|
let wat = r#"
|
||||||
@@ -224,7 +224,7 @@ wasm backtrace:
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642)
|
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642)
|
||||||
#[cfg_attr(feature = "experimental_x64", ignore)] // FIXME(#2078)
|
#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079)
|
||||||
fn trap_start_function_import() -> Result<()> {
|
fn trap_start_function_import() -> Result<()> {
|
||||||
let store = Store::default();
|
let store = Store::default();
|
||||||
let binary = wat::parse_str(
|
let binary = wat::parse_str(
|
||||||
@@ -252,7 +252,7 @@ fn trap_start_function_import() -> Result<()> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642)
|
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642)
|
||||||
#[cfg_attr(feature = "experimental_x64", ignore)] // FIXME(#2078)
|
#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079)
|
||||||
fn rust_panic_import() -> Result<()> {
|
fn rust_panic_import() -> Result<()> {
|
||||||
let store = Store::default();
|
let store = Store::default();
|
||||||
let binary = wat::parse_str(
|
let binary = wat::parse_str(
|
||||||
@@ -298,7 +298,7 @@ fn rust_panic_import() -> Result<()> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642)
|
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642)
|
||||||
#[cfg_attr(feature = "experimental_x64", ignore)] // FIXME(#2078)
|
#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079)
|
||||||
fn rust_panic_start_function() -> Result<()> {
|
fn rust_panic_start_function() -> Result<()> {
|
||||||
let store = Store::default();
|
let store = Store::default();
|
||||||
let binary = wat::parse_str(
|
let binary = wat::parse_str(
|
||||||
@@ -333,7 +333,7 @@ fn rust_panic_start_function() -> Result<()> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642)
|
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642)
|
||||||
#[cfg_attr(feature = "experimental_x64", ignore)] // FIXME(#2078)
|
#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079)
|
||||||
fn mismatched_arguments() -> Result<()> {
|
fn mismatched_arguments() -> Result<()> {
|
||||||
let store = Store::default();
|
let store = Store::default();
|
||||||
let binary = wat::parse_str(
|
let binary = wat::parse_str(
|
||||||
@@ -366,7 +366,7 @@ fn mismatched_arguments() -> Result<()> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642)
|
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642)
|
||||||
#[cfg_attr(feature = "experimental_x64", ignore)] // FIXME(#2078)
|
#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079)
|
||||||
fn call_signature_mismatch() -> Result<()> {
|
fn call_signature_mismatch() -> Result<()> {
|
||||||
let store = Store::default();
|
let store = Store::default();
|
||||||
let binary = wat::parse_str(
|
let binary = wat::parse_str(
|
||||||
@@ -398,7 +398,7 @@ fn call_signature_mismatch() -> Result<()> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642)
|
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642)
|
||||||
#[cfg_attr(feature = "experimental_x64", ignore)] // FIXME(#2078)
|
#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079)
|
||||||
fn start_trap_pretty() -> Result<()> {
|
fn start_trap_pretty() -> Result<()> {
|
||||||
let store = Store::default();
|
let store = Store::default();
|
||||||
let wat = r#"
|
let wat = r#"
|
||||||
|
|||||||
Reference in New Issue
Block a user