machinst aarch64: New backend unwind (#2313)

* Unwind information for aarch64 backend.
This commit is contained in:
Yury Delendik
2020-11-06 08:02:45 -06:00
committed by GitHub
parent f60c0f3ec3
commit b2b7bc10e2
5 changed files with 381 additions and 17 deletions

View File

@@ -1,14 +1,194 @@
use super::*; use super::*;
use crate::isa::unwind::input::UnwindInfo; use crate::isa::aarch64::inst::{args::PairAMode, imms::Imm12, regs, ALUOp, Inst};
use crate::isa::unwind::input::{UnwindCode, UnwindInfo};
use crate::machinst::UnwindInfoContext;
use crate::result::CodegenResult; use crate::result::CodegenResult;
use alloc::vec::Vec;
use regalloc::Reg;
#[cfg(feature = "unwind")]
pub(crate) mod systemv;
pub struct AArch64UnwindInfo; pub struct AArch64UnwindInfo;
impl UnwindInfoGenerator<Inst> for AArch64UnwindInfo { impl UnwindInfoGenerator<Inst> for AArch64UnwindInfo {
fn create_unwind_info( fn create_unwind_info(
_context: UnwindInfoContext<Inst>, context: UnwindInfoContext<Inst>,
) -> CodegenResult<Option<UnwindInfo<Reg>>> { ) -> CodegenResult<Option<UnwindInfo<Reg>>> {
// TODO let word_size = 8u8;
Ok(None) let pair_size = word_size * 2;
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::StoreP64 {
rt,
rt2,
mem: PairAMode::PreIndexed(rn, imm7),
} if *rt == regs::fp_reg()
&& *rt2 == regs::link_reg()
&& *rn == regs::writable_stack_reg()
&& imm7.value == -(pair_size as i16) =>
{
// stp fp (x29), lr (x30), [sp, #-16]!
codes.push((
offset,
UnwindCode::StackAlloc {
size: pair_size as u32,
},
));
codes.push((
offset,
UnwindCode::SaveRegister {
reg: *rt,
stack_offset: 0,
},
));
codes.push((
offset,
UnwindCode::SaveRegister {
reg: *rt2,
stack_offset: word_size as u32,
},
));
}
Inst::StoreP64 {
rt,
rt2,
mem: PairAMode::PreIndexed(rn, imm7),
} if rn.to_reg() == regs::stack_reg() && imm7.value % (pair_size as i16) == 0 => {
// stp r1, r2, [sp, #(i * #16)]
let stack_offset = imm7.value as u32;
codes.push((
offset,
UnwindCode::SaveRegister {
reg: *rt,
stack_offset,
},
));
if *rt2 != regs::zero_reg() {
codes.push((
offset,
UnwindCode::SaveRegister {
reg: *rt2,
stack_offset: stack_offset + word_size as u32,
},
));
}
}
Inst::AluRRImm12 {
alu_op: ALUOp::Add64,
rd,
rn,
imm12:
Imm12 {
bits: 0,
shift12: false,
},
} if *rd == regs::writable_fp_reg() && *rn == regs::stack_reg() => {
// mov fp (x29), sp.
codes.push((offset, UnwindCode::SetFramePointer { reg: rd.to_reg() }));
}
Inst::VirtualSPOffsetAdj { offset: adj } if offset > 0 => {
codes.push((offset, UnwindCode::StackAlloc { size: *adj as u32 }));
}
_ => {}
}
}
// TODO epilogues
let prologue_size = context.insts_layout[context.prologue.end as usize - 1];
Ok(Some(UnwindInfo {
prologue_size,
prologue_unwind_codes: codes,
epilogues_unwind_codes: vec![],
function_size: context.len,
word_size,
initial_sp_offset: 0,
}))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cursor::{Cursor, FuncCursor};
use crate::ir::{ExternalName, Function, InstBuilder, Signature, StackSlotData, StackSlotKind};
use crate::isa::{lookup, CallConv};
use crate::settings::{builder, Flags};
use crate::Context;
use std::str::FromStr;
use target_lexicon::triple;
#[test]
fn test_simple_func() {
let isa = lookup(triple!("aarch64"))
.expect("expect aarch64 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 result = context.mach_compile_result.unwrap();
let unwind_info = result.unwind_info.unwrap();
assert_eq!(
unwind_info,
UnwindInfo {
prologue_size: 12,
prologue_unwind_codes: vec![
(4, UnwindCode::StackAlloc { size: 16 }),
(
4,
UnwindCode::SaveRegister {
reg: regs::fp_reg(),
stack_offset: 0
}
),
(
4,
UnwindCode::SaveRegister {
reg: regs::link_reg(),
stack_offset: 8
}
),
(
8,
UnwindCode::SetFramePointer {
reg: regs::fp_reg()
}
)
],
epilogues_unwind_codes: vec![],
function_size: 24,
word_size: 8,
initial_sp_offset: 0,
}
);
}
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
} }
} }

View File

@@ -0,0 +1,158 @@
//! Unwind information for System V ABI (Aarch64).
use crate::isa::aarch64::inst::regs;
use crate::isa::unwind::input;
use crate::isa::unwind::systemv::{RegisterMappingError, UnwindInfo};
use crate::result::CodegenResult;
use gimli::{write::CommonInformationEntry, Encoding, Format, Register};
use regalloc::{Reg, RegClass};
/// Creates a new aarch64 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,
},
4, // Code alignment factor
-8, // Data alignment factor
Register(regs::link_reg().get_hw_encoding().into()),
);
// Every frame will start with the call frame address (CFA) at SP
let sp = Register(regs::stack_reg().get_hw_encoding().into());
entry.add_instruction(CallFrameInstruction::Cfa(sp, 0));
entry
}
/// Map Cranelift registers to their corresponding Gimli registers.
pub fn map_reg(reg: Reg) -> Result<Register, RegisterMappingError> {
match reg.get_class() {
RegClass::I64 => Ok(Register(reg.get_hw_encoding().into())),
_ => Err(RegisterMappingError::UnsupportedRegisterBank("class?")),
}
}
pub(crate) fn create_unwind_info(
unwind: input::UnwindInfo<Reg>,
) -> CodegenResult<Option<UnwindInfo>> {
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 sp(&self) -> u16 {
regs::stack_reg().get_hw_encoding().into()
}
}
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!("aarch64"))
.expect("expect aarch64 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: 24, lsda: None, instructions: [(4, CfaOffset(16)), (4, Offset(Register(29), -16)), (4, Offset(Register(30), -8)), (8, CfaRegister(Register(29)))] }");
}
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!("aarch64"))
.expect("expect aarch64 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: 40, lsda: None, instructions: [(4, CfaOffset(16)), (4, Offset(Register(29), -16)), (4, Offset(Register(30), -8)), (8, CfaRegister(Register(29)))] }");
}
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
}
}

View File

@@ -64,6 +64,7 @@ impl MachBackend for AArch64Backend {
let buffer = vcode.emit(); let buffer = vcode.emit();
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(flags)))) Some(vcode.show_rru(Some(&create_reg_universe(flags))))
@@ -77,7 +78,7 @@ impl MachBackend for AArch64Backend {
buffer, buffer,
frame_size, frame_size,
disasm, disasm,
unwind_info: None, unwind_info,
}) })
} }
@@ -109,6 +110,31 @@ impl MachBackend for AArch64Backend {
// opposite of x86). // opposite of x86).
IntCC::UnsignedLessThan IntCC::UnsignedLessThan
} }
#[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: support Windows unwind info on AArch64
None
}
_ => None,
})
}
#[cfg(feature = "unwind")]
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
Some(inst::unwind::systemv::create_cie())
}
} }
/// Create a new `isa::Builder`. /// Create a new `isa::Builder`.

View File

@@ -129,7 +129,7 @@ impl TargetIsa for TargetIsaAdapter {
self.backend.unsigned_sub_overflow_condition() self.backend.unsigned_sub_overflow_condition()
} }
#[cfg(all(feature = "unwind", feature = "x64"))] #[cfg(feature = "unwind")]
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> { fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
self.backend.create_systemv_cie() self.backend.create_systemv_cie()
} }

View File

@@ -31,7 +31,7 @@ fn test_trap_return() -> Result<()> {
} }
#[test] #[test]
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) #[cfg_attr(all(target_os = "windows", target_arch = "aarch64"), ignore)] // FIXME(#1642)
#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) #[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();
@@ -74,7 +74,7 @@ fn test_trap_trace() -> Result<()> {
} }
#[test] #[test]
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) #[cfg_attr(all(target_os = "windows", target_arch = "aarch64"), ignore)] // FIXME(#1642)
#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) #[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();
@@ -111,7 +111,7 @@ fn test_trap_trace_cb() -> Result<()> {
} }
#[test] #[test]
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) #[cfg_attr(all(target_os = "windows", target_arch = "aarch64"), ignore)] // FIXME(#1642)
#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) #[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();
@@ -144,7 +144,7 @@ fn test_trap_stack_overflow() -> Result<()> {
} }
#[test] #[test]
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) #[cfg_attr(all(target_os = "windows", target_arch = "aarch64"), ignore)] // FIXME(#1642)
#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) #[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();
@@ -177,7 +177,7 @@ wasm backtrace:
} }
#[test] #[test]
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) #[cfg_attr(all(target_os = "windows", target_arch = "aarch64"), ignore)] // FIXME(#1642)
#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) #[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();
@@ -223,7 +223,7 @@ wasm backtrace:
} }
#[test] #[test]
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) #[cfg_attr(all(target_os = "windows", target_arch = "aarch64"), ignore)] // FIXME(#1642)
#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) #[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();
@@ -251,7 +251,7 @@ fn trap_start_function_import() -> Result<()> {
} }
#[test] #[test]
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) #[cfg_attr(all(target_os = "windows", target_arch = "aarch64"), ignore)] // FIXME(#1642)
#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) #[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();
@@ -297,7 +297,7 @@ fn rust_panic_import() -> Result<()> {
} }
#[test] #[test]
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) #[cfg_attr(all(target_os = "windows", target_arch = "aarch64"), ignore)] // FIXME(#1642)
#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) #[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();
@@ -332,7 +332,7 @@ fn rust_panic_start_function() -> Result<()> {
} }
#[test] #[test]
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) #[cfg_attr(all(target_os = "windows", target_arch = "aarch64"), ignore)] // FIXME(#1642)
#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) #[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();
@@ -365,7 +365,7 @@ fn mismatched_arguments() -> Result<()> {
} }
#[test] #[test]
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) #[cfg_attr(all(target_os = "windows", target_arch = "aarch64"), ignore)] // FIXME(#1642)
#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) #[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();
@@ -397,7 +397,7 @@ fn call_signature_mismatch() -> Result<()> {
} }
#[test] #[test]
#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) #[cfg_attr(all(target_os = "windows", target_arch = "aarch64"), ignore)] // FIXME(#1642)
#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) #[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();