add riscv64 backend for cranelift. (#4271)
Add a RISC-V 64 (`riscv64`, RV64GC) backend. Co-authored-by: yuyang <756445638@qq.com> Co-authored-by: Chris Fallin <chris@cfallin.org> Co-authored-by: Afonso Bordado <afonsobordado@az8.co>
This commit is contained in:
1972
cranelift/codegen/src/isa/riscv64/inst/args.rs
Normal file
1972
cranelift/codegen/src/isa/riscv64/inst/args.rs
Normal file
File diff suppressed because it is too large
Load Diff
2920
cranelift/codegen/src/isa/riscv64/inst/emit.rs
Normal file
2920
cranelift/codegen/src/isa/riscv64/inst/emit.rs
Normal file
File diff suppressed because it is too large
Load Diff
2279
cranelift/codegen/src/isa/riscv64/inst/emit_tests.rs
Normal file
2279
cranelift/codegen/src/isa/riscv64/inst/emit_tests.rs
Normal file
File diff suppressed because it is too large
Load Diff
218
cranelift/codegen/src/isa/riscv64/inst/imms.rs
Normal file
218
cranelift/codegen/src/isa/riscv64/inst/imms.rs
Normal file
@@ -0,0 +1,218 @@
|
||||
//! Riscv64 ISA definitions: immediate constants.
|
||||
|
||||
// Some variants are never constructed, but we still want them as options in the future.
|
||||
use super::Inst;
|
||||
#[allow(dead_code)]
|
||||
use std::fmt::{Debug, Display, Formatter, Result};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct Imm12 {
|
||||
pub bits: i16,
|
||||
}
|
||||
|
||||
impl Imm12 {
|
||||
pub(crate) const FALSE: Self = Self { bits: 0 };
|
||||
pub(crate) const TRUE: Self = Self { bits: -1 };
|
||||
pub fn maybe_from_u64(val: u64) -> Option<Imm12> {
|
||||
let sign_bit = 1 << 11;
|
||||
if val == 0 {
|
||||
Some(Imm12 { bits: 0 })
|
||||
} else if (val & sign_bit) != 0 && (val >> 12) == 0xffff_ffff_ffff_f {
|
||||
Some(Imm12 {
|
||||
bits: (val & 0xffff) as i16,
|
||||
})
|
||||
} else if (val & sign_bit) == 0 && (val >> 12) == 0 {
|
||||
Some(Imm12 {
|
||||
bits: (val & 0xffff) as i16,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn from_bits(bits: i16) -> Self {
|
||||
Self { bits: bits & 0xfff }
|
||||
}
|
||||
/// Create a zero immediate of this format.
|
||||
#[inline]
|
||||
pub fn zero() -> Self {
|
||||
Imm12 { bits: 0 }
|
||||
}
|
||||
#[inline]
|
||||
pub fn as_i16(self) -> i16 {
|
||||
self.bits
|
||||
}
|
||||
#[inline]
|
||||
pub fn as_u32(&self) -> u32 {
|
||||
(self.bits as u32) & 0xfff
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<i64> for Imm12 {
|
||||
fn into(self) -> i64 {
|
||||
self.bits as i64
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Imm12 {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
write!(f, "{:+}", self.bits)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Neg for Imm12 {
|
||||
type Output = Self;
|
||||
fn neg(self) -> Self::Output {
|
||||
Self { bits: -self.bits }
|
||||
}
|
||||
}
|
||||
|
||||
// singed
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct Imm20 {
|
||||
/// The immediate bits.
|
||||
pub bits: i32,
|
||||
}
|
||||
|
||||
impl Imm20 {
|
||||
#[inline]
|
||||
pub fn from_bits(bits: i32) -> Self {
|
||||
Self {
|
||||
bits: bits & 0xf_ffff,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn as_u32(&self) -> u32 {
|
||||
(self.bits as u32) & 0xf_ffff
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Imm20 {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
write!(f, "{}", self.bits)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Imm20 {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
write!(f, "{}", self.bits)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Uimm5 {
|
||||
bits: u8,
|
||||
}
|
||||
|
||||
impl Uimm5 {
|
||||
pub fn from_bits(bits: u8) -> Self {
|
||||
Self { bits }
|
||||
}
|
||||
/// Create a zero immediate of this format.
|
||||
pub fn zero() -> Self {
|
||||
Self { bits: 0 }
|
||||
}
|
||||
pub fn as_u32(&self) -> u32 {
|
||||
(self.bits as u32) & 0b1_1111
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Uimm5 {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
write!(f, "{}", self.bits)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Uimm5 {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
write!(f, "{}", self.bits)
|
||||
}
|
||||
}
|
||||
|
||||
impl Inst {
|
||||
pub(crate) fn imm_min() -> i64 {
|
||||
let imm20_max: i64 = (1 << 19) << 12;
|
||||
let imm12_max = 1 << 11;
|
||||
-imm20_max - imm12_max
|
||||
}
|
||||
pub(crate) fn imm_max() -> i64 {
|
||||
let imm20_max: i64 = ((1 << 19) - 1) << 12;
|
||||
let imm12_max = (1 << 11) - 1;
|
||||
imm20_max + imm12_max
|
||||
}
|
||||
|
||||
/// An imm20 immediate and an Imm12 immediate can generate a 32-bit immediate.
|
||||
/// This helper produces an imm12, imm20, or both to generate the value.
|
||||
///
|
||||
/// `value` must be between `imm_min()` and `imm_max()`, or else
|
||||
/// this helper returns `None`.
|
||||
pub(crate) fn generate_imm<R>(
|
||||
value: u64,
|
||||
mut handle_imm: impl FnMut(Option<Imm20>, Option<Imm12>) -> R,
|
||||
) -> Option<R> {
|
||||
if let Some(imm12) = Imm12::maybe_from_u64(value) {
|
||||
// can be load using single imm12.
|
||||
let r = handle_imm(None, Some(imm12));
|
||||
return Some(r);
|
||||
}
|
||||
let value = value as i64;
|
||||
if !(value >= Self::imm_min() && value <= Self::imm_max()) {
|
||||
// not in range, return None.
|
||||
return None;
|
||||
}
|
||||
const MOD_NUM: i64 = 4096;
|
||||
let (imm20, imm12) = if value > 0 {
|
||||
let mut imm20 = value / MOD_NUM;
|
||||
let mut imm12 = value % MOD_NUM;
|
||||
if imm12 >= 2048 {
|
||||
imm12 -= MOD_NUM;
|
||||
imm20 += 1;
|
||||
}
|
||||
assert!(imm12 >= -2048 && imm12 <= 2047);
|
||||
(imm20, imm12)
|
||||
} else {
|
||||
// this is the abs value.
|
||||
let value_abs = value.abs();
|
||||
let imm20 = value_abs / MOD_NUM;
|
||||
let imm12 = value_abs % MOD_NUM;
|
||||
let mut imm20 = -imm20;
|
||||
let mut imm12 = -imm12;
|
||||
if imm12 < -2048 {
|
||||
imm12 += MOD_NUM;
|
||||
imm20 -= 1;
|
||||
}
|
||||
(imm20, imm12)
|
||||
};
|
||||
assert!(imm20 >= -(0x7_ffff + 1) && imm20 <= 0x7_ffff);
|
||||
assert!(imm20 != 0 || imm12 != 0);
|
||||
Some(handle_imm(
|
||||
if imm20 != 0 {
|
||||
Some(Imm20::from_bits(imm20 as i32))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
if imm12 != 0 {
|
||||
Some(Imm12::from_bits(imm12 as i16))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_imm12() {
|
||||
let x = Imm12::zero();
|
||||
assert_eq!(0, x.as_u32());
|
||||
Imm12::maybe_from_u64(0xffff_ffff_ffff_ffff).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn imm20_and_imm12() {
|
||||
assert!(Inst::imm_max() == (i32::MAX - 2048) as i64);
|
||||
assert!(Inst::imm_min() == i32::MIN as i64 - 2048);
|
||||
}
|
||||
}
|
||||
1749
cranelift/codegen/src/isa/riscv64/inst/mod.rs
Normal file
1749
cranelift/codegen/src/isa/riscv64/inst/mod.rs
Normal file
File diff suppressed because it is too large
Load Diff
220
cranelift/codegen/src/isa/riscv64/inst/regs.rs
Normal file
220
cranelift/codegen/src/isa/riscv64/inst/regs.rs
Normal file
@@ -0,0 +1,220 @@
|
||||
//! Riscv64 ISA definitions: registers.
|
||||
//!
|
||||
|
||||
use crate::settings;
|
||||
|
||||
use crate::machinst::{Reg, Writable};
|
||||
|
||||
use crate::machinst::RealReg;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use regalloc2::VReg;
|
||||
use regalloc2::{MachineEnv, PReg, RegClass};
|
||||
|
||||
// first argument of function call
|
||||
#[inline]
|
||||
pub fn a0() -> Reg {
|
||||
x_reg(10)
|
||||
}
|
||||
|
||||
// second argument of function call
|
||||
#[inline]
|
||||
pub fn a1() -> Reg {
|
||||
x_reg(11)
|
||||
}
|
||||
|
||||
// third argument of function call
|
||||
#[inline]
|
||||
pub fn a2() -> Reg {
|
||||
x_reg(12)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn writable_a0() -> Writable<Reg> {
|
||||
Writable::from_reg(a0())
|
||||
}
|
||||
#[inline]
|
||||
pub fn writable_a1() -> Writable<Reg> {
|
||||
Writable::from_reg(a1())
|
||||
}
|
||||
#[inline]
|
||||
pub fn writable_a2() -> Writable<Reg> {
|
||||
Writable::from_reg(a2())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn fa0() -> Reg {
|
||||
f_reg(10)
|
||||
}
|
||||
#[inline]
|
||||
pub fn writable_fa0() -> Writable<Reg> {
|
||||
Writable::from_reg(fa0())
|
||||
}
|
||||
#[inline]
|
||||
pub fn writable_fa1() -> Writable<Reg> {
|
||||
Writable::from_reg(fa1())
|
||||
}
|
||||
#[inline]
|
||||
pub fn fa1() -> Reg {
|
||||
f_reg(11)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn fa7() -> Reg {
|
||||
f_reg(17)
|
||||
}
|
||||
|
||||
/// Get a reference to the zero-register.
|
||||
#[inline]
|
||||
pub fn zero_reg() -> Reg {
|
||||
x_reg(0)
|
||||
}
|
||||
|
||||
/// Get a writable reference to the zero-register (this discards a result).
|
||||
#[inline]
|
||||
pub fn writable_zero_reg() -> Writable<Reg> {
|
||||
Writable::from_reg(zero_reg())
|
||||
}
|
||||
#[inline]
|
||||
pub fn stack_reg() -> Reg {
|
||||
x_reg(2)
|
||||
}
|
||||
|
||||
/// Get a writable reference to the stack-pointer register.
|
||||
#[inline]
|
||||
pub fn writable_stack_reg() -> Writable<Reg> {
|
||||
Writable::from_reg(stack_reg())
|
||||
}
|
||||
|
||||
/// Get a reference to the link register (x1).
|
||||
pub fn link_reg() -> Reg {
|
||||
x_reg(1)
|
||||
}
|
||||
|
||||
/// Get a writable reference to the link register.
|
||||
#[inline]
|
||||
pub fn writable_link_reg() -> Writable<Reg> {
|
||||
Writable::from_reg(link_reg())
|
||||
}
|
||||
|
||||
/// Get a reference to the frame pointer (x29).
|
||||
#[inline]
|
||||
pub fn fp_reg() -> Reg {
|
||||
x_reg(8)
|
||||
}
|
||||
|
||||
/// Get a writable reference to the frame pointer.
|
||||
#[inline]
|
||||
pub fn writable_fp_reg() -> Writable<Reg> {
|
||||
Writable::from_reg(fp_reg())
|
||||
}
|
||||
|
||||
/// Get a reference to the first temporary, sometimes "spill temporary",
|
||||
/// register. This register is used in various ways as a temporary.
|
||||
#[inline]
|
||||
pub fn spilltmp_reg() -> Reg {
|
||||
x_reg(31)
|
||||
}
|
||||
|
||||
/// Get a writable reference to the spilltmp reg.
|
||||
#[inline]
|
||||
pub fn writable_spilltmp_reg() -> Writable<Reg> {
|
||||
Writable::from_reg(spilltmp_reg())
|
||||
}
|
||||
|
||||
///spilltmp2
|
||||
#[inline]
|
||||
pub fn spilltmp_reg2() -> Reg {
|
||||
x_reg(30)
|
||||
}
|
||||
|
||||
/// Get a writable reference to the spilltmp2 reg.
|
||||
#[inline]
|
||||
pub fn writable_spilltmp_reg2() -> Writable<Reg> {
|
||||
Writable::from_reg(spilltmp_reg2())
|
||||
}
|
||||
|
||||
pub fn crate_reg_eviroment(_flags: &settings::Flags) -> MachineEnv {
|
||||
let preferred_regs_by_class: [Vec<PReg>; 2] = {
|
||||
let mut x_register: Vec<PReg> = vec![];
|
||||
x_register.push(PReg::new(5, RegClass::Int));
|
||||
for i in 6..=7 {
|
||||
x_register.push(PReg::new(i, RegClass::Int));
|
||||
}
|
||||
for i in 10..=17 {
|
||||
x_register.push(PReg::new(i, RegClass::Int));
|
||||
}
|
||||
for i in 28..=29 {
|
||||
x_register.push(PReg::new(i, RegClass::Int));
|
||||
}
|
||||
|
||||
let mut f_register: Vec<PReg> = vec![];
|
||||
for i in 0..=7 {
|
||||
f_register.push(PReg::new(i, RegClass::Float));
|
||||
}
|
||||
for i in 10..=17 {
|
||||
f_register.push(PReg::new(i, RegClass::Float));
|
||||
}
|
||||
for i in 28..=31 {
|
||||
f_register.push(PReg::new(i, RegClass::Float));
|
||||
}
|
||||
[x_register, f_register]
|
||||
};
|
||||
|
||||
let non_preferred_regs_by_class: [Vec<PReg>; 2] = {
|
||||
let mut x_register: Vec<PReg> = vec![];
|
||||
x_register.push(PReg::new(9, RegClass::Int));
|
||||
for i in 18..=27 {
|
||||
x_register.push(PReg::new(i, RegClass::Int));
|
||||
}
|
||||
let mut f_register: Vec<PReg> = vec![];
|
||||
for i in 8..=9 {
|
||||
f_register.push(PReg::new(i, RegClass::Float));
|
||||
}
|
||||
for i in 18..=27 {
|
||||
f_register.push(PReg::new(i, RegClass::Float));
|
||||
}
|
||||
[x_register, f_register]
|
||||
};
|
||||
|
||||
MachineEnv {
|
||||
preferred_regs_by_class,
|
||||
non_preferred_regs_by_class,
|
||||
fixed_stack_slots: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn x_reg(enc: usize) -> Reg {
|
||||
let p_reg = PReg::new(enc, RegClass::Int);
|
||||
let v_reg = VReg::new(p_reg.index(), p_reg.class());
|
||||
Reg::from(v_reg)
|
||||
}
|
||||
pub fn px_reg(enc: usize) -> PReg {
|
||||
PReg::new(enc, RegClass::Int)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn f_reg(enc: usize) -> Reg {
|
||||
let p_reg = PReg::new(enc, RegClass::Float);
|
||||
let v_reg = VReg::new(p_reg.index(), p_reg.class());
|
||||
Reg::from(v_reg)
|
||||
}
|
||||
pub const fn pf_reg(enc: usize) -> PReg {
|
||||
PReg::new(enc, RegClass::Float)
|
||||
}
|
||||
#[inline]
|
||||
pub(crate) fn real_reg_to_reg(x: RealReg) -> Reg {
|
||||
let v_reg = VReg::new(x.hw_enc() as usize, x.class());
|
||||
Reg::from(v_reg)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn x_reg_range(start: usize, end: usize) -> Vec<Writable<Reg>> {
|
||||
let mut regs = vec![];
|
||||
for i in start..=end {
|
||||
regs.push(Writable::from_reg(x_reg(i)));
|
||||
}
|
||||
regs
|
||||
}
|
||||
2
cranelift/codegen/src/isa/riscv64/inst/unwind.rs
Normal file
2
cranelift/codegen/src/isa/riscv64/inst/unwind.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
#[cfg(feature = "unwind")]
|
||||
pub(crate) mod systemv;
|
||||
173
cranelift/codegen/src/isa/riscv64/inst/unwind/systemv.rs
Normal file
173
cranelift/codegen/src/isa/riscv64/inst/unwind/systemv.rs
Normal file
@@ -0,0 +1,173 @@
|
||||
//! Unwind information for System V ABI (Riscv64).
|
||||
|
||||
use crate::isa::riscv64::inst::regs;
|
||||
use crate::isa::unwind::systemv::RegisterMappingError;
|
||||
use crate::machinst::Reg;
|
||||
use gimli::{write::CommonInformationEntry, Encoding, Format, Register};
|
||||
use regalloc2::RegClass;
|
||||
|
||||
/// Creates a new riscv64 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().to_real_reg().unwrap().hw_enc() as u16),
|
||||
);
|
||||
|
||||
// Every frame will start with the call frame address (CFA) at SP
|
||||
let sp = Register(regs::stack_reg().to_real_reg().unwrap().hw_enc().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.class() {
|
||||
RegClass::Int => {
|
||||
let reg = reg.to_real_reg().unwrap().hw_enc() as u16;
|
||||
Ok(Register(reg))
|
||||
}
|
||||
RegClass::Float => {
|
||||
let reg = reg.to_real_reg().unwrap().hw_enc() as u16;
|
||||
Ok(Register(32 + reg))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) 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().to_real_reg().unwrap().hw_enc() as u16
|
||||
}
|
||||
fn fp(&self) -> Option<u16> {
|
||||
Some(regs::fp_reg().to_real_reg().unwrap().hw_enc() as u16)
|
||||
}
|
||||
fn lr(&self) -> Option<u16> {
|
||||
Some(regs::link_reg().to_real_reg().unwrap().hw_enc() as u16)
|
||||
}
|
||||
fn lr_offset(&self) -> Option<u32> {
|
||||
Some(8)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::cursor::{Cursor, FuncCursor};
|
||||
|
||||
use crate::ir::{
|
||||
types, AbiParam, Function, InstBuilder, Signature, StackSlotData, StackSlotKind,
|
||||
UserFuncName,
|
||||
};
|
||||
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!("riscv64"))
|
||||
.expect("expect riscv64 ISA")
|
||||
.finish(Flags::new(builder()))
|
||||
.expect("Creating compiler backend");
|
||||
|
||||
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: 40, lsda: None, instructions: [(12, CfaOffset(16)), (12, Offset(Register(8), -16)), (12, Offset(Register(1), -8)), (16, CfaRegister(Register(8)))] }");
|
||||
}
|
||||
|
||||
fn create_function(call_conv: CallConv, stack_slot: Option<StackSlotData>) -> Function {
|
||||
let mut func =
|
||||
Function::with_name_signature(UserFuncName::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.sized_stack_slots.push(stack_slot);
|
||||
}
|
||||
|
||||
func
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_return_func() {
|
||||
let isa = lookup(triple!("riscv64"))
|
||||
.expect("expect riscv64 ISA")
|
||||
.finish(Flags::new(builder()))
|
||||
.expect("Creating compiler backend");
|
||||
|
||||
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: 12, lsda: None, instructions: [] }"
|
||||
);
|
||||
}
|
||||
|
||||
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(UserFuncName::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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user