Cranelift: Add instructions for getting the current stack/frame/return pointers (#4573)
* Cranelift: Add instructions for getting the current stack/frame pointers and return address This is the initial part of https://github.com/bytecodealliance/wasmtime/issues/4535 * x64: Remove `Amode::RbpOffset` and use `Amode::ImmReg` instead We just special case getting operands from `Amode`s now. * Fix s390x `get_return_address`; require `preserve_frame_pointers=true` * Assert that `Amode::ImmRegRegShift` doesn't use rbp/rsp * Handle non-allocatable registers in Amode::with_allocs * Use "stack" instead of "r15" on s390x * r14 is an allocatable register on s390x, so it shouldn't be used with `MovPReg`
This commit is contained in:
@@ -1296,6 +1296,43 @@ pub(crate) fn define(
|
|||||||
.other_side_effects(true),
|
.other_side_effects(true),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ig.push(
|
||||||
|
Inst::new(
|
||||||
|
"get_frame_pointer",
|
||||||
|
r#"
|
||||||
|
Get the address in the frame pointer register.
|
||||||
|
|
||||||
|
Usage of this instruction requires setting `preserve_frame_pointers` to `true`.
|
||||||
|
"#,
|
||||||
|
&formats.nullary,
|
||||||
|
)
|
||||||
|
.operands_out(vec![addr]),
|
||||||
|
);
|
||||||
|
|
||||||
|
ig.push(
|
||||||
|
Inst::new(
|
||||||
|
"get_stack_pointer",
|
||||||
|
r#"
|
||||||
|
Get the address in the stack pointer register.
|
||||||
|
"#,
|
||||||
|
&formats.nullary,
|
||||||
|
)
|
||||||
|
.operands_out(vec![addr]),
|
||||||
|
);
|
||||||
|
|
||||||
|
ig.push(
|
||||||
|
Inst::new(
|
||||||
|
"get_return_address",
|
||||||
|
r#"
|
||||||
|
Get the PC where this function will transfer control to when it returns.
|
||||||
|
|
||||||
|
Usage of this instruction requires setting `preserve_frame_pointers` to `true`.
|
||||||
|
"#,
|
||||||
|
&formats.nullary,
|
||||||
|
)
|
||||||
|
.operands_out(vec![addr]),
|
||||||
|
);
|
||||||
|
|
||||||
let TableOffset = &TypeVar::new(
|
let TableOffset = &TypeVar::new(
|
||||||
"TableOffset",
|
"TableOffset",
|
||||||
"An unsigned table offset",
|
"An unsigned table offset",
|
||||||
|
|||||||
@@ -165,6 +165,12 @@
|
|||||||
(rd WritableReg)
|
(rd WritableReg)
|
||||||
(rm Reg))
|
(rm Reg))
|
||||||
|
|
||||||
|
;; Like `Move` but with a particular `PReg` source (for implementing CLIF
|
||||||
|
;; instructions like `get_stack_pointer`).
|
||||||
|
(MovPReg
|
||||||
|
(rd WritableReg)
|
||||||
|
(rm PReg))
|
||||||
|
|
||||||
;; A MOV[Z,N,K] with a 16-bit immediate.
|
;; A MOV[Z,N,K] with a 16-bit immediate.
|
||||||
(MovWide
|
(MovWide
|
||||||
(op MoveWideOp)
|
(op MoveWideOp)
|
||||||
@@ -2421,3 +2427,31 @@
|
|||||||
;; And finally, copy the preordained AtomicCASLoop output reg to its destination.
|
;; And finally, copy the preordained AtomicCASLoop output reg to its destination.
|
||||||
;; Also, x24 and x28 are trashed.
|
;; Also, x24 and x28 are trashed.
|
||||||
(mov64_from_real 27)))
|
(mov64_from_real 27)))
|
||||||
|
|
||||||
|
;; Helper for emitting `MInst.MovPReg` instructions.
|
||||||
|
(decl mov_preg (PReg) Reg)
|
||||||
|
(rule (mov_preg src)
|
||||||
|
(let ((dst WritableReg (temp_writable_reg $I64))
|
||||||
|
(_ Unit (emit (MInst.MovPReg dst src))))
|
||||||
|
dst))
|
||||||
|
|
||||||
|
(decl preg_sp () PReg)
|
||||||
|
(extern constructor preg_sp preg_sp)
|
||||||
|
|
||||||
|
(decl preg_fp () PReg)
|
||||||
|
(extern constructor preg_fp preg_fp)
|
||||||
|
|
||||||
|
(decl preg_link () PReg)
|
||||||
|
(extern constructor preg_link preg_link)
|
||||||
|
|
||||||
|
(decl aarch64_sp () Reg)
|
||||||
|
(rule (aarch64_sp)
|
||||||
|
(mov_preg (preg_sp)))
|
||||||
|
|
||||||
|
(decl aarch64_fp () Reg)
|
||||||
|
(rule (aarch64_fp)
|
||||||
|
(mov_preg (preg_fp)))
|
||||||
|
|
||||||
|
(decl aarch64_link () Reg)
|
||||||
|
(rule (aarch64_link)
|
||||||
|
(mov_preg (preg_link)))
|
||||||
|
|||||||
@@ -1334,6 +1334,15 @@ impl MachInstEmit for Inst {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&Inst::MovPReg { rd, rm } => {
|
||||||
|
let rd = allocs.next_writable(rd);
|
||||||
|
let rm: Reg = rm.into();
|
||||||
|
debug_assert!([regs::fp_reg(), regs::stack_reg(), regs::link_reg()].contains(&rm));
|
||||||
|
assert!(rm.class() == RegClass::Int);
|
||||||
|
assert!(rd.to_reg().class() == rm.class());
|
||||||
|
let size = OperandSize::Size64;
|
||||||
|
Inst::Mov { size, rd, rm }.emit(&[], sink, emit_info, state);
|
||||||
|
}
|
||||||
&Inst::MovWide { op, rd, imm, size } => {
|
&Inst::MovWide { op, rd, imm, size } => {
|
||||||
let rd = allocs.next_writable(rd);
|
let rd = allocs.next_writable(rd);
|
||||||
sink.put4(enc_move_wide(op, rd, imm, size));
|
sink.put4(enc_move_wide(op, rd, imm, size));
|
||||||
|
|||||||
@@ -649,6 +649,13 @@ fn aarch64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut Operan
|
|||||||
collector.reg_def(rd);
|
collector.reg_def(rd);
|
||||||
collector.reg_use(rm);
|
collector.reg_use(rm);
|
||||||
}
|
}
|
||||||
|
&Inst::MovPReg { rd, rm } => {
|
||||||
|
debug_assert!(
|
||||||
|
[regs::fp_reg(), regs::stack_reg(), regs::link_reg()].contains(&rm.into())
|
||||||
|
);
|
||||||
|
debug_assert!(rd.to_reg().is_virtual());
|
||||||
|
collector.reg_def(rd);
|
||||||
|
}
|
||||||
&Inst::MovWide { op, rd, .. } => match op {
|
&Inst::MovWide { op, rd, .. } => match op {
|
||||||
MoveWideOp::MovK => collector.reg_mod(rd),
|
MoveWideOp::MovK => collector.reg_mod(rd),
|
||||||
_ => collector.reg_def(rd),
|
_ => collector.reg_def(rd),
|
||||||
@@ -1474,6 +1481,11 @@ impl Inst {
|
|||||||
let rm = pretty_print_ireg(rm, size, allocs);
|
let rm = pretty_print_ireg(rm, size, allocs);
|
||||||
format!("mov {}, {}", rd, rm)
|
format!("mov {}, {}", rd, rm)
|
||||||
}
|
}
|
||||||
|
&Inst::MovPReg { rd, rm } => {
|
||||||
|
let rd = pretty_print_ireg(rd.to_reg(), OperandSize::Size64, allocs);
|
||||||
|
let rm = show_ireg_sized(rm.into(), OperandSize::Size64);
|
||||||
|
format!("mov {}, {}", rd, rm)
|
||||||
|
}
|
||||||
&Inst::MovWide {
|
&Inst::MovWide {
|
||||||
op,
|
op,
|
||||||
rd,
|
rd,
|
||||||
|
|||||||
@@ -1697,6 +1697,7 @@
|
|||||||
|
|
||||||
|
|
||||||
;;;; Rules for `uunarrow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;; Rules for `uunarrow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(rule (lower (has_type (ty_vec128_int ty) (uunarrow x y)))
|
(rule (lower (has_type (ty_vec128_int ty) (uunarrow x y)))
|
||||||
(if (zero_value y))
|
(if (zero_value y))
|
||||||
(uqxtn x (lane_size ty)))
|
(uqxtn x (lane_size ty)))
|
||||||
@@ -1733,3 +1734,14 @@
|
|||||||
|
|
||||||
(rule (lower (debugtrap))
|
(rule (lower (debugtrap))
|
||||||
(side_effect (brk)))
|
(side_effect (brk)))
|
||||||
|
|
||||||
|
;;; Rules for `get_{frame,stack}_pointer` and `get_return_address` ;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(rule (lower (get_frame_pointer))
|
||||||
|
(aarch64_fp))
|
||||||
|
|
||||||
|
(rule (lower (get_stack_pointer))
|
||||||
|
(aarch64_sp))
|
||||||
|
|
||||||
|
(rule (lower (get_return_address))
|
||||||
|
(aarch64_link))
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ use crate::{
|
|||||||
isa::unwind::UnwindInst,
|
isa::unwind::UnwindInst,
|
||||||
machinst::{ty_bits, InsnOutput, LowerCtx, VCodeConstant, VCodeConstantData},
|
machinst::{ty_bits, InsnOutput, LowerCtx, VCodeConstant, VCodeConstantData},
|
||||||
};
|
};
|
||||||
|
use regalloc2::PReg;
|
||||||
use std::boxed::Box;
|
use std::boxed::Box;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
@@ -466,4 +467,16 @@ where
|
|||||||
|
|
||||||
rd.to_reg()
|
rd.to_reg()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn preg_sp(&mut self) -> PReg {
|
||||||
|
super::regs::stack_reg().to_real_reg().unwrap().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn preg_fp(&mut self) -> PReg {
|
||||||
|
super::regs::fp_reg().to_real_reg().unwrap().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn preg_link(&mut self) -> PReg {
|
||||||
|
super::regs::link_reg().to_real_reg().unwrap().into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,10 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||||||
point, as constants are rematerialized at use-sites"
|
point, as constants are rematerialized at use-sites"
|
||||||
),
|
),
|
||||||
|
|
||||||
|
Opcode::GetFramePointer | Opcode::GetStackPointer | Opcode::GetReturnAddress => {
|
||||||
|
implemented_in_isle(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
Opcode::Iadd => implemented_in_isle(ctx),
|
Opcode::Iadd => implemented_in_isle(ctx),
|
||||||
Opcode::Isub => implemented_in_isle(ctx),
|
Opcode::Isub => implemented_in_isle(ctx),
|
||||||
Opcode::UaddSat | Opcode::SaddSat | Opcode::UsubSat | Opcode::SsubSat => {
|
Opcode::UaddSat | Opcode::SaddSat | Opcode::UsubSat | Opcode::SsubSat => {
|
||||||
|
|||||||
@@ -373,6 +373,11 @@
|
|||||||
(rd WritableReg)
|
(rd WritableReg)
|
||||||
(rm Reg))
|
(rm Reg))
|
||||||
|
|
||||||
|
;; Like `Mov64` but with a particular physical register source.
|
||||||
|
(MovPReg
|
||||||
|
(rd WritableReg)
|
||||||
|
(rm PReg))
|
||||||
|
|
||||||
;; A 32-bit move instruction with a full 32-bit immediate.
|
;; A 32-bit move instruction with a full 32-bit immediate.
|
||||||
(Mov32Imm
|
(Mov32Imm
|
||||||
(rd WritableReg)
|
(rd WritableReg)
|
||||||
@@ -1556,6 +1561,10 @@
|
|||||||
(decl memarg_stack_off (i64 i64) MemArg)
|
(decl memarg_stack_off (i64 i64) MemArg)
|
||||||
(extern constructor memarg_stack_off memarg_stack_off)
|
(extern constructor memarg_stack_off memarg_stack_off)
|
||||||
|
|
||||||
|
;; Create a `MemArg` referring to an offset from the initial SP.
|
||||||
|
(decl memarg_initial_sp_offset (i64) MemArg)
|
||||||
|
(extern constructor memarg_initial_sp_offset memarg_initial_sp_offset)
|
||||||
|
|
||||||
;; Form the sum of two offset values, and check that the result is
|
;; Form the sum of two offset values, and check that the result is
|
||||||
;; a valid `MemArg::Symbol` offset (i.e. is even and fits into i32).
|
;; a valid `MemArg::Symbol` offset (i.e. is even and fits into i32).
|
||||||
(decl pure memarg_symbol_offset_sum (i64 i64) i32)
|
(decl pure memarg_symbol_offset_sum (i64 i64) i32)
|
||||||
@@ -2469,6 +2478,20 @@
|
|||||||
(rule (emit_load $I64 dst addr)
|
(rule (emit_load $I64 dst addr)
|
||||||
(emit (MInst.Load64 dst addr)))
|
(emit (MInst.Load64 dst addr)))
|
||||||
|
|
||||||
|
;; Helper for creating `MInst.MovPReg` instructions.
|
||||||
|
(decl mov_preg (PReg) Reg)
|
||||||
|
(rule (mov_preg src)
|
||||||
|
(let ((dst WritableReg (temp_writable_reg $I64))
|
||||||
|
(_ Unit (emit (MInst.MovPReg dst src))))
|
||||||
|
dst))
|
||||||
|
|
||||||
|
(decl preg_stack () PReg)
|
||||||
|
(extern constructor preg_stack preg_stack)
|
||||||
|
|
||||||
|
;; Copy the physical stack register into a virtual register.
|
||||||
|
(decl sp () Reg)
|
||||||
|
(rule (sp)
|
||||||
|
(mov_preg (preg_stack)))
|
||||||
|
|
||||||
;; Helpers for accessing argument / return value slots ;;;;;;;;;;;;;;;;;;;;;;;;;
|
;; Helpers for accessing argument / return value slots ;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
|||||||
@@ -2081,6 +2081,12 @@ impl MachInstEmit for Inst {
|
|||||||
let opcode = 0xb904; // LGR
|
let opcode = 0xb904; // LGR
|
||||||
put(sink, &enc_rre(opcode, rd.to_reg(), rm));
|
put(sink, &enc_rre(opcode, rd.to_reg(), rm));
|
||||||
}
|
}
|
||||||
|
&Inst::MovPReg { rd, rm } => {
|
||||||
|
let rm: Reg = rm.into();
|
||||||
|
debug_assert!([regs::gpr(15)].contains(&rm));
|
||||||
|
let rd = allocs.next_writable(rd);
|
||||||
|
Inst::Mov64 { rd, rm }.emit(&[], sink, emit_info, state);
|
||||||
|
}
|
||||||
&Inst::Mov32 { rd, rm } => {
|
&Inst::Mov32 { rd, rm } => {
|
||||||
let rd = allocs.next_writable(rd);
|
let rd = allocs.next_writable(rd);
|
||||||
let rm = allocs.next(rm);
|
let rm = allocs.next(rm);
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ impl Inst {
|
|||||||
| Inst::StoreMultiple64 { .. }
|
| Inst::StoreMultiple64 { .. }
|
||||||
| Inst::Mov32 { .. }
|
| Inst::Mov32 { .. }
|
||||||
| Inst::Mov64 { .. }
|
| Inst::Mov64 { .. }
|
||||||
|
| Inst::MovPReg { .. }
|
||||||
| Inst::Mov32Imm { .. }
|
| Inst::Mov32Imm { .. }
|
||||||
| Inst::Mov32SImm16 { .. }
|
| Inst::Mov32SImm16 { .. }
|
||||||
| Inst::Mov64SImm16 { .. }
|
| Inst::Mov64SImm16 { .. }
|
||||||
@@ -623,6 +624,11 @@ fn s390x_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandC
|
|||||||
collector.reg_def(rd);
|
collector.reg_def(rd);
|
||||||
collector.reg_use(rm);
|
collector.reg_use(rm);
|
||||||
}
|
}
|
||||||
|
&Inst::MovPReg { rd, rm } => {
|
||||||
|
debug_assert!([regs::gpr(14), regs::gpr(15)].contains(&rm.into()));
|
||||||
|
debug_assert!(rd.to_reg().is_virtual());
|
||||||
|
collector.reg_def(rd);
|
||||||
|
}
|
||||||
&Inst::Mov32 { rd, rm } => {
|
&Inst::Mov32 { rd, rm } => {
|
||||||
collector.reg_def(rd);
|
collector.reg_def(rd);
|
||||||
collector.reg_use(rm);
|
collector.reg_use(rm);
|
||||||
@@ -1778,6 +1784,11 @@ impl Inst {
|
|||||||
let rm = pretty_print_reg(rm, allocs);
|
let rm = pretty_print_reg(rm, allocs);
|
||||||
format!("lgr {}, {}", rd, rm)
|
format!("lgr {}, {}", rd, rm)
|
||||||
}
|
}
|
||||||
|
&Inst::MovPReg { rd, rm } => {
|
||||||
|
let rd = pretty_print_reg(rd.to_reg(), allocs);
|
||||||
|
let rm = show_reg(rm.into());
|
||||||
|
format!("lgr {}, {}", rd, rm)
|
||||||
|
}
|
||||||
&Inst::Mov32 { rd, rm } => {
|
&Inst::Mov32 { rd, rm } => {
|
||||||
let rd = pretty_print_reg(rd.to_reg(), allocs);
|
let rd = pretty_print_reg(rd.to_reg(), allocs);
|
||||||
let rm = pretty_print_reg(rm, allocs);
|
let rm = pretty_print_reg(rm, allocs);
|
||||||
|
|||||||
@@ -3616,3 +3616,15 @@
|
|||||||
(_ Unit (output_builder_push builder ret)))
|
(_ Unit (output_builder_push builder ret)))
|
||||||
(lower_call_rets abi tail builder)))
|
(lower_call_rets abi tail builder)))
|
||||||
|
|
||||||
|
;;;; Rules for `get_{frame,stack}_pointer` and `get_return_address` ;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(rule (lower (get_stack_pointer))
|
||||||
|
(sp))
|
||||||
|
|
||||||
|
(rule (lower (get_frame_pointer))
|
||||||
|
(load64 (memarg_stack_off 0 0)))
|
||||||
|
|
||||||
|
(rule (lower (get_return_address))
|
||||||
|
;; The return address is 14 pointer-sized slots above the initial SP. So
|
||||||
|
;; our offset is `14 * 8 = 112`.
|
||||||
|
(load64 (memarg_initial_sp_offset 112)))
|
||||||
|
|||||||
@@ -180,7 +180,10 @@ impl LowerBackend for S390xBackend {
|
|||||||
| Opcode::Return
|
| Opcode::Return
|
||||||
| Opcode::StackAddr
|
| Opcode::StackAddr
|
||||||
| Opcode::FuncAddr
|
| Opcode::FuncAddr
|
||||||
| Opcode::SymbolValue => {
|
| Opcode::SymbolValue
|
||||||
|
| Opcode::GetFramePointer
|
||||||
|
| Opcode::GetStackPointer
|
||||||
|
| Opcode::GetReturnAddress => {
|
||||||
unreachable!(
|
unreachable!(
|
||||||
"implemented in ISLE: inst = `{}`, type = `{:?}`",
|
"implemented in ISLE: inst = `{}`, type = `{:?}`",
|
||||||
ctx.dfg().display_inst(ir_inst),
|
ctx.dfg().display_inst(ir_inst),
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ use crate::{
|
|||||||
isa::unwind::UnwindInst,
|
isa::unwind::UnwindInst,
|
||||||
machinst::{InsnOutput, LowerCtx, VCodeConstant, VCodeConstantData},
|
machinst::{InsnOutput, LowerCtx, VCodeConstant, VCodeConstantData},
|
||||||
};
|
};
|
||||||
|
use regalloc2::PReg;
|
||||||
use std::boxed::Box;
|
use std::boxed::Box;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
@@ -603,6 +604,11 @@ where
|
|||||||
MemArg::reg_plus_off(stack_reg(), base + off, MemFlags::trusted())
|
MemArg::reg_plus_off(stack_reg(), base + off, MemFlags::trusted())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn memarg_initial_sp_offset(&mut self, off: i64) -> MemArg {
|
||||||
|
MemArg::InitialSPOffset { off }
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn memarg_symbol(&mut self, name: ExternalName, offset: i32, flags: MemFlags) -> MemArg {
|
fn memarg_symbol(&mut self, name: ExternalName, offset: i32, flags: MemFlags) -> MemArg {
|
||||||
MemArg::Symbol {
|
MemArg::Symbol {
|
||||||
@@ -670,6 +676,11 @@ where
|
|||||||
fn emit(&mut self, inst: &MInst) -> Unit {
|
fn emit(&mut self, inst: &MInst) -> Unit {
|
||||||
self.lower_ctx.emit(inst.clone());
|
self.lower_ctx.emit(inst.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn preg_stack(&mut self) -> PReg {
|
||||||
|
stack_reg().to_real_reg().unwrap().into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Zero-extend the low `from_bits` bits of `value` to a full u64.
|
/// Zero-extend the low `from_bits` bits of `value` to a full u64.
|
||||||
|
|||||||
@@ -298,16 +298,16 @@ pub(crate) fn emit_std_enc_mem(
|
|||||||
|
|
||||||
prefixes.emit(sink);
|
prefixes.emit(sink);
|
||||||
|
|
||||||
match mem_e {
|
match *mem_e {
|
||||||
Amode::ImmReg { simm32, base, .. } => {
|
Amode::ImmReg { simm32, base, .. } => {
|
||||||
// If this is an access based off of RSP, it may trap with a stack overflow if it's the
|
// If this is an access based off of RSP, it may trap with a stack overflow if it's the
|
||||||
// first touch of a new stack page.
|
// first touch of a new stack page.
|
||||||
if *base == regs::rsp() && !can_trap && info.flags.enable_probestack() {
|
if base == regs::rsp() && !can_trap && info.flags.enable_probestack() {
|
||||||
sink.add_trap(TrapCode::StackOverflow);
|
sink.add_trap(TrapCode::StackOverflow);
|
||||||
}
|
}
|
||||||
|
|
||||||
// First, the REX byte.
|
// First, the REX byte.
|
||||||
let enc_e = int_reg_enc(*base);
|
let enc_e = int_reg_enc(base);
|
||||||
rex.emit_two_op(sink, enc_g, enc_e);
|
rex.emit_two_op(sink, enc_g, enc_e);
|
||||||
|
|
||||||
// Now the opcode(s). These include any other prefixes the caller
|
// Now the opcode(s). These include any other prefixes the caller
|
||||||
@@ -319,7 +319,7 @@ pub(crate) fn emit_std_enc_mem(
|
|||||||
|
|
||||||
// Now the mod/rm and associated immediates. This is
|
// Now the mod/rm and associated immediates. This is
|
||||||
// significantly complicated due to the multiple special cases.
|
// significantly complicated due to the multiple special cases.
|
||||||
if *simm32 == 0
|
if simm32 == 0
|
||||||
&& enc_e != regs::ENC_RSP
|
&& enc_e != regs::ENC_RSP
|
||||||
&& enc_e != regs::ENC_RBP
|
&& enc_e != regs::ENC_RBP
|
||||||
&& enc_e != regs::ENC_R12
|
&& enc_e != regs::ENC_R12
|
||||||
@@ -329,10 +329,10 @@ pub(crate) fn emit_std_enc_mem(
|
|||||||
// replaced by a single mask-and-compare check. We should do
|
// replaced by a single mask-and-compare check. We should do
|
||||||
// that because this routine is likely to be hot.
|
// that because this routine is likely to be hot.
|
||||||
sink.put1(encode_modrm(0, enc_g & 7, enc_e & 7));
|
sink.put1(encode_modrm(0, enc_g & 7, enc_e & 7));
|
||||||
} else if *simm32 == 0 && (enc_e == regs::ENC_RSP || enc_e == regs::ENC_R12) {
|
} else if simm32 == 0 && (enc_e == regs::ENC_RSP || enc_e == regs::ENC_R12) {
|
||||||
sink.put1(encode_modrm(0, enc_g & 7, 4));
|
sink.put1(encode_modrm(0, enc_g & 7, 4));
|
||||||
sink.put1(0x24);
|
sink.put1(0x24);
|
||||||
} else if low8_will_sign_extend_to_32(*simm32)
|
} else if low8_will_sign_extend_to_32(simm32)
|
||||||
&& enc_e != regs::ENC_RSP
|
&& enc_e != regs::ENC_RSP
|
||||||
&& enc_e != regs::ENC_R12
|
&& enc_e != regs::ENC_R12
|
||||||
{
|
{
|
||||||
@@ -340,9 +340,9 @@ pub(crate) fn emit_std_enc_mem(
|
|||||||
sink.put1((simm32 & 0xFF) as u8);
|
sink.put1((simm32 & 0xFF) as u8);
|
||||||
} else if enc_e != regs::ENC_RSP && enc_e != regs::ENC_R12 {
|
} else if enc_e != regs::ENC_RSP && enc_e != regs::ENC_R12 {
|
||||||
sink.put1(encode_modrm(2, enc_g & 7, enc_e & 7));
|
sink.put1(encode_modrm(2, enc_g & 7, enc_e & 7));
|
||||||
sink.put4(*simm32);
|
sink.put4(simm32);
|
||||||
} else if (enc_e == regs::ENC_RSP || enc_e == regs::ENC_R12)
|
} else if (enc_e == regs::ENC_RSP || enc_e == regs::ENC_R12)
|
||||||
&& low8_will_sign_extend_to_32(*simm32)
|
&& low8_will_sign_extend_to_32(simm32)
|
||||||
{
|
{
|
||||||
// REX.B distinguishes RSP from R12
|
// REX.B distinguishes RSP from R12
|
||||||
sink.put1(encode_modrm(1, enc_g & 7, 4));
|
sink.put1(encode_modrm(1, enc_g & 7, 4));
|
||||||
@@ -353,7 +353,7 @@ pub(crate) fn emit_std_enc_mem(
|
|||||||
// REX.B distinguishes RSP from R12
|
// REX.B distinguishes RSP from R12
|
||||||
sink.put1(encode_modrm(2, enc_g & 7, 4));
|
sink.put1(encode_modrm(2, enc_g & 7, 4));
|
||||||
sink.put1(0x24);
|
sink.put1(0x24);
|
||||||
sink.put4(*simm32);
|
sink.put4(simm32);
|
||||||
} else {
|
} else {
|
||||||
unreachable!("ImmReg");
|
unreachable!("ImmReg");
|
||||||
}
|
}
|
||||||
@@ -385,14 +385,14 @@ pub(crate) fn emit_std_enc_mem(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// modrm, SIB, immediates.
|
// modrm, SIB, immediates.
|
||||||
if low8_will_sign_extend_to_32(*simm32) && enc_index != regs::ENC_RSP {
|
if low8_will_sign_extend_to_32(simm32) && enc_index != regs::ENC_RSP {
|
||||||
sink.put1(encode_modrm(1, enc_g & 7, 4));
|
sink.put1(encode_modrm(1, enc_g & 7, 4));
|
||||||
sink.put1(encode_sib(*shift, enc_index & 7, enc_base & 7));
|
sink.put1(encode_sib(shift, enc_index & 7, enc_base & 7));
|
||||||
sink.put1(*simm32 as u8);
|
sink.put1(simm32 as u8);
|
||||||
} else if enc_index != regs::ENC_RSP {
|
} else if enc_index != regs::ENC_RSP {
|
||||||
sink.put1(encode_modrm(2, enc_g & 7, 4));
|
sink.put1(encode_modrm(2, enc_g & 7, 4));
|
||||||
sink.put1(encode_sib(*shift, enc_index & 7, enc_base & 7));
|
sink.put1(encode_sib(shift, enc_index & 7, enc_base & 7));
|
||||||
sink.put4(*simm32);
|
sink.put4(simm32);
|
||||||
} else {
|
} else {
|
||||||
panic!("ImmRegRegShift");
|
panic!("ImmRegRegShift");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,6 +104,11 @@
|
|||||||
(src Gpr)
|
(src Gpr)
|
||||||
(dst WritableGpr))
|
(dst WritableGpr))
|
||||||
|
|
||||||
|
;; Like `MovRR` but with a physical register source (for implementing
|
||||||
|
;; CLIF instructions like `get_stack_pointer`).
|
||||||
|
(MovPReg (src PReg)
|
||||||
|
(dst WritableGpr))
|
||||||
|
|
||||||
;; Zero-extended loads, except for 64 bits: movz (bl bq wl wq lq) addr
|
;; Zero-extended loads, except for 64 bits: movz (bl bq wl wq lq) addr
|
||||||
;; reg.
|
;; reg.
|
||||||
;;
|
;;
|
||||||
@@ -3328,3 +3333,24 @@
|
|||||||
(decl synthetic_amode_to_xmm_mem (SyntheticAmode) XmmMem)
|
(decl synthetic_amode_to_xmm_mem (SyntheticAmode) XmmMem)
|
||||||
(rule (synthetic_amode_to_xmm_mem amode)
|
(rule (synthetic_amode_to_xmm_mem amode)
|
||||||
(synthetic_amode_to_reg_mem amode))
|
(synthetic_amode_to_reg_mem amode))
|
||||||
|
|
||||||
|
;; Helper for creating `MovPReg` instructions.
|
||||||
|
(decl mov_preg (PReg) Reg)
|
||||||
|
(rule (mov_preg preg)
|
||||||
|
(let ((dst WritableGpr (temp_writable_gpr))
|
||||||
|
(_ Unit (emit (MInst.MovPReg preg dst))))
|
||||||
|
dst))
|
||||||
|
|
||||||
|
(decl preg_rbp () PReg)
|
||||||
|
(extern constructor preg_rbp preg_rbp)
|
||||||
|
|
||||||
|
(decl preg_rsp () PReg)
|
||||||
|
(extern constructor preg_rsp preg_rsp)
|
||||||
|
|
||||||
|
(decl x64_rbp () Reg)
|
||||||
|
(rule (x64_rbp)
|
||||||
|
(mov_preg (preg_rbp)))
|
||||||
|
|
||||||
|
(decl x64_rsp () Reg)
|
||||||
|
(rule (x64_rsp)
|
||||||
|
(mov_preg (preg_rsp)))
|
||||||
|
|||||||
@@ -313,10 +313,16 @@ impl Amode {
|
|||||||
) {
|
) {
|
||||||
match self {
|
match self {
|
||||||
Amode::ImmReg { base, .. } => {
|
Amode::ImmReg { base, .. } => {
|
||||||
collector.reg_use(*base);
|
if *base != regs::rbp() && *base != regs::rsp() {
|
||||||
|
collector.reg_use(*base);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Amode::ImmRegRegShift { base, index, .. } => {
|
Amode::ImmRegRegShift { base, index, .. } => {
|
||||||
|
debug_assert_ne!(base.to_reg(), regs::rbp());
|
||||||
|
debug_assert_ne!(base.to_reg(), regs::rsp());
|
||||||
collector.reg_use(base.to_reg());
|
collector.reg_use(base.to_reg());
|
||||||
|
debug_assert_ne!(index.to_reg(), regs::rbp());
|
||||||
|
debug_assert_ne!(index.to_reg(), regs::rsp());
|
||||||
collector.reg_use(index.to_reg());
|
collector.reg_use(index.to_reg());
|
||||||
}
|
}
|
||||||
Amode::RipRelative { .. } => {
|
Amode::RipRelative { .. } => {
|
||||||
@@ -346,8 +352,7 @@ impl Amode {
|
|||||||
|
|
||||||
pub(crate) fn get_flags(&self) -> MemFlags {
|
pub(crate) fn get_flags(&self) -> MemFlags {
|
||||||
match self {
|
match self {
|
||||||
Amode::ImmReg { flags, .. } => *flags,
|
Amode::ImmReg { flags, .. } | Amode::ImmRegRegShift { flags, .. } => *flags,
|
||||||
Amode::ImmRegRegShift { flags, .. } => *flags,
|
|
||||||
Amode::RipRelative { .. } => MemFlags::trusted(),
|
Amode::RipRelative { .. } => MemFlags::trusted(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -364,11 +369,18 @@ impl Amode {
|
|||||||
simm32,
|
simm32,
|
||||||
base,
|
base,
|
||||||
flags,
|
flags,
|
||||||
} => Amode::ImmReg {
|
} => {
|
||||||
simm32,
|
let base = if base == regs::rsp() || base == regs::rbp() {
|
||||||
flags,
|
base
|
||||||
base: allocs.next(base),
|
} else {
|
||||||
},
|
allocs.next(base)
|
||||||
|
};
|
||||||
|
Amode::ImmReg {
|
||||||
|
simm32,
|
||||||
|
flags,
|
||||||
|
base,
|
||||||
|
}
|
||||||
|
}
|
||||||
&Amode::ImmRegRegShift {
|
&Amode::ImmRegRegShift {
|
||||||
simm32,
|
simm32,
|
||||||
base,
|
base,
|
||||||
|
|||||||
@@ -678,6 +678,16 @@ pub(crate) fn emit(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Inst::MovPReg { src, dst } => {
|
||||||
|
let src: Reg = (*src).into();
|
||||||
|
debug_assert!([regs::rsp(), regs::rbp()].contains(&src));
|
||||||
|
let src = Gpr::new(src).unwrap();
|
||||||
|
let size = OperandSize::Size64;
|
||||||
|
let dst = allocs.next(dst.to_reg().to_reg());
|
||||||
|
let dst = WritableGpr::from_writable_reg(Writable::from_reg(dst)).unwrap();
|
||||||
|
Inst::MovRR { size, src, dst }.emit(&[], sink, info, state);
|
||||||
|
}
|
||||||
|
|
||||||
Inst::MovzxRmR { ext_mode, src, dst } => {
|
Inst::MovzxRmR { ext_mode, src, dst } => {
|
||||||
let dst = allocs.next(dst.to_reg().to_reg());
|
let dst = allocs.next(dst.to_reg().to_reg());
|
||||||
let (opcodes, num_opcodes, mut rex_flags) = match ext_mode {
|
let (opcodes, num_opcodes, mut rex_flags) = match ext_mode {
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ impl Inst {
|
|||||||
| Inst::Mov64MR { .. }
|
| Inst::Mov64MR { .. }
|
||||||
| Inst::MovRM { .. }
|
| Inst::MovRM { .. }
|
||||||
| Inst::MovRR { .. }
|
| Inst::MovRR { .. }
|
||||||
|
| Inst::MovPReg { .. }
|
||||||
| Inst::MovsxRmR { .. }
|
| Inst::MovsxRmR { .. }
|
||||||
| Inst::MovzxRmR { .. }
|
| Inst::MovzxRmR { .. }
|
||||||
| Inst::MulHi { .. }
|
| Inst::MulHi { .. }
|
||||||
@@ -1423,6 +1424,13 @@ impl PrettyPrint for Inst {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Inst::MovPReg { src, dst } => {
|
||||||
|
let src: Reg = (*src).into();
|
||||||
|
let src = regs::show_ireg_sized(src, 8);
|
||||||
|
let dst = pretty_print_reg(dst.to_reg().to_reg(), 8, allocs);
|
||||||
|
format!("{} {}, {}", ljustify("movq".to_string()), src, dst)
|
||||||
|
}
|
||||||
|
|
||||||
Inst::MovzxRmR {
|
Inst::MovzxRmR {
|
||||||
ext_mode, src, dst, ..
|
ext_mode, src, dst, ..
|
||||||
} => {
|
} => {
|
||||||
@@ -1984,6 +1992,11 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
|
|||||||
collector.reg_use(src.to_reg());
|
collector.reg_use(src.to_reg());
|
||||||
collector.reg_def(dst.to_writable_reg());
|
collector.reg_def(dst.to_writable_reg());
|
||||||
}
|
}
|
||||||
|
Inst::MovPReg { dst, src } => {
|
||||||
|
debug_assert!([regs::rsp(), regs::rbp()].contains(&(*src).into()));
|
||||||
|
debug_assert!(dst.to_reg().to_reg().is_virtual());
|
||||||
|
collector.reg_def(dst.to_writable_reg());
|
||||||
|
}
|
||||||
Inst::XmmToGpr { src, dst, .. } => {
|
Inst::XmmToGpr { src, dst, .. } => {
|
||||||
collector.reg_use(src.to_reg());
|
collector.reg_use(src.to_reg());
|
||||||
collector.reg_def(dst.to_writable_reg());
|
collector.reg_def(dst.to_writable_reg());
|
||||||
|
|||||||
@@ -2839,3 +2839,16 @@
|
|||||||
|
|
||||||
(rule (lower (call_indirect sig_ref val inputs))
|
(rule (lower (call_indirect sig_ref val inputs))
|
||||||
(gen_call_indirect sig_ref val inputs))
|
(gen_call_indirect sig_ref val inputs))
|
||||||
|
|
||||||
|
;;;; Rules for `get_{frame,stack}_pointer` and `get_return_address` ;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(rule (lower (get_frame_pointer))
|
||||||
|
(x64_rbp))
|
||||||
|
|
||||||
|
(rule (lower (get_stack_pointer))
|
||||||
|
(x64_rsp))
|
||||||
|
|
||||||
|
(rule (lower (get_return_address))
|
||||||
|
(x64_load $I64
|
||||||
|
(Amode.ImmReg 8 (preg_rbp) (mem_flags_trusted))
|
||||||
|
(ExtKind.None)))
|
||||||
|
|||||||
@@ -914,7 +914,10 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||||||
| Opcode::Call
|
| Opcode::Call
|
||||||
| Opcode::CallIndirect
|
| Opcode::CallIndirect
|
||||||
| Opcode::Trapif
|
| Opcode::Trapif
|
||||||
| Opcode::Trapff => {
|
| Opcode::Trapff
|
||||||
|
| Opcode::GetFramePointer
|
||||||
|
| Opcode::GetStackPointer
|
||||||
|
| Opcode::GetReturnAddress => {
|
||||||
implemented_in_isle(ctx);
|
implemented_in_isle(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ use crate::{
|
|||||||
VCodeConstant, VCodeConstantData,
|
VCodeConstant, VCodeConstantData,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use regalloc2::PReg;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::boxed::Box;
|
use std::boxed::Box;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
@@ -636,6 +637,16 @@ where
|
|||||||
|
|
||||||
self.gen_call_common(abi, num_rets, caller, args)
|
self.gen_call_common(abi, num_rets, caller, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn preg_rbp(&mut self) -> PReg {
|
||||||
|
regs::rbp().to_real_reg().unwrap().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn preg_rsp(&mut self) -> PReg {
|
||||||
|
regs::rsp().to_real_reg().unwrap().into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C> IsleContext<'_, C, Flags, IsaFlags, 6>
|
impl<C> IsleContext<'_, C, Flags, IsaFlags, 6>
|
||||||
|
|||||||
@@ -879,6 +879,16 @@ macro_rules! isle_prelude_methods {
|
|||||||
fn sink_inst(&mut self, inst: Inst) {
|
fn sink_inst(&mut self, inst: Inst) {
|
||||||
self.lower_ctx.sink_inst(inst);
|
self.lower_ctx.sink_inst(inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn mem_flags_trusted(&mut self) -> MemFlags {
|
||||||
|
MemFlags::trusted()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn preg_to_reg(&mut self, preg: PReg) -> Reg {
|
||||||
|
preg.into()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,7 @@
|
|||||||
(type OptionWritableReg (primitive OptionWritableReg))
|
(type OptionWritableReg (primitive OptionWritableReg))
|
||||||
(type VecReg extern (enum))
|
(type VecReg extern (enum))
|
||||||
(type VecWritableReg extern (enum))
|
(type VecWritableReg extern (enum))
|
||||||
|
(type PReg (primitive PReg))
|
||||||
|
|
||||||
;; Construct a `ValueRegs` of one register.
|
;; Construct a `ValueRegs` of one register.
|
||||||
(decl value_reg (Reg) ValueRegs)
|
(decl value_reg (Reg) ValueRegs)
|
||||||
@@ -196,6 +197,10 @@
|
|||||||
(let ((regs ValueRegs (put_in_regs val)))
|
(let ((regs ValueRegs (put_in_regs val)))
|
||||||
(value_regs_get regs 0)))
|
(value_regs_get regs 0)))
|
||||||
|
|
||||||
|
;; Convert a `PReg` into a `Reg`
|
||||||
|
(decl preg_to_reg (PReg) Reg)
|
||||||
|
(extern constructor preg_to_reg preg_to_reg)
|
||||||
|
|
||||||
;;;; Common Mach Types ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;; Common Mach Types ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(type MachLabel (primitive MachLabel))
|
(type MachLabel (primitive MachLabel))
|
||||||
@@ -293,6 +298,12 @@
|
|||||||
(decl lane_type (Type) Type)
|
(decl lane_type (Type) Type)
|
||||||
(extern constructor lane_type lane_type)
|
(extern constructor lane_type lane_type)
|
||||||
|
|
||||||
|
;;;; `cranelift_codegen::ir::MemFlags ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
;; `MemFlags::trusted`
|
||||||
|
(decl mem_flags_trusted () MemFlags)
|
||||||
|
(extern constructor mem_flags_trusted mem_flags_trusted)
|
||||||
|
|
||||||
;;;; Helper Clif Extractors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;; Helper Clif Extractors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
;; An extractor that only matches types that can fit in 16 bits.
|
;; An extractor that only matches types that can fit in 16 bits.
|
||||||
@@ -887,3 +898,4 @@
|
|||||||
(convert Value InstOutput output_value)
|
(convert Value InstOutput output_value)
|
||||||
(convert Offset32 u32 offset32_to_u32)
|
(convert Offset32 u32 offset32_to_u32)
|
||||||
(convert ExternalName BoxExternalName box_external_name)
|
(convert ExternalName BoxExternalName box_external_name)
|
||||||
|
(convert PReg Reg preg_to_reg)
|
||||||
|
|||||||
@@ -721,6 +721,26 @@ impl<'a> Verifier<'a> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
NullAry {
|
||||||
|
opcode: Opcode::GetFramePointer | Opcode::GetReturnAddress,
|
||||||
|
} => {
|
||||||
|
if let Some(isa) = &self.isa {
|
||||||
|
if !isa.flags().preserve_frame_pointers() {
|
||||||
|
return errors.fatal((
|
||||||
|
inst,
|
||||||
|
self.context(inst),
|
||||||
|
"`get_frame_pointer`/`get_return_address` cannot be used without \
|
||||||
|
enabling `preserve_frame_pointers`",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return errors.fatal((
|
||||||
|
inst,
|
||||||
|
self.context(inst),
|
||||||
|
"`get_frame_pointer`/`get_return_address` require an ISA!",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
Unary {
|
Unary {
|
||||||
opcode: Opcode::Bitcast,
|
opcode: Opcode::Bitcast,
|
||||||
arg,
|
arg,
|
||||||
|
|||||||
43
cranelift/filetests/filetests/isa/aarch64/fp_sp_pc.clif
Normal file
43
cranelift/filetests/filetests/isa/aarch64/fp_sp_pc.clif
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
test compile precise-output
|
||||||
|
set preserve_frame_pointers=true
|
||||||
|
target aarch64
|
||||||
|
|
||||||
|
function %fp() -> i64 {
|
||||||
|
block0:
|
||||||
|
v0 = get_frame_pointer.i64
|
||||||
|
return v0
|
||||||
|
}
|
||||||
|
|
||||||
|
; stp fp, lr, [sp, #-16]!
|
||||||
|
; mov fp, sp
|
||||||
|
; block0:
|
||||||
|
; mov x0, fp
|
||||||
|
; ldp fp, lr, [sp], #16
|
||||||
|
; ret
|
||||||
|
|
||||||
|
function %sp() -> i64 {
|
||||||
|
block0:
|
||||||
|
v0 = get_stack_pointer.i64
|
||||||
|
return v0
|
||||||
|
}
|
||||||
|
|
||||||
|
; stp fp, lr, [sp, #-16]!
|
||||||
|
; mov fp, sp
|
||||||
|
; block0:
|
||||||
|
; mov x0, sp
|
||||||
|
; ldp fp, lr, [sp], #16
|
||||||
|
; ret
|
||||||
|
|
||||||
|
function %return_address() -> i64 {
|
||||||
|
block0:
|
||||||
|
v0 = get_return_address.i64
|
||||||
|
return v0
|
||||||
|
}
|
||||||
|
|
||||||
|
; stp fp, lr, [sp, #-16]!
|
||||||
|
; mov fp, sp
|
||||||
|
; block0:
|
||||||
|
; mov x0, lr
|
||||||
|
; ldp fp, lr, [sp], #16
|
||||||
|
; ret
|
||||||
|
|
||||||
52
cranelift/filetests/filetests/isa/s390x/fp_sp_pc.clif
Normal file
52
cranelift/filetests/filetests/isa/s390x/fp_sp_pc.clif
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
test compile precise-output
|
||||||
|
set preserve_frame_pointers=true
|
||||||
|
target s390x
|
||||||
|
|
||||||
|
function %fp() -> i64 {
|
||||||
|
block0:
|
||||||
|
v0 = get_frame_pointer.i64
|
||||||
|
return v0
|
||||||
|
}
|
||||||
|
|
||||||
|
; stmg %r14, %r15, 112(%r15)
|
||||||
|
; lgr %r1, %r15
|
||||||
|
; aghi %r15, -160
|
||||||
|
; virtual_sp_offset_adjust 160
|
||||||
|
; stg %r1, 0(%r15)
|
||||||
|
; block0:
|
||||||
|
; lg %r2, 0(%r15)
|
||||||
|
; lmg %r14, %r15, 272(%r15)
|
||||||
|
; br %r14
|
||||||
|
|
||||||
|
function %sp() -> i64 {
|
||||||
|
block0:
|
||||||
|
v0 = get_stack_pointer.i64
|
||||||
|
return v0
|
||||||
|
}
|
||||||
|
|
||||||
|
; stmg %r14, %r15, 112(%r15)
|
||||||
|
; lgr %r1, %r15
|
||||||
|
; aghi %r15, -160
|
||||||
|
; virtual_sp_offset_adjust 160
|
||||||
|
; stg %r1, 0(%r15)
|
||||||
|
; block0:
|
||||||
|
; lgr %r2, %r15
|
||||||
|
; lmg %r14, %r15, 272(%r15)
|
||||||
|
; br %r14
|
||||||
|
|
||||||
|
function %return_address() -> i64 {
|
||||||
|
block0:
|
||||||
|
v0 = get_return_address.i64
|
||||||
|
return v0
|
||||||
|
}
|
||||||
|
|
||||||
|
; stmg %r14, %r15, 112(%r15)
|
||||||
|
; lgr %r1, %r15
|
||||||
|
; aghi %r15, -160
|
||||||
|
; virtual_sp_offset_adjust 160
|
||||||
|
; stg %r1, 0(%r15)
|
||||||
|
; block0:
|
||||||
|
; lg %r2, 272(%r15)
|
||||||
|
; lmg %r14, %r15, 272(%r15)
|
||||||
|
; br %r14
|
||||||
|
|
||||||
45
cranelift/filetests/filetests/isa/x64/fp_sp_pc.clif
Normal file
45
cranelift/filetests/filetests/isa/x64/fp_sp_pc.clif
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
test compile precise-output
|
||||||
|
set preserve_frame_pointers=true
|
||||||
|
target x86_64
|
||||||
|
|
||||||
|
function %fp() -> i64 {
|
||||||
|
block0:
|
||||||
|
v0 = get_frame_pointer.i64
|
||||||
|
return v0
|
||||||
|
}
|
||||||
|
|
||||||
|
; pushq %rbp
|
||||||
|
; movq %rsp, %rbp
|
||||||
|
; block0:
|
||||||
|
; movq %rbp, %rax
|
||||||
|
; movq %rbp, %rsp
|
||||||
|
; popq %rbp
|
||||||
|
; ret
|
||||||
|
|
||||||
|
function %sp() -> i64 {
|
||||||
|
block0:
|
||||||
|
v0 = get_stack_pointer.i64
|
||||||
|
return v0
|
||||||
|
}
|
||||||
|
|
||||||
|
; pushq %rbp
|
||||||
|
; movq %rsp, %rbp
|
||||||
|
; block0:
|
||||||
|
; movq %rsp, %rax
|
||||||
|
; movq %rbp, %rsp
|
||||||
|
; popq %rbp
|
||||||
|
; ret
|
||||||
|
|
||||||
|
function %return_address() -> i64 {
|
||||||
|
block0:
|
||||||
|
v0 = get_return_address.i64
|
||||||
|
return v0
|
||||||
|
}
|
||||||
|
|
||||||
|
; pushq %rbp
|
||||||
|
; movq %rsp, %rbp
|
||||||
|
; block0:
|
||||||
|
; movq 8(%rbp), %rax
|
||||||
|
; movq %rbp, %rsp
|
||||||
|
; popq %rbp
|
||||||
|
; ret
|
||||||
@@ -1032,6 +1032,9 @@ where
|
|||||||
Opcode::ExtractVector => {
|
Opcode::ExtractVector => {
|
||||||
unimplemented!("ExtractVector not supported");
|
unimplemented!("ExtractVector not supported");
|
||||||
}
|
}
|
||||||
|
Opcode::GetFramePointer => unimplemented!("GetFramePointer"),
|
||||||
|
Opcode::GetStackPointer => unimplemented!("GetStackPointer"),
|
||||||
|
Opcode::GetReturnAddress => unimplemented!("GetReturnAddress"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user