Skip to content

Commit

Permalink
Cranelift: Add instructions for getting the current stack/frame/retur…
Browse files Browse the repository at this point in the history
…n pointers (#4573)

* Cranelift: Add instructions for getting the current stack/frame pointers and return address

This is the initial part of #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`
  • Loading branch information
fitzgen authored Aug 2, 2022
1 parent 6b4e652 commit 42bba45
Show file tree
Hide file tree
Showing 28 changed files with 484 additions and 24 deletions.
37 changes: 37 additions & 0 deletions cranelift/codegen/meta/src/shared/instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1296,6 +1296,43 @@ pub(crate) fn define(
.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(
"TableOffset",
"An unsigned table offset",
Expand Down
34 changes: 34 additions & 0 deletions cranelift/codegen/src/isa/aarch64/inst.isle
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,12 @@
(rd WritableReg)
(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.
(MovWide
(op MoveWideOp)
Expand Down Expand Up @@ -2421,3 +2427,31 @@
;; And finally, copy the preordained AtomicCASLoop output reg to its destination.
;; Also, x24 and x28 are trashed.
(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)))
9 changes: 9 additions & 0 deletions cranelift/codegen/src/isa/aarch64/inst/emit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 } => {
let rd = allocs.next_writable(rd);
sink.put4(enc_move_wide(op, rd, imm, size));
Expand Down
12 changes: 12 additions & 0 deletions cranelift/codegen/src/isa/aarch64/inst/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,13 @@ fn aarch64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut Operan
collector.reg_def(rd);
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 {
MoveWideOp::MovK => collector.reg_mod(rd),
_ => collector.reg_def(rd),
Expand Down Expand Up @@ -1474,6 +1481,11 @@ impl Inst {
let rm = pretty_print_ireg(rm, size, allocs);
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 {
op,
rd,
Expand Down
12 changes: 12 additions & 0 deletions cranelift/codegen/src/isa/aarch64/lower.isle
Original file line number Diff line number Diff line change
Expand Up @@ -1697,6 +1697,7 @@


;;;; Rules for `uunarrow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(rule (lower (has_type (ty_vec128_int ty) (uunarrow x y)))
(if (zero_value y))
(uqxtn x (lane_size ty)))
Expand Down Expand Up @@ -1733,3 +1734,14 @@

(rule (lower (debugtrap))
(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))
13 changes: 13 additions & 0 deletions cranelift/codegen/src/isa/aarch64/lower/isle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use crate::{
isa::unwind::UnwindInst,
machinst::{ty_bits, InsnOutput, LowerCtx, VCodeConstant, VCodeConstantData},
};
use regalloc2::PReg;
use std::boxed::Box;
use std::convert::TryFrom;
use std::vec::Vec;
Expand Down Expand Up @@ -466,4 +467,16 @@ where

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()
}
}
4 changes: 4 additions & 0 deletions cranelift/codegen/src/isa/aarch64/lower_inst.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
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::Isub => implemented_in_isle(ctx),
Opcode::UaddSat | Opcode::SaddSat | Opcode::UsubSat | Opcode::SsubSat => {
Expand Down
23 changes: 23 additions & 0 deletions cranelift/codegen/src/isa/s390x/inst.isle
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,11 @@
(rd WritableReg)
(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.
(Mov32Imm
(rd WritableReg)
Expand Down Expand Up @@ -1556,6 +1561,10 @@
(decl memarg_stack_off (i64 i64) MemArg)
(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
;; a valid `MemArg::Symbol` offset (i.e. is even and fits into i32).
(decl pure memarg_symbol_offset_sum (i64 i64) i32)
Expand Down Expand Up @@ -2469,6 +2478,20 @@
(rule (emit_load $I64 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 ;;;;;;;;;;;;;;;;;;;;;;;;;

Expand Down
6 changes: 6 additions & 0 deletions cranelift/codegen/src/isa/s390x/inst/emit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2081,6 +2081,12 @@ impl MachInstEmit for Inst {
let opcode = 0xb904; // LGR
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 } => {
let rd = allocs.next_writable(rd);
let rm = allocs.next(rm);
Expand Down
11 changes: 11 additions & 0 deletions cranelift/codegen/src/isa/s390x/inst/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ impl Inst {
| Inst::StoreMultiple64 { .. }
| Inst::Mov32 { .. }
| Inst::Mov64 { .. }
| Inst::MovPReg { .. }
| Inst::Mov32Imm { .. }
| Inst::Mov32SImm16 { .. }
| Inst::Mov64SImm16 { .. }
Expand Down Expand Up @@ -623,6 +624,11 @@ fn s390x_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandC
collector.reg_def(rd);
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 } => {
collector.reg_def(rd);
collector.reg_use(rm);
Expand Down Expand Up @@ -1778,6 +1784,11 @@ impl Inst {
let rm = pretty_print_reg(rm, allocs);
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 } => {
let rd = pretty_print_reg(rd.to_reg(), allocs);
let rm = pretty_print_reg(rm, allocs);
Expand Down
12 changes: 12 additions & 0 deletions cranelift/codegen/src/isa/s390x/lower.isle
Original file line number Diff line number Diff line change
Expand Up @@ -3616,3 +3616,15 @@
(_ Unit (output_builder_push builder ret)))
(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)))
5 changes: 4 additions & 1 deletion cranelift/codegen/src/isa/s390x/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,10 @@ impl LowerBackend for S390xBackend {
| Opcode::Return
| Opcode::StackAddr
| Opcode::FuncAddr
| Opcode::SymbolValue => {
| Opcode::SymbolValue
| Opcode::GetFramePointer
| Opcode::GetStackPointer
| Opcode::GetReturnAddress => {
unreachable!(
"implemented in ISLE: inst = `{}`, type = `{:?}`",
ctx.dfg().display_inst(ir_inst),
Expand Down
11 changes: 11 additions & 0 deletions cranelift/codegen/src/isa/s390x/lower/isle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use crate::{
isa::unwind::UnwindInst,
machinst::{InsnOutput, LowerCtx, VCodeConstant, VCodeConstantData},
};
use regalloc2::PReg;
use std::boxed::Box;
use std::cell::Cell;
use std::convert::TryFrom;
Expand Down Expand Up @@ -603,6 +604,11 @@ where
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]
fn memarg_symbol(&mut self, name: ExternalName, offset: i32, flags: MemFlags) -> MemArg {
MemArg::Symbol {
Expand Down Expand Up @@ -670,6 +676,11 @@ where
fn emit(&mut self, inst: &MInst) -> Unit {
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.
Expand Down
Loading

0 comments on commit 42bba45

Please sign in to comment.