Merge pull request #2267 from cfallin/fix-aarch64-abi

Fix AArch64 ABI to respect half-caller-save, half-callee-save vec regs.
This commit is contained in:
Chris Fallin
2020-10-06 15:42:51 -07:00
committed by GitHub
29 changed files with 325 additions and 206 deletions

4
Cargo.lock generated
View File

@@ -1692,9 +1692,9 @@ dependencies = [
[[package]]
name = "regalloc"
version = "0.0.30"
version = "0.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2041c2d34f6ff346d6f428974f03d8bf12679b0c816bb640dc5eb1d48848d8d1"
checksum = "571f7f397d61c4755285cd37853fe8e03271c243424a907415909379659381c5"
dependencies = [
"log",
"rustc-hash",

View File

@@ -28,7 +28,7 @@ byteorder = { version = "1.3.2", default-features = false }
peepmatic = { path = "../peepmatic", optional = true, version = "0.67.0" }
peepmatic-traits = { path = "../peepmatic/crates/traits", optional = true, version = "0.67.0" }
peepmatic-runtime = { path = "../peepmatic/crates/runtime", optional = true, version = "0.67.0" }
regalloc = "0.0.30"
regalloc = { version = "0.0.31" }
souper-ir = { version = "1", optional = true }
wast = { version = "25.0.0", optional = true }
# It is a goal of the cranelift-codegen crate to have minimal external dependencies.

View File

@@ -510,7 +510,7 @@ impl ABIMachineSpec for AArch64MachineDeps {
fixed_frame_storage_size: u32,
) -> (u64, SmallVec<[Inst; 16]>) {
let mut insts = SmallVec::new();
let (clobbered_int, clobbered_vec) = get_callee_saves(call_conv, clobbers);
let (clobbered_int, clobbered_vec) = get_regs_saved_in_prologue(call_conv, clobbers);
let (int_save_bytes, vec_save_bytes) = saved_reg_stack_size(&clobbered_int, &clobbered_vec);
let total_save_bytes = (vec_save_bytes + int_save_bytes) as i32;
@@ -561,7 +561,7 @@ impl ABIMachineSpec for AArch64MachineDeps {
clobbers: &Set<Writable<RealReg>>,
) -> SmallVec<[Inst; 16]> {
let mut insts = SmallVec::new();
let (clobbered_int, clobbered_vec) = get_callee_saves(call_conv, clobbers);
let (clobbered_int, clobbered_vec) = get_regs_saved_in_prologue(call_conv, clobbers);
let (int_save_bytes, vec_save_bytes) = saved_reg_stack_size(&clobbered_int, &clobbered_vec);
for (i, reg_pair) in clobbered_int.chunks(2).enumerate() {
@@ -629,6 +629,8 @@ impl ABIMachineSpec for AArch64MachineDeps {
loc: SourceLoc,
opcode: ir::Opcode,
tmp: Writable<Reg>,
callee_conv: isa::CallConv,
caller_conv: isa::CallConv,
) -> SmallVec<[(InstIsSafepoint, Inst); 2]> {
let mut insts = SmallVec::new();
match &dest {
@@ -641,6 +643,8 @@ impl ABIMachineSpec for AArch64MachineDeps {
defs,
loc,
opcode,
caller_callconv: caller_conv,
callee_callconv: callee_conv,
}),
},
)),
@@ -663,6 +667,8 @@ impl ABIMachineSpec for AArch64MachineDeps {
defs,
loc,
opcode,
caller_callconv: caller_conv,
callee_callconv: callee_conv,
}),
},
));
@@ -676,6 +682,8 @@ impl ABIMachineSpec for AArch64MachineDeps {
defs,
loc,
opcode,
caller_callconv: caller_conv,
callee_callconv: callee_conv,
}),
},
)),
@@ -704,17 +712,17 @@ impl ABIMachineSpec for AArch64MachineDeps {
s.nominal_sp_to_fp
}
fn get_caller_saves(call_conv: isa::CallConv) -> Vec<Writable<Reg>> {
fn get_regs_clobbered_by_call(call_conv_of_callee: isa::CallConv) -> Vec<Writable<Reg>> {
let mut caller_saved = Vec::new();
for i in 0..29 {
let x = writable_xreg(i);
if is_caller_save_reg(call_conv, x.to_reg().to_real_reg()) {
if is_reg_clobbered_by_call(call_conv_of_callee, x.to_reg().to_real_reg()) {
caller_saved.push(x);
}
}
for i in 0..32 {
let v = writable_vreg(i);
if is_caller_save_reg(call_conv, v.to_reg().to_real_reg()) {
if is_reg_clobbered_by_call(call_conv_of_callee, v.to_reg().to_real_reg()) {
caller_saved.push(v);
}
}
@@ -731,7 +739,9 @@ fn legal_type_for_machine(ty: Type) -> bool {
}
}
fn is_callee_save_reg(call_conv: isa::CallConv, r: RealReg) -> bool {
/// Is the given register saved in the prologue if clobbered, i.e., is it a
/// callee-save?
fn is_reg_saved_in_prologue(call_conv: isa::CallConv, r: RealReg) -> bool {
if call_conv.extends_baldrdash() {
match r.get_class() {
RegClass::I64 => {
@@ -759,14 +769,17 @@ fn is_callee_save_reg(call_conv: isa::CallConv, r: RealReg) -> bool {
}
}
fn get_callee_saves(
/// Return the set of all integer and vector registers that must be saved in the
/// prologue and restored in the epilogue, given the set of all registers
/// written by the function's body.
fn get_regs_saved_in_prologue(
call_conv: isa::CallConv,
regs: &Set<Writable<RealReg>>,
) -> (Vec<Writable<RealReg>>, Vec<Writable<RealReg>>) {
let mut int_saves = vec![];
let mut vec_saves = vec![];
for &reg in regs.iter() {
if is_callee_save_reg(call_conv, reg.to_reg()) {
if is_reg_saved_in_prologue(call_conv, reg.to_reg()) {
match reg.to_reg().get_class() {
RegClass::I64 => int_saves.push(reg),
RegClass::V128 => vec_saves.push(reg),
@@ -781,8 +794,8 @@ fn get_callee_saves(
(int_saves, vec_saves)
}
fn is_caller_save_reg(call_conv: isa::CallConv, r: RealReg) -> bool {
if call_conv.extends_baldrdash() {
fn is_reg_clobbered_by_call(call_conv_of_callee: isa::CallConv, r: RealReg) -> bool {
if call_conv_of_callee.extends_baldrdash() {
match r.get_class() {
RegClass::I64 => {
let enc = r.get_hw_encoding();
@@ -808,8 +821,21 @@ fn is_caller_save_reg(call_conv: isa::CallConv, r: RealReg) -> bool {
r.get_hw_encoding() <= 17
}
RegClass::V128 => {
// v0 - v7 inclusive and v16 - v31 inclusive are caller-saves.
r.get_hw_encoding() <= 7 || (r.get_hw_encoding() >= 16 && r.get_hw_encoding() <= 31)
// v0 - v7 inclusive and v16 - v31 inclusive are caller-saves. The
// upper 64 bits of v8 - v15 inclusive are also caller-saves.
// However, because we cannot currently represent partial registers
// to regalloc.rs, we indicate here that every vector register is
// caller-save. Because this function is used at *callsites*,
// approximating in this direction (save more than necessary) is
// conservative and thus safe.
//
// Note that we set the 'not included in clobber set' flag in the
// regalloc.rs API when a call instruction's callee has the same ABI
// as the caller (the current function body); this is safe (anything
// clobbered by callee can be clobbered by caller as well) and
// avoids unnecessary saves of v8-v15 in the prologue even though we
// include them as defs here.
true
}
_ => panic!("Unexpected RegClass"),
}

View File

@@ -8,7 +8,7 @@ use crate::ir::Type;
use crate::isa::aarch64::inst::*;
use crate::machinst::{ty_bits, MachLabel};
use regalloc::{RealRegUniverse, Reg, Writable};
use regalloc::{PrettyPrint, RealRegUniverse, Reg, Writable};
use core::convert::Into;
use std::string::String;
@@ -348,19 +348,19 @@ impl BranchTarget {
}
}
impl ShowWithRRU for ShiftOpAndAmt {
impl PrettyPrint for ShiftOpAndAmt {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
format!("{:?} {}", self.op(), self.amt().value())
}
}
impl ShowWithRRU for ExtendOp {
impl PrettyPrint for ExtendOp {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
format!("{:?}", self)
}
}
impl ShowWithRRU for MemLabel {
impl PrettyPrint for MemLabel {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
match self {
&MemLabel::PCRel(off) => format!("pc+{}", off),
@@ -379,7 +379,7 @@ fn shift_for_type(ty: Type) -> usize {
}
}
impl ShowWithRRU for AMode {
impl PrettyPrint for AMode {
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
match self {
&AMode::Unscaled(reg, simm9) => {
@@ -458,7 +458,7 @@ impl ShowWithRRU for AMode {
}
}
impl ShowWithRRU for PairAMode {
impl PrettyPrint for PairAMode {
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
match self {
&PairAMode::SignedOffset(reg, simm7) => {
@@ -482,7 +482,7 @@ impl ShowWithRRU for PairAMode {
}
}
impl ShowWithRRU for Cond {
impl PrettyPrint for Cond {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
let mut s = format!("{:?}", self);
s.make_ascii_lowercase();
@@ -490,7 +490,7 @@ impl ShowWithRRU for Cond {
}
}
impl ShowWithRRU for BranchTarget {
impl PrettyPrint for BranchTarget {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
match self {
&BranchTarget::Label(label) => format!("label{:?}", label.get()),

View File

@@ -1,6 +1,7 @@
use crate::ir::types::*;
use crate::isa::aarch64::inst::*;
use crate::isa::test_utils;
use crate::isa::CallConv;
use crate::settings;
use alloc::boxed::Box;
@@ -3789,6 +3790,8 @@ fn test_aarch64_binemit() {
defs: Vec::new(),
loc: SourceLoc::default(),
opcode: Opcode::Call,
caller_callconv: CallConv::SystemV,
callee_callconv: CallConv::SystemV,
}),
},
"00000094",
@@ -3803,6 +3806,8 @@ fn test_aarch64_binemit() {
defs: Vec::new(),
loc: SourceLoc::default(),
opcode: Opcode::CallIndirect,
caller_callconv: CallConv::SystemV,
callee_callconv: CallConv::SystemV,
}),
},
"40013FD6",

View File

@@ -5,9 +5,8 @@
use crate::ir::types::*;
use crate::ir::Type;
use crate::isa::aarch64::inst::OperandSize;
use crate::machinst::*;
use regalloc::RealRegUniverse;
use regalloc::{PrettyPrint, RealRegUniverse};
use core::convert::TryFrom;
use std::string::String;
@@ -668,7 +667,7 @@ impl MoveWideConst {
}
}
impl ShowWithRRU for NZCV {
impl PrettyPrint for NZCV {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
let fmt = |c: char, v| if v { c.to_ascii_uppercase() } else { c };
format!(
@@ -681,13 +680,13 @@ impl ShowWithRRU for NZCV {
}
}
impl ShowWithRRU for UImm5 {
impl PrettyPrint for UImm5 {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
format!("#{}", self.value)
}
}
impl ShowWithRRU for Imm12 {
impl PrettyPrint for Imm12 {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
let shift = if self.shift12 { 12 } else { 0 };
let value = u32::from(self.bits) << shift;
@@ -695,49 +694,49 @@ impl ShowWithRRU for Imm12 {
}
}
impl ShowWithRRU for SImm7Scaled {
impl PrettyPrint for SImm7Scaled {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
format!("#{}", self.value)
}
}
impl ShowWithRRU for FPULeftShiftImm {
impl PrettyPrint for FPULeftShiftImm {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
format!("#{}", self.amount)
}
}
impl ShowWithRRU for FPURightShiftImm {
impl PrettyPrint for FPURightShiftImm {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
format!("#{}", self.amount)
}
}
impl ShowWithRRU for SImm9 {
impl PrettyPrint for SImm9 {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
format!("#{}", self.value)
}
}
impl ShowWithRRU for UImm12Scaled {
impl PrettyPrint for UImm12Scaled {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
format!("#{}", self.value)
}
}
impl ShowWithRRU for ImmLogic {
impl PrettyPrint for ImmLogic {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
format!("#{}", self.value())
}
}
impl ShowWithRRU for ImmShift {
impl PrettyPrint for ImmShift {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
format!("#{}", self.imm)
}
}
impl ShowWithRRU for MoveWideConst {
impl PrettyPrint for MoveWideConst {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
if self.shift == 0 {
format!("#{}", self.bits)

View File

@@ -9,10 +9,11 @@ use crate::ir::types::{
I32, I32X4, I64, I64X2, I8, I8X16, IFLAGS, R32, R64,
};
use crate::ir::{ExternalName, Opcode, SourceLoc, TrapCode, Type};
use crate::isa::CallConv;
use crate::machinst::*;
use crate::{settings, CodegenError, CodegenResult};
use regalloc::{RealRegUniverse, Reg, RegClass, SpillSlot, VirtualReg, Writable};
use regalloc::{PrettyPrint, RealRegUniverse, Reg, RegClass, SpillSlot, VirtualReg, Writable};
use regalloc::{RegUsageCollector, RegUsageMapper};
use alloc::boxed::Box;
@@ -392,6 +393,8 @@ pub struct CallInfo {
pub defs: Vec<Writable<Reg>>,
pub loc: SourceLoc,
pub opcode: Opcode,
pub caller_callconv: CallConv,
pub callee_callconv: CallConv,
}
/// Additional information for CallInd instructions, left out of line to lower the size of the Inst
@@ -403,6 +406,8 @@ pub struct CallIndInfo {
pub defs: Vec<Writable<Reg>>,
pub loc: SourceLoc,
pub opcode: Opcode,
pub caller_callconv: CallConv,
pub callee_callconv: CallConv,
}
/// Additional information for JTSequence instructions, left out of line to lower the size of the Inst
@@ -2491,6 +2496,24 @@ impl MachInst for Inst {
}
}
fn is_included_in_clobbers(&self) -> bool {
// We exclude call instructions from the clobber-set when they are calls
// from caller to callee with the same ABI. Such calls cannot possibly
// force any new registers to be saved in the prologue, because anything
// that the callee clobbers, the caller is also allowed to clobber. This
// both saves work and enables us to more precisely follow the
// half-caller-save, half-callee-save SysV ABI for some vector
// registers.
//
// See the note in [crate::isa::aarch64::abi::is_caller_save_reg] for
// more information on this ABI-implementation hack.
match self {
&Inst::Call { ref info } => info.caller_callconv != info.callee_callconv,
&Inst::CallInd { ref info } => info.caller_callconv != info.callee_callconv,
_ => true,
}
}
fn is_term<'a>(&'a self) -> MachTerminator<'a> {
match self {
&Inst::Ret | &Inst::EpiloguePlaceholder => MachTerminator::Ret,
@@ -2623,7 +2646,7 @@ fn mem_finalize_for_show(
(mem_str, mem)
}
impl ShowWithRRU for Inst {
impl PrettyPrint for Inst {
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
self.pretty_print(mb_rru, &mut EmitState::default())
}
@@ -2883,13 +2906,13 @@ impl Inst {
&Inst::StoreP64 { rt, rt2, ref mem } => {
let rt = rt.show_rru(mb_rru);
let rt2 = rt2.show_rru(mb_rru);
let mem = mem.show_rru_sized(mb_rru, /* size = */ 8);
let mem = mem.show_rru(mb_rru);
format!("stp {}, {}, {}", rt, rt2, mem)
}
&Inst::LoadP64 { rt, rt2, ref mem } => {
let rt = rt.to_reg().show_rru(mb_rru);
let rt2 = rt2.to_reg().show_rru(mb_rru);
let mem = mem.show_rru_sized(mb_rru, /* size = */ 8);
let mem = mem.show_rru(mb_rru);
format!("ldp {}, {}, {}", rt, rt2, mem)
}
&Inst::Mov64 { rd, rm } => {

View File

@@ -3,10 +3,11 @@
use crate::isa::aarch64::inst::OperandSize;
use crate::isa::aarch64::inst::ScalarSize;
use crate::isa::aarch64::inst::VectorSize;
use crate::machinst::*;
use crate::settings;
use regalloc::{RealRegUniverse, Reg, RegClass, RegClassInfo, Writable, NUM_REG_CLASSES};
use regalloc::{
PrettyPrint, RealRegUniverse, Reg, RegClass, RegClassInfo, Writable, NUM_REG_CLASSES,
};
use std::string::{String, ToString};

View File

@@ -1857,6 +1857,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
Opcode::Call | Opcode::CallIndirect => {
let loc = ctx.srcloc(insn);
let caller_conv = ctx.abi().call_conv();
let (mut abi, inputs) = match op {
Opcode::Call => {
let (extname, dist) = ctx.call_target(insn).unwrap();
@@ -1865,7 +1866,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
assert!(inputs.len() == sig.params.len());
assert!(outputs.len() == sig.returns.len());
(
AArch64ABICaller::from_func(sig, &extname, dist, loc)?,
AArch64ABICaller::from_func(sig, &extname, dist, loc, caller_conv)?,
&inputs[..],
)
}
@@ -1874,7 +1875,10 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
let sig = ctx.call_sig(insn).unwrap();
assert!(inputs.len() - 1 == sig.params.len());
assert!(outputs.len() == sig.returns.len());
(AArch64ABICaller::from_ptr(sig, ptr, loc, op)?, &inputs[1..])
(
AArch64ABICaller::from_ptr(sig, ptr, loc, op, caller_conv)?,
&inputs[1..],
)
}
_ => unreachable!(),
};

View File

@@ -3,15 +3,13 @@
use crate::ir::condcodes::IntCC;
use crate::ir::Function;
use crate::isa::Builder as IsaBuilder;
use crate::machinst::{
compile, MachBackend, MachCompileResult, ShowWithRRU, TargetIsaAdapter, VCode,
};
use crate::machinst::{compile, MachBackend, MachCompileResult, TargetIsaAdapter, VCode};
use crate::result::CodegenResult;
use crate::settings;
use alloc::boxed::Box;
use regalloc::RealRegUniverse;
use regalloc::{PrettyPrint, RealRegUniverse};
use target_lexicon::{Aarch64Architecture, Architecture, Triple};
// New backend:

View File

@@ -361,6 +361,8 @@ impl ABIMachineSpec for Arm32MachineDeps {
loc: SourceLoc,
opcode: ir::Opcode,
tmp: Writable<Reg>,
_callee_conv: isa::CallConv,
_caller_conv: isa::CallConv,
) -> SmallVec<[(InstIsSafepoint, Inst); 2]> {
let mut insts = SmallVec::new();
match &dest {
@@ -431,11 +433,11 @@ impl ABIMachineSpec for Arm32MachineDeps {
s.nominal_sp_to_fp
}
fn get_caller_saves(_call_conv: isa::CallConv) -> Vec<Writable<Reg>> {
fn get_regs_clobbered_by_call(_: isa::CallConv) -> Vec<Writable<Reg>> {
let mut caller_saved = Vec::new();
for i in 0..15 {
let r = writable_rreg(i);
if is_caller_save(r.to_reg().to_real_reg()) {
if is_reg_clobbered_by_call(r.to_reg().to_real_reg()) {
caller_saved.push(r);
}
}
@@ -461,7 +463,7 @@ fn get_callee_saves(regs: &Set<Writable<RealReg>>) -> Vec<Writable<RealReg>> {
ret
}
fn is_caller_save(r: RealReg) -> bool {
fn is_reg_clobbered_by_call(r: RealReg) -> bool {
let enc = r.get_hw_encoding();
enc <= 3
}

View File

@@ -2,7 +2,7 @@
use crate::isa::arm32::inst::*;
use regalloc::{RealRegUniverse, Reg};
use regalloc::{PrettyPrint, RealRegUniverse, Reg};
use std::string::String;
@@ -265,7 +265,7 @@ impl BranchTarget {
}
}
impl ShowWithRRU for ShiftOpAndAmt {
impl PrettyPrint for ShiftOpAndAmt {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
let op = match self.op() {
ShiftOp::LSL => "lsl",
@@ -277,19 +277,19 @@ impl ShowWithRRU for ShiftOpAndAmt {
}
}
impl ShowWithRRU for UImm8 {
impl PrettyPrint for UImm8 {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
format!("#{}", self.value)
}
}
impl ShowWithRRU for UImm12 {
impl PrettyPrint for UImm12 {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
format!("#{}", self.value)
}
}
impl ShowWithRRU for AMode {
impl PrettyPrint for AMode {
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
match self {
&AMode::RegReg(rn, rm, imm2) => {
@@ -317,7 +317,7 @@ impl ShowWithRRU for AMode {
}
}
impl ShowWithRRU for Cond {
impl PrettyPrint for Cond {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
let mut s = format!("{:?}", self);
s.make_ascii_lowercase();
@@ -325,7 +325,7 @@ impl ShowWithRRU for Cond {
}
}
impl ShowWithRRU for BranchTarget {
impl PrettyPrint for BranchTarget {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
match self {
&BranchTarget::Label(label) => format!("label{:?}", label.get()),

View File

@@ -8,7 +8,7 @@ use crate::ir::{ExternalName, Opcode, SourceLoc, TrapCode, Type};
use crate::machinst::*;
use crate::{settings, CodegenError, CodegenResult};
use regalloc::{RealRegUniverse, Reg, RegClass, SpillSlot, VirtualReg, Writable};
use regalloc::{PrettyPrint, RealRegUniverse, Reg, RegClass, SpillSlot, VirtualReg, Writable};
use regalloc::{RegUsageCollector, RegUsageMapper};
use alloc::boxed::Box;
@@ -897,7 +897,7 @@ fn mem_finalize_for_show(
(mem_str, mem)
}
impl ShowWithRRU for Inst {
impl PrettyPrint for Inst {
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
self.pretty_print(mb_rru, &mut EmitState::default())
}

View File

@@ -513,6 +513,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
}
Opcode::Call | Opcode::CallIndirect => {
let loc = ctx.srcloc(insn);
let caller_conv = ctx.abi().call_conv();
let (mut abi, inputs) = match op {
Opcode::Call => {
let (extname, dist) = ctx.call_target(insn).unwrap();
@@ -521,7 +522,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
assert_eq!(inputs.len(), sig.params.len());
assert_eq!(outputs.len(), sig.returns.len());
(
Arm32ABICaller::from_func(sig, &extname, dist, loc)?,
Arm32ABICaller::from_func(sig, &extname, dist, loc, caller_conv)?,
&inputs[..],
)
}
@@ -530,7 +531,10 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
let sig = ctx.call_sig(insn).unwrap();
assert_eq!(inputs.len() - 1, sig.params.len());
assert_eq!(outputs.len(), sig.returns.len());
(Arm32ABICaller::from_ptr(sig, ptr, loc, op)?, &inputs[1..])
(
Arm32ABICaller::from_ptr(sig, ptr, loc, op, caller_conv)?,
&inputs[1..],
)
}
_ => unreachable!(),
};

View File

@@ -3,14 +3,12 @@
use crate::ir::condcodes::IntCC;
use crate::ir::Function;
use crate::isa::Builder as IsaBuilder;
use crate::machinst::{
compile, MachBackend, MachCompileResult, ShowWithRRU, TargetIsaAdapter, VCode,
};
use crate::machinst::{compile, MachBackend, MachCompileResult, TargetIsaAdapter, VCode};
use crate::result::CodegenResult;
use crate::settings;
use alloc::boxed::Box;
use regalloc::RealRegUniverse;
use regalloc::{PrettyPrint, RealRegUniverse};
use target_lexicon::{Architecture, ArmArchitecture, Triple};
// New backend:

View File

@@ -493,6 +493,8 @@ impl ABIMachineSpec for X64ABIMachineSpec {
loc: SourceLoc,
opcode: ir::Opcode,
tmp: Writable<Reg>,
_callee_conv: isa::CallConv,
_caller_conv: isa::CallConv,
) -> SmallVec<[(InstIsSafepoint, Self::I); 2]> {
let mut insts = SmallVec::new();
match dest {
@@ -545,7 +547,7 @@ impl ABIMachineSpec for X64ABIMachineSpec {
s.nominal_sp_to_fp
}
fn get_caller_saves(call_conv: isa::CallConv) -> Vec<Writable<Reg>> {
fn get_regs_clobbered_by_call(call_conv_of_callee: isa::CallConv) -> Vec<Writable<Reg>> {
let mut caller_saved = vec![
// Systemv calling convention:
// - GPR: all except RBX, RBP, R12 to R15 (which are callee-saved).
@@ -577,7 +579,7 @@ impl ABIMachineSpec for X64ABIMachineSpec {
Writable::from_reg(regs::xmm15()),
];
if call_conv.extends_baldrdash() {
if call_conv_of_callee.extends_baldrdash() {
caller_saved.push(Writable::from_reg(regs::r12()));
caller_saved.push(Writable::from_reg(regs::r13()));
// Not r14; implicitly preserved in the entry.

View File

@@ -5,7 +5,10 @@ use super::EmitState;
use crate::ir::condcodes::{FloatCC, IntCC};
use crate::machinst::*;
use core::fmt::Debug;
use regalloc::{RealRegUniverse, Reg, RegClass, RegUsageCollector, RegUsageMapper, Writable};
use regalloc::{
PrettyPrint, PrettyPrintSized, RealRegUniverse, Reg, RegClass, RegUsageCollector,
RegUsageMapper, Writable,
};
use std::fmt;
use std::string::{String, ToString};
@@ -68,7 +71,7 @@ impl Amode {
}
}
impl ShowWithRRU for Amode {
impl PrettyPrint for Amode {
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
match self {
Amode::ImmReg { simm32, base } => {
@@ -156,7 +159,7 @@ impl Into<SyntheticAmode> for Amode {
}
}
impl ShowWithRRU for SyntheticAmode {
impl PrettyPrint for SyntheticAmode {
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
match self {
SyntheticAmode::Real(addr) => addr.show_rru(mb_rru),
@@ -214,11 +217,13 @@ impl RegMemImm {
}
}
impl ShowWithRRU for RegMemImm {
impl PrettyPrint for RegMemImm {
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
self.show_rru_sized(mb_rru, 8)
}
}
impl PrettyPrintSized for RegMemImm {
fn show_rru_sized(&self, mb_rru: Option<&RealRegUniverse>, size: u8) -> String {
match self {
Self::Reg { reg } => show_ireg_sized(*reg, mb_rru, size),
@@ -271,11 +276,13 @@ impl From<Writable<Reg>> for RegMem {
}
}
impl ShowWithRRU for RegMem {
impl PrettyPrint for RegMem {
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
self.show_rru_sized(mb_rru, 8)
}
}
impl PrettyPrintSized for RegMem {
fn show_rru_sized(&self, mb_rru: Option<&RealRegUniverse>, size: u8) -> String {
match self {
RegMem::Reg { reg } => show_ireg_sized(*reg, mb_rru, size),
@@ -1098,7 +1105,7 @@ pub enum BranchTarget {
ResolvedOffset(isize),
}
impl ShowWithRRU for BranchTarget {
impl PrettyPrint for BranchTarget {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
match self {
BranchTarget::Label(l) => format!("{:?}", l),

View File

@@ -8,8 +8,8 @@ use crate::{settings, settings::Flags, CodegenError, CodegenResult};
use alloc::boxed::Box;
use alloc::vec::Vec;
use regalloc::{
RealRegUniverse, Reg, RegClass, RegUsageCollector, RegUsageMapper, SpillSlot, VirtualReg,
Writable,
PrettyPrint, PrettyPrintSized, RealRegUniverse, Reg, RegClass, RegUsageCollector,
RegUsageMapper, SpillSlot, VirtualReg, Writable,
};
use smallvec::SmallVec;
use std::fmt;
@@ -1165,7 +1165,7 @@ impl Inst {
//=============================================================================
// Instructions: printing
impl ShowWithRRU for Inst {
impl PrettyPrint for Inst {
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
fn ljustify(s: String) -> String {
let w = 7;

View File

@@ -10,9 +10,11 @@
//! Also, they will have to be ABI dependent. Need to find a way to avoid constructing a universe
//! for each function we compile.
use crate::{machinst::pretty_print::ShowWithRRU, settings};
use crate::settings;
use alloc::vec::Vec;
use regalloc::{RealReg, RealRegUniverse, Reg, RegClass, RegClassInfo, NUM_REG_CLASSES};
use regalloc::{
PrettyPrint, RealReg, RealRegUniverse, Reg, RegClass, RegClassInfo, NUM_REG_CLASSES,
};
use std::string::String;
// Hardware encodings for a few registers.

View File

@@ -384,9 +384,10 @@ fn emit_vm_call<C: LowerCtx<I = Inst>>(
// TODO avoid recreating signatures for every single Libcall function.
let call_conv = CallConv::for_libcall(flags, CallConv::triple_default(triple));
let sig = make_libcall_sig(ctx, insn, call_conv, types::I64);
let caller_conv = ctx.abi().call_conv();
let loc = ctx.srcloc(insn);
let mut abi = X64ABICaller::from_func(&sig, &extname, dist, loc)?;
let mut abi = X64ABICaller::from_func(&sig, &extname, dist, loc, caller_conv)?;
abi.emit_stack_pre_adjust(ctx);
@@ -1558,6 +1559,7 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
Opcode::Call | Opcode::CallIndirect => {
let loc = ctx.srcloc(insn);
let caller_conv = ctx.abi().call_conv();
let (mut abi, inputs) = match op {
Opcode::Call => {
let (extname, dist) = ctx.call_target(insn).unwrap();
@@ -1565,7 +1567,7 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
assert_eq!(inputs.len(), sig.params.len());
assert_eq!(outputs.len(), sig.returns.len());
(
X64ABICaller::from_func(sig, &extname, dist, loc)?,
X64ABICaller::from_func(sig, &extname, dist, loc, caller_conv)?,
&inputs[..],
)
}
@@ -1575,7 +1577,10 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
let sig = ctx.call_sig(insn).unwrap();
assert_eq!(inputs.len() - 1, sig.params.len());
assert_eq!(outputs.len(), sig.returns.len());
(X64ABICaller::from_ptr(sig, ptr, loc, op)?, &inputs[1..])
(
X64ABICaller::from_ptr(sig, ptr, loc, op, caller_conv)?,
&inputs[1..],
)
}
_ => unreachable!(),

View File

@@ -4,13 +4,11 @@ use super::TargetIsa;
use crate::ir::{condcodes::IntCC, Function};
use crate::isa::x64::{inst::regs::create_reg_universe_systemv, settings as x64_settings};
use crate::isa::Builder as IsaBuilder;
use crate::machinst::{
compile, pretty_print::ShowWithRRU, MachBackend, MachCompileResult, TargetIsaAdapter, VCode,
};
use crate::machinst::{compile, MachBackend, MachCompileResult, TargetIsaAdapter, VCode};
use crate::result::CodegenResult;
use crate::settings::{self as shared_settings, Flags};
use alloc::boxed::Box;
use regalloc::RealRegUniverse;
use regalloc::{PrettyPrint, RealRegUniverse};
use target_lexicon::Triple;
mod abi;

View File

@@ -2,6 +2,7 @@
use crate::binemit::StackMap;
use crate::ir::StackSlot;
use crate::isa::CallConv;
use crate::machinst::*;
use crate::settings;
@@ -25,6 +26,9 @@ pub trait ABICallee {
/// Get the settings controlling this function's compilation.
fn flags(&self) -> &settings::Flags;
/// Get the calling convention implemented by this ABI object.
fn call_conv(&self) -> CallConv;
/// Get the liveins of the function.
fn liveins(&self) -> Set<RealReg>;

View File

@@ -347,6 +347,8 @@ pub trait ABIMachineSpec {
loc: SourceLoc,
opcode: ir::Opcode,
tmp: Writable<Reg>,
callee_conv: isa::CallConv,
callee_conv: isa::CallConv,
) -> SmallVec<[(InstIsSafepoint, Self::I); 2]>;
/// Get the number of spillslots required for the given register-class and
@@ -359,8 +361,9 @@ pub trait ABIMachineSpec {
/// Get the "nominal SP to FP" offset from an instruction-emission state.
fn get_nominal_sp_to_fp(s: &<Self::I as MachInstEmit>::State) -> i64;
/// Get all caller-save registers.
fn get_caller_saves(call_conv: isa::CallConv) -> Vec<Writable<Reg>>;
/// Get all caller-save registers, that is, registers that we expect
/// not to be saved across a call to a callee with the given ABI.
fn get_regs_clobbered_by_call(call_conv_of_callee: isa::CallConv) -> Vec<Writable<Reg>>;
}
/// ABI information shared between body (callee) and caller.
@@ -682,6 +685,10 @@ impl<M: ABIMachineSpec> ABICallee for ABICalleeImpl<M> {
&self.flags
}
fn call_conv(&self) -> isa::CallConv {
self.sig.call_conv
}
fn liveins(&self) -> Set<RealReg> {
let mut set: Set<RealReg> = Set::empty();
for &arg in &self.sig.args {
@@ -1040,7 +1047,7 @@ fn abisig_to_uses_and_defs<M: ABIMachineSpec>(sig: &ABISig) -> (Vec<Reg>, Vec<Wr
}
// Compute defs: all retval regs, and all caller-save (clobbered) regs.
let mut defs = M::get_caller_saves(sig.call_conv);
let mut defs = M::get_regs_clobbered_by_call(sig.call_conv);
for ret in &sig.rets {
match ret {
&ABIArg::Reg(reg, ..) => defs.push(Writable::from_reg(reg.to_reg())),
@@ -1063,8 +1070,10 @@ pub struct ABICallerImpl<M: ABIMachineSpec> {
dest: CallDest,
/// Location of callsite.
loc: ir::SourceLoc,
/// Actuall call opcode; used to distinguish various types of calls.
/// Actual call opcode; used to distinguish various types of calls.
opcode: ir::Opcode,
/// Caller's calling convention.
caller_conv: isa::CallConv,
_mach: PhantomData<M>,
}
@@ -1085,6 +1094,7 @@ impl<M: ABIMachineSpec> ABICallerImpl<M> {
extname: &ir::ExternalName,
dist: RelocDistance,
loc: ir::SourceLoc,
caller_conv: isa::CallConv,
) -> CodegenResult<ABICallerImpl<M>> {
let sig = ABISig::from_func_sig::<M>(sig)?;
let (uses, defs) = abisig_to_uses_and_defs::<M>(&sig);
@@ -1095,6 +1105,7 @@ impl<M: ABIMachineSpec> ABICallerImpl<M> {
dest: CallDest::ExtName(extname.clone(), dist),
loc,
opcode: ir::Opcode::Call,
caller_conv,
_mach: PhantomData,
})
}
@@ -1106,6 +1117,7 @@ impl<M: ABIMachineSpec> ABICallerImpl<M> {
ptr: Reg,
loc: ir::SourceLoc,
opcode: ir::Opcode,
caller_conv: isa::CallConv,
) -> CodegenResult<ABICallerImpl<M>> {
let sig = ABISig::from_func_sig::<M>(sig)?;
let (uses, defs) = abisig_to_uses_and_defs::<M>(&sig);
@@ -1116,6 +1128,7 @@ impl<M: ABIMachineSpec> ABICallerImpl<M> {
dest: CallDest::Reg(ptr),
loc,
opcode,
caller_conv,
_mach: PhantomData,
})
}
@@ -1255,8 +1268,17 @@ impl<M: ABIMachineSpec> ABICaller for ABICallerImpl<M> {
self.emit_copy_reg_to_arg(ctx, i, rd.to_reg());
}
let tmp = ctx.alloc_tmp(word_rc, word_type);
for (is_safepoint, inst) in
M::gen_call(&self.dest, uses, defs, self.loc, self.opcode, tmp).into_iter()
for (is_safepoint, inst) in M::gen_call(
&self.dest,
uses,
defs,
self.loc,
self.opcode,
tmp,
self.sig.call_conv,
self.caller_conv,
)
.into_iter()
{
match is_safepoint {
InstIsSafepoint::Yes => ctx.emit_safepoint(inst),

View File

@@ -6,7 +6,7 @@ use crate::settings;
use crate::timing;
use log::debug;
use regalloc::{allocate_registers_with_opts, Algorithm, Options};
use regalloc::{allocate_registers_with_opts, Algorithm, Options, PrettyPrint};
/// Compile the given function down to VCode with allocated registers, ready
/// for binary emission.
@@ -16,7 +16,7 @@ pub fn compile<B: LowerBackend + MachBackend>(
abi: Box<dyn ABICallee<I = B::MInst>>,
) -> CodegenResult<VCode<B::MInst>>
where
B::MInst: ShowWithRRU,
B::MInst: PrettyPrint,
{
// Compute lowered block order.
let block_order = BlockLoweringOrder::new(f);

View File

@@ -125,8 +125,6 @@ pub mod abi;
pub use abi::*;
pub mod abi_impl;
pub use abi_impl::*;
pub mod pretty_print;
pub use pretty_print::*;
pub mod buffer;
pub use buffer::*;
pub mod adapter;
@@ -156,6 +154,11 @@ pub trait MachInst: Clone + Debug {
/// Returns true if the instruction is an epilogue placeholder.
fn is_epilogue_placeholder(&self) -> bool;
/// Should this instruction be included in the clobber-set?
fn is_included_in_clobbers(&self) -> bool {
true
}
/// Generate a move.
fn gen_move(to_reg: Writable<Reg>, from_reg: Reg, ty: Type) -> Self;

View File

@@ -1,66 +0,0 @@
//! Pretty-printing for machine code (virtual-registerized or final).
use regalloc::{RealRegUniverse, Reg, Writable};
use std::fmt::Debug;
use std::hash::Hash;
use std::string::{String, ToString};
// FIXME: Should this go into regalloc.rs instead?
/// A trait for printing instruction bits and pieces, with the the ability to
/// take a contextualising RealRegUniverse that is used to give proper names to
/// registers.
pub trait ShowWithRRU {
/// Return a string that shows the implementing object in context of the
/// given `RealRegUniverse`, if provided.
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String;
/// The same as |show_rru|, but with an optional hint giving a size in
/// bytes. Its interpretation is object-dependent, and it is intended to
/// pass around enough information to facilitate printing sub-parts of
/// real registers correctly. Objects may ignore size hints that are
/// irrelevant to them.
fn show_rru_sized(&self, mb_rru: Option<&RealRegUniverse>, _size: u8) -> String {
// Default implementation is to ignore the hint.
self.show_rru(mb_rru)
}
}
impl ShowWithRRU for Reg {
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
if self.is_real() {
if let Some(rru) = mb_rru {
let reg_ix = self.get_index();
if reg_ix < rru.regs.len() {
return rru.regs[reg_ix].1.to_string();
} else {
// We have a real reg which isn't listed in the universe.
// Per the regalloc.rs interface requirements, this is
// Totally Not Allowed. Print it generically anyway, so
// we have something to debug.
return format!("!!{:?}!!", self);
}
}
}
// The reg is virtual, or we have no universe. Be generic.
format!("%{:?}", self)
}
fn show_rru_sized(&self, _mb_rru: Option<&RealRegUniverse>, _size: u8) -> String {
// For the specific case of Reg, we demand not to have a size hint,
// since interpretation of the size is target specific, but this code
// is used by all targets.
panic!("Reg::show_rru_sized: impossible to implement");
}
}
impl<R: ShowWithRRU + Copy + Ord + Hash + Eq + Debug> ShowWithRRU for Writable<R> {
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
self.to_reg().show_rru(mb_rru)
}
fn show_rru_sized(&self, mb_rru: Option<&RealRegUniverse>, size: u8) -> String {
self.to_reg().show_rru_sized(mb_rru, size)
}
}

View File

@@ -25,8 +25,8 @@ use crate::timing;
use regalloc::Function as RegallocFunction;
use regalloc::Set as RegallocSet;
use regalloc::{
BlockIx, InstIx, Range, RegAllocResult, RegClass, RegUsageCollector, RegUsageMapper, SpillSlot,
StackmapRequestInfo,
BlockIx, InstIx, PrettyPrint, Range, RegAllocResult, RegClass, RegUsageCollector,
RegUsageMapper, SpillSlot, StackmapRequestInfo,
};
use alloc::boxed::Box;
@@ -543,6 +543,10 @@ impl<I: VCodeInst> RegallocFunction for VCode<I> {
}
}
fn is_included_in_clobbers(&self, insn: &I) -> bool {
insn.is_included_in_clobbers()
}
fn get_regs(insn: &I, collector: &mut RegUsageCollector) {
insn.get_regs(collector)
}
@@ -624,7 +628,7 @@ impl<I: VCodeInst> fmt::Debug for VCode<I> {
}
/// Pretty-printing with `RealRegUniverse` context.
impl<I: VCodeInst> ShowWithRRU for VCode<I> {
impl<I: VCodeInst> PrettyPrint for VCode<I> {
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
use std::fmt::Write;

View File

@@ -151,34 +151,27 @@ block0:
; check: stp fp, lr, [sp, #-16]!
; nextln: mov fp, sp
; nextln: sub sp, sp, #48
; nextln: str q8, [sp]
; nextln: str q9, [sp, #16]
; nextln: str q10, [sp, #32]
; nextln: virtual_sp_offset_adjust 48
; nextln: sub sp, sp, #32
; nextln: ldr x0, 8 ; b 12 ; data
; nextln: blr x0
; nextln: mov v8.16b, v0.16b
; nextln: str s0, [sp]
; nextln: ldr x0, 8 ; b 12 ; data
; nextln: blr x0
; nextln: mov v9.16b, v0.16b
; nextln: str d0, [sp, #8]
; nextln: ldr x0, 8 ; b 12 ; data
; nextln: blr x0
; nextln: mov v10.16b, v0.16b
; nextln: str d0, [sp, #16]
; nextln: ldr x0, 8 ; b 12 ; data
; nextln: blr x0
; nextln: mov v0.16b, v8.16b
; nextln: ldr s0, [sp]
; nextln: ldr x0, 8 ; b 12 ; data
; nextln: blr x0
; nextln: mov v0.16b, v9.16b
; nextln: ldr d0, [sp, #8]
; nextln: ldr x0, 8 ; b 12 ; data
; nextln: blr x0
; nextln: mov v0.16b, v10.16b
; nextln: ldr d0, [sp, #16]
; nextln: ldr x0, 8 ; b 12 ; data
; nextln: blr x0
; nextln: ldr q8, [sp]
; nextln: ldr q9, [sp, #16]
; nextln: ldr q10, [sp, #32]
; nextln: mov sp, fp
; nextln: ldp fp, lr, [sp], #16
; nextln: ret
@@ -202,33 +195,26 @@ block0:
; check: stp fp, lr, [sp, #-16]!
; nextln: mov fp, sp
; nextln: sub sp, sp, #48
; nextln: str q8, [sp]
; nextln: str q9, [sp, #16]
; nextln: str q10, [sp, #32]
; nextln: virtual_sp_offset_adjust 48
; nextln: ldr x0, 8 ; b 12 ; data
; nextln: blr x0
; nextln: mov v8.16b, v0.16b
; nextln: str q0, [sp]
; nextln: ldr x0, 8 ; b 12 ; data
; nextln: blr x0
; nextln: mov v9.16b, v0.16b
; nextln: str q0, [sp, #16]
; nextln: ldr x0, 8 ; b 12 ; data
; nextln: blr x0
; nextln: mov v10.16b, v0.16b
; nextln: str q0, [sp, #32]
; nextln: ldr x0, 8 ; b 12 ; data
; nextln: blr x0
; nextln: mov v0.16b, v8.16b
; nextln: ldr q0, [sp]
; nextln: ldr x0, 8 ; b 12 ; data
; nextln: blr x0
; nextln: mov v0.16b, v9.16b
; nextln: ldr q0, [sp, #16]
; nextln: ldr x0, 8 ; b 12 ; data
; nextln: blr x0
; nextln: mov v0.16b, v10.16b
; nextln: ldr q0, [sp, #32]
; nextln: ldr x0, 8 ; b 12 ; data
; nextln: blr x0
; nextln: ldr q8, [sp]
; nextln: ldr q9, [sp, #16]
; nextln: ldr q10, [sp, #32]
; nextln: mov sp, fp
; nextln: ldp fp, lr, [sp], #16
; nextln: ret
@@ -255,34 +241,27 @@ block0:
; check: stp fp, lr, [sp, #-16]!
; nextln: mov fp, sp
; nextln: sub sp, sp, #48
; nextln: str q8, [sp]
; nextln: str q9, [sp, #16]
; nextln: str q10, [sp, #32]
; nextln: virtual_sp_offset_adjust 48
; nextln: sub sp, sp, #32
; nextln: ldr x0, 8 ; b 12 ; data
; nextln: blr x0
; nextln: mov v8.16b, v0.16b
; nextln: str s0, [sp]
; nextln: ldr x0, 8 ; b 12 ; data
; nextln: blr x0
; nextln: mov v9.16b, v0.16b
; nextln: str d0, [sp, #8]
; nextln: ldr x0, 8 ; b 12 ; data
; nextln: blr x0
; nextln: mov v10.16b, v0.16b
; nextln: str q0, [sp, #16]
; nextln: ldr x0, 8 ; b 12 ; data
; nextln: blr x0
; nextln: mov v0.16b, v8.16b
; nextln: ldr s0, [sp]
; nextln: ldr x0, 8 ; b 12 ; data
; nextln: blr x0
; nextln: mov v0.16b, v9.16b
; nextln: ldr d0, [sp, #8]
; nextln: ldr x0, 8 ; b 12 ; data
; nextln: blr x0
; nextln: mov v0.16b, v10.16b
; nextln: ldr q0, [sp, #16]
; nextln: ldr x0, 8 ; b 12 ; data
; nextln: blr x0
; nextln: ldr q8, [sp]
; nextln: ldr q9, [sp, #16]
; nextln: ldr q10, [sp, #32]
; nextln: mov sp, fp
; nextln: ldp fp, lr, [sp], #16
; nextln: ret

View File

@@ -0,0 +1,99 @@
test compile
target aarch64
function %f(f64) -> f64 {
block0(v0: f64):
v1 = fadd.f64 v0, v0
v2 = fadd.f64 v0, v0
v3 = fadd.f64 v0, v0
v4 = fadd.f64 v0, v0
v5 = fadd.f64 v0, v0
v6 = fadd.f64 v0, v0
v7 = fadd.f64 v0, v0
v8 = fadd.f64 v0, v0
v9 = fadd.f64 v0, v0
v10 = fadd.f64 v0, v0
v11 = fadd.f64 v0, v0
v12 = fadd.f64 v0, v0
v13 = fadd.f64 v0, v0
v14 = fadd.f64 v0, v0
v15 = fadd.f64 v0, v0
v16 = fadd.f64 v0, v0
v17 = fadd.f64 v0, v0
v18 = fadd.f64 v0, v0
v19 = fadd.f64 v0, v0
v20 = fadd.f64 v0, v0
v21 = fadd.f64 v0, v0
v22 = fadd.f64 v0, v0
v23 = fadd.f64 v0, v0
v24 = fadd.f64 v0, v0
v25 = fadd.f64 v0, v0
v26 = fadd.f64 v0, v0
v27 = fadd.f64 v0, v0
v28 = fadd.f64 v0, v0
v29 = fadd.f64 v0, v0
v30 = fadd.f64 v0, v0
v31 = fadd.f64 v0, v0
v32 = fadd.f64 v0, v1
v33 = fadd.f64 v2, v3
v34 = fadd.f64 v4, v5
v35 = fadd.f64 v6, v7
v36 = fadd.f64 v8, v9
v37 = fadd.f64 v10, v11
v38 = fadd.f64 v12, v13
v39 = fadd.f64 v14, v15
v40 = fadd.f64 v16, v17
v41 = fadd.f64 v18, v19
v42 = fadd.f64 v20, v21
v43 = fadd.f64 v22, v23
v44 = fadd.f64 v24, v25
v45 = fadd.f64 v26, v27
v46 = fadd.f64 v28, v29
v47 = fadd.f64 v30, v31
v48 = fadd.f64 v32, v33
v49 = fadd.f64 v34, v35
v50 = fadd.f64 v36, v37
v51 = fadd.f64 v38, v39
v52 = fadd.f64 v40, v41
v53 = fadd.f64 v42, v43
v54 = fadd.f64 v44, v45
v55 = fadd.f64 v46, v47
v56 = fadd.f64 v48, v49
v57 = fadd.f64 v50, v51
v58 = fadd.f64 v52, v53
v59 = fadd.f64 v54, v55
v60 = fadd.f64 v56, v57
v61 = fadd.f64 v58, v59
v62 = fadd.f64 v60, v61
return v62
}
; check: stp fp, lr, [sp, #-16]!
; nextln: mov fp, sp
; nextln: sub sp, sp, #128
; nextln: str q8, [sp]
; nextln: str q9, [sp, #16]
; nextln: str q10, [sp, #32]
; nextln: str q11, [sp, #48]
; nextln: str q12, [sp, #64]
; nextln: str q13, [sp, #80]
; nextln: str q14, [sp, #96]
; nextln: str q15, [sp, #112]
; check: ldr q8, [sp]
; nextln: ldr q9, [sp, #16]
; nextln: ldr q10, [sp, #32]
; nextln: ldr q11, [sp, #48]
; nextln: ldr q12, [sp, #64]
; nextln: ldr q13, [sp, #80]
; nextln: ldr q14, [sp, #96]
; nextln: ldr q15, [sp, #112]
; nextln: mov sp, fp
; nextln: ldp fp, lr, [sp], #16
; nextln: ret