ISLE: Migrate call and return instructions (#3785)

This adds infrastructure to allow implementing call and return
instructions in ISLE, and migrates the s390x back-end.

To implement ABI details, this patch creates public accessors
for `ABISig` and makes them accessible in ISLE.  All actual
code generation is then done in ISLE rules, following the
information provided by that signature.

[ Note that the s390x back end never requires multiple slots for
a single argument - the infrastructure to handle this should
already be present, however. ]

To implement loops in ISLE rules, this patch uses regular tail
recursion, employing a `Range` data structure holding a range
of integers to be looped over.
This commit is contained in:
Ulrich Weigand
2022-06-29 23:22:50 +02:00
committed by GitHub
parent 688168b4d7
commit 7a9479f77c
9 changed files with 566 additions and 184 deletions

View File

@@ -69,7 +69,6 @@ use crate::machinst::*;
use crate::machinst::{RealReg, Reg, RegClass, Writable};
use crate::settings;
use crate::{CodegenError, CodegenResult};
use alloc::boxed::Box;
use alloc::vec::Vec;
use regalloc2::{PReg, PRegSet, VReg};
use smallvec::{smallvec, SmallVec};
@@ -80,9 +79,6 @@ use std::convert::TryFrom;
/// Support for the S390x ABI from the callee side (within a function body).
pub type S390xABICallee = ABICalleeImpl<S390xMachineDeps>;
/// Support for the S390x ABI from the caller side (at a callsite).
pub type S390xABICaller = ABICallerImpl<S390xMachineDeps>;
/// ABI Register usage
fn in_int_reg(ty: Type) -> bool {
@@ -616,57 +612,16 @@ impl ABIMachineSpec for S390xMachineDeps {
}
fn gen_call(
dest: &CallDest,
uses: SmallVec<[Reg; 8]>,
defs: SmallVec<[Writable<Reg>; 8]>,
clobbers: PRegSet,
opcode: ir::Opcode,
tmp: Writable<Reg>,
_dest: &CallDest,
_uses: SmallVec<[Reg; 8]>,
_defs: SmallVec<[Writable<Reg>; 8]>,
_clobbers: PRegSet,
_opcode: ir::Opcode,
_tmp: Writable<Reg>,
_callee_conv: isa::CallConv,
_caller_conv: isa::CallConv,
) -> SmallVec<[Inst; 2]> {
let mut insts = SmallVec::new();
match &dest {
&CallDest::ExtName(ref name, RelocDistance::Near) => insts.push(Inst::Call {
link: writable_gpr(14),
info: Box::new(CallInfo {
dest: name.clone(),
uses,
defs,
clobbers,
opcode,
}),
}),
&CallDest::ExtName(ref name, RelocDistance::Far) => {
insts.push(Inst::LoadExtNameFar {
rd: tmp,
name: Box::new(name.clone()),
offset: 0,
});
insts.push(Inst::CallInd {
link: writable_gpr(14),
info: Box::new(CallIndInfo {
rn: tmp.to_reg(),
uses,
defs,
clobbers,
opcode,
}),
});
}
&CallDest::Reg(reg) => insts.push(Inst::CallInd {
link: writable_gpr(14),
info: Box::new(CallIndInfo {
rn: *reg,
uses,
defs,
clobbers,
opcode,
}),
}),
}
insts
unreachable!();
}
fn gen_memcpy(

View File

@@ -1124,6 +1124,11 @@
(decl memarg_symbol (ExternalName i32 MemFlags) MemArg)
(extern constructor memarg_symbol memarg_symbol)
;; Create a MemArg refering to a stack address formed by
;; adding a base (relative to SP) and an offset.
(decl memarg_stack_off (i64 i64) MemArg)
(extern constructor memarg_stack_off memarg_stack_off)
;; 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)
@@ -1736,6 +1741,16 @@
(_ Unit (emit (MInst.LoadAddr dst mem))))
dst))
;; Helper for emitting `MInst.Call` instructions.
(decl call_impl (WritableReg BoxCallInfo) SideEffectNoResult)
(rule (call_impl reg info)
(SideEffectNoResult.Inst (MInst.Call reg info)))
;; Helper for emitting `MInst.CallInd` instructions.
(decl call_ind_impl (WritableReg BoxCallIndInfo) SideEffectNoResult)
(rule (call_ind_impl reg info)
(SideEffectNoResult.Inst (MInst.CallInd reg info)))
;; Helper for emitting `MInst.Jump` instructions.
(decl jump_impl (MachLabel) SideEffectNoResult)
(rule (jump_impl target)
@@ -1890,6 +1905,81 @@
(emit (MInst.Load64 dst addr)))
;; Helpers for accessing argument / return value slots ;;;;;;;;;;;;;;;;;;;;;;;;;
(decl emit_side_effect (SideEffectNoResult) Unit)
(rule (emit_side_effect (SideEffectNoResult.Inst inst)) (emit inst))
(decl emit_arg_store (Type Reg MemArg) Unit)
(rule (emit_arg_store $I8 reg mem) (emit_side_effect (store8 reg mem)))
(rule (emit_arg_store $I16 reg mem) (emit_side_effect (store16 reg mem)))
(rule (emit_arg_store $I32 reg mem) (emit_side_effect (store32 reg mem)))
(rule (emit_arg_store $I64 reg mem) (emit_side_effect (store64 reg mem)))
(rule (emit_arg_store $R64 reg mem) (emit_side_effect (store64 reg mem)))
(rule (emit_arg_store $F32 reg mem) (emit_side_effect (fpu_store32 reg mem)))
(rule (emit_arg_store $F64 reg mem) (emit_side_effect (fpu_store64 reg mem)))
(decl emit_arg_load (Type MemArg) Reg)
(rule (emit_arg_load $I8 mem) (zext32_mem $I8 mem))
(rule (emit_arg_load $I16 mem) (zext32_mem $I16 mem))
(rule (emit_arg_load $I32 mem) (load32 mem))
(rule (emit_arg_load $I64 mem) (load64 mem))
(rule (emit_arg_load $R64 mem) (load64 mem))
(rule (emit_arg_load $F32 mem) (fpu_load64 mem))
(rule (emit_arg_load $F64 mem) (fpu_load64 mem))
;; Copy a single argument/return value to its slots.
(decl copy_to_arg (i64 ABIArg Value) Unit)
(rule (copy_to_arg base (abi_arg_only_slot slot) val)
(copy_val_to_arg_slot base slot val))
;; Copy a single argument/return value from its slots.
(decl copy_from_arg (i64 ABIArg) ValueRegs)
(rule (copy_from_arg base (abi_arg_only_slot slot))
(value_reg (copy_reg_from_arg_slot base slot)))
;; Copy one component of an argument/return value to its slot.
(decl copy_val_to_arg_slot (i64 ABIArgSlot Value) Unit)
(rule (copy_val_to_arg_slot _ (ABIArgSlot.Reg reg ty (ArgumentExtension.None)) val)
(emit_mov ty (real_reg_to_writable_reg reg) val))
(rule (copy_val_to_arg_slot _ (ABIArgSlot.Reg reg _ (ArgumentExtension.Uext)) val)
(emit_put_in_reg_zext64 (real_reg_to_writable_reg reg) val))
(rule (copy_val_to_arg_slot _ (ABIArgSlot.Reg reg _ (ArgumentExtension.Sext)) val)
(emit_put_in_reg_sext64 (real_reg_to_writable_reg reg) val))
(rule (copy_val_to_arg_slot base (ABIArgSlot.Stack offset ty (ArgumentExtension.None)) val)
(emit_arg_store ty val (memarg_stack_off base offset)))
(rule (copy_val_to_arg_slot base (ABIArgSlot.Stack offset _ (ArgumentExtension.Uext)) val)
(emit_arg_store $I64 (put_in_reg_zext64 val) (memarg_stack_off base offset)))
(rule (copy_val_to_arg_slot base (ABIArgSlot.Stack offset _ (ArgumentExtension.Sext)) val)
(emit_arg_store $I64 (put_in_reg_sext64 val) (memarg_stack_off base offset)))
;; Copy one component of an argument/return value to its slot, where the
;; value is already extended and present in a register.
(decl copy_reg_to_arg_slot (i64 ABIArgSlot Reg) Unit)
(rule (copy_reg_to_arg_slot _ (ABIArgSlot.Reg reg ty ext) src)
(emit_mov (abi_ext_ty ext ty) (real_reg_to_writable_reg reg) src))
(rule (copy_reg_to_arg_slot base (ABIArgSlot.Stack offset ty ext) src)
(emit_arg_store (abi_ext_ty ext ty) src (memarg_stack_off base offset)))
;; Copy one component of an argument/return value from its slot.
(decl copy_reg_from_arg_slot (i64 ABIArgSlot) Reg)
(rule (copy_reg_from_arg_slot _ (ABIArgSlot.Reg reg ty ext))
(copy_reg (abi_ext_ty ext ty) (real_reg_to_reg reg)))
(rule (copy_reg_from_arg_slot base (ABIArgSlot.Stack offset ty ext))
(emit_arg_load (abi_ext_ty ext ty) (memarg_stack_off base offset)))
;; Helper to compute the type of an implicitly extended argument/return value.
(decl abi_ext_ty (ArgumentExtension Type) Type)
(rule (abi_ext_ty (ArgumentExtension.None) ty) ty)
(rule (abi_ext_ty (ArgumentExtension.Uext) _) $I64)
(rule (abi_ext_ty (ArgumentExtension.Sext) _) $I64)
;; Copy a return value to a set of registers.
(decl copy_to_regs (WritableValueRegs Value) Unit)
(rule (copy_to_regs (only_writable_reg reg) val @ (value_type ty))
(emit_mov ty reg val))
;; Helpers for generating immediate values ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Move immediate value into destination register. (Non-SSA form.)
@@ -2512,6 +2602,32 @@
(casloop_emit ib ty flags aligned_addr val)))
;; Helpers for generating `call` instructions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(decl abi_sig (SigRef) ABISig)
(extern constructor abi_sig abi_sig)
(decl abi_call_info (ABISig ExternalName Opcode) BoxCallInfo)
(extern constructor abi_call_info abi_call_info)
(decl abi_call_ind_info (ABISig Reg Opcode) BoxCallIndInfo)
(extern constructor abi_call_ind_info abi_call_ind_info)
(decl writable_link_reg () WritableReg)
(rule (writable_link_reg) (writable_gpr 14))
(decl abi_call (ABISig ExternalName Opcode) SideEffectNoResult)
(rule (abi_call abi name opcode)
(call_impl (writable_link_reg) (abi_call_info abi name opcode)))
(decl abi_call_ind (ABISig Reg Opcode) SideEffectNoResult)
(rule (abi_call_ind abi target opcode)
(call_ind_impl (writable_link_reg) (abi_call_ind_info abi target opcode)))
(decl abi_accumulate_outgoing_args_size (ABISig) Unit)
(extern constructor abi_accumulate_outgoing_args_size abi_accumulate_outgoing_args_size)
;; Helpers for generating `clz` instructions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Count leading zeroes. For a zero input, return the specified value.

View File

@@ -2215,3 +2215,71 @@
(side_effect (trap_if_impl (mask_as_cond 3) trap_code)))
;;;; Rules for `return` and `fallthrough_return` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(rule (lower (return args))
(lower_return (range 0 (value_slice_len args)) args))
(rule (lower (fallthrough_return args))
(lower_return (range 0 (value_slice_len args)) args))
(decl lower_return (Range ValueSlice) InstOutput)
(rule (lower_return (range_empty) _) (output_none))
(rule (lower_return (range_unwrap head tail) args)
(let ((_ Unit (copy_to_regs (retval head) (value_slice_get args head))))
(lower_return tail args)))
;;;; Rules for `call` and `call_indirect` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Direct call to an in-range function.
(rule (lower (call (func_ref_data sig_ref name (reloc_distance_near)) args))
(let ((abi ABISig (abi_sig sig_ref))
(_1 Unit (abi_accumulate_outgoing_args_size abi))
(_2 InstOutput (lower_call_args abi (range 0 (abi_num_args abi)) args))
(_3 InstOutput (side_effect (abi_call abi name (Opcode.Call)))))
(lower_call_rets abi (range 0 (abi_num_rets abi)) (output_builder_new))))
;; Direct call to an out-of-range function (implicitly via pointer).
(rule (lower (call (func_ref_data sig_ref name _) args))
(let ((abi ABISig (abi_sig sig_ref))
(_1 Unit (abi_accumulate_outgoing_args_size abi))
(_2 InstOutput (lower_call_args abi (range 0 (abi_num_args abi)) args))
(target Reg (load_ext_name_far name 0))
(_3 InstOutput (side_effect (abi_call_ind abi target (Opcode.Call)))))
(lower_call_rets abi (range 0 (abi_num_rets abi)) (output_builder_new))))
;; Indirect call.
(rule (lower (call_indirect sig_ref ptr args))
(let ((abi ABISig (abi_sig sig_ref))
(target Reg (put_in_reg ptr))
(_1 Unit (abi_accumulate_outgoing_args_size abi))
(_2 InstOutput (lower_call_args abi (range 0 (abi_num_args abi)) args))
(_3 InstOutput (side_effect (abi_call_ind abi target (Opcode.CallIndirect)))))
(lower_call_rets abi (range 0 (abi_num_rets abi)) (output_builder_new))))
;; Lower function arguments by loading them into registers / stack slots.
(decl lower_call_args (ABISig Range ValueSlice) InstOutput)
(rule (lower_call_args abi (range_empty) _) (lower_call_ret_arg abi))
(rule (lower_call_args abi (range_unwrap head tail) args)
(let ((idx usize (abi_copy_to_arg_order abi head))
(_ Unit (copy_to_arg 0 (abi_get_arg abi idx)
(value_slice_get args idx))))
(lower_call_args abi tail args)))
;; Lower the implicit return-area pointer argument, if present.
(decl lower_call_ret_arg (ABISig) InstOutput)
(rule (lower_call_ret_arg (abi_no_ret_arg)) (output_none))
(rule (lower_call_ret_arg abi @ (abi_ret_arg (abi_arg_only_slot slot)))
(let ((ret_arg Reg (load_addr (memarg_stack_off (abi_stack_arg_space abi) 0)))
(_ Unit (copy_reg_to_arg_slot 0 slot ret_arg)))
(output_none)))
;; Lower function return values by collecting them from registers / stack slots.
(decl lower_call_rets (ABISig Range InstOutputBuilder) InstOutput)
(rule (lower_call_rets abi (range_empty) builder) (output_builder_finish builder))
(rule (lower_call_rets abi (range_unwrap head tail) builder)
(let ((ret ValueRegs (copy_from_arg (abi_stack_arg_space abi) (abi_get_ret abi head)))
(_ Unit (output_builder_push builder ret)))
(lower_call_rets abi tail builder)))

View File

@@ -28,13 +28,6 @@ fn sign_extend_to_u64(value: u64, from_bits: u8) -> u64 {
}
}
/// Lower an instruction input to a reg.
fn put_input_in_reg<C: LowerCtx<I = Inst>>(ctx: &mut C, input: InsnInput) -> Reg {
ctx.put_input_in_regs(input.insn, input.input)
.only_reg()
.unwrap()
}
//=============================================================================
// Lowering: comparisons
@@ -69,9 +62,6 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
isa_flags: &s390x_settings::Flags,
) -> CodegenResult<()> {
let op = ctx.data(insn).opcode();
let inputs: SmallVec<[InsnInput; 4]> = (0..ctx.num_inputs(insn))
.map(|i| InsnInput { insn, input: i })
.collect();
let outputs: SmallVec<[InsnOutput; 2]> = (0..ctx.num_outputs(insn))
.map(|i| InsnOutput { insn, output: i })
.collect();
@@ -190,6 +180,10 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
| Opcode::ResumableTrapnz
| Opcode::Trapif
| Opcode::Debugtrap
| Opcode::Call
| Opcode::CallIndirect
| Opcode::FallthroughReturn
| Opcode::Return
| Opcode::StackAddr
| Opcode::FuncAddr
| Opcode::SymbolValue => implemented_in_isle(),
@@ -227,56 +221,6 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
unimplemented!("Pinned register support not implemented!");
}
Opcode::Call | Opcode::CallIndirect => {
let caller_conv = ctx.abi().call_conv();
let (mut abi, inputs) = match op {
Opcode::Call => {
let (extname, dist) = ctx.call_target(insn).unwrap();
let extname = extname.clone();
let sig = ctx.call_sig(insn).unwrap();
assert!(inputs.len() == sig.params.len());
assert!(outputs.len() == sig.returns.len());
(
S390xABICaller::from_func(sig, &extname, dist, caller_conv, flags)?,
&inputs[..],
)
}
Opcode::CallIndirect => {
let ptr = put_input_in_reg(ctx, inputs[0]);
let sig = ctx.call_sig(insn).unwrap();
assert!(inputs.len() - 1 == sig.params.len());
assert!(outputs.len() == sig.returns.len());
(
S390xABICaller::from_ptr(sig, ptr, op, caller_conv, flags)?,
&inputs[1..],
)
}
_ => unreachable!(),
};
assert!(inputs.len() == abi.num_args());
for (i, input) in inputs.iter().enumerate() {
let arg_reg = put_input_in_reg(ctx, *input);
abi.emit_copy_regs_to_arg(ctx, i, ValueRegs::one(arg_reg));
}
abi.emit_call(ctx);
for (i, output) in outputs.iter().enumerate() {
let retval_reg = get_output_reg(ctx, *output).only_reg().unwrap();
abi.emit_copy_retval_to_regs(ctx, i, ValueRegs::one(retval_reg));
}
abi.accumulate_outgoing_args_size(ctx);
}
Opcode::FallthroughReturn | Opcode::Return => {
for (i, input) in inputs.iter().enumerate() {
let reg = put_input_in_reg(ctx, *input);
let retval_reg = ctx.retval(i).only_reg().unwrap();
let ty = ctx.input_ty(insn, i);
ctx.emit(Inst::gen_move(retval_reg, reg, ty));
}
// N.B.: the Ret itself is generated by the ABI.
}
Opcode::RawBitcast
| Opcode::Splat
| Opcode::Swizzle

View File

@@ -6,7 +6,7 @@ pub mod generated_code;
// Types that the generated ISLE code uses via `use super::*`.
use super::{
CallIndInfo, CallInfo, Cond, Inst as MInst, MachLabel, MemArg, MemFlags, Opcode, Reg,
UImm16Shifted, UImm32Shifted,
S390xMachineDeps, UImm16Shifted, UImm32Shifted,
};
use crate::isa::s390x::settings::Flags as IsaFlags;
use crate::machinst::isle::*;
@@ -69,6 +69,40 @@ where
{
isle_prelude_methods!();
fn abi_sig(&mut self, sig_ref: SigRef) -> ABISig {
let sig = &self.lower_ctx.dfg().signatures[sig_ref];
ABISig::from_func_sig::<S390xMachineDeps>(sig, self.flags).unwrap()
}
fn abi_accumulate_outgoing_args_size(&mut self, abi: &ABISig) -> Unit {
let off = abi.stack_arg_space() + abi.stack_ret_space();
self.lower_ctx
.abi()
.accumulate_outgoing_args_size(off as u32);
}
fn abi_call_info(&mut self, abi: &ABISig, name: ExternalName, opcode: &Opcode) -> BoxCallInfo {
let (uses, defs, clobbers) = abi.call_uses_defs_clobbers::<S390xMachineDeps>();
Box::new(CallInfo {
dest: name.clone(),
uses,
defs,
clobbers,
opcode: *opcode,
})
}
fn abi_call_ind_info(&mut self, abi: &ABISig, target: Reg, opcode: &Opcode) -> BoxCallIndInfo {
let (uses, defs, clobbers) = abi.call_uses_defs_clobbers::<S390xMachineDeps>();
Box::new(CallIndInfo {
rn: target,
uses,
defs,
clobbers,
opcode: *opcode,
})
}
#[inline]
fn allow_div_traps(&mut self, _: Type) -> Option<()> {
if !self.flags.avoid_div_traps() {
@@ -432,6 +466,11 @@ where
MemArg::reg_plus_off(reg, off, flags)
}
#[inline]
fn memarg_stack_off(&mut self, base: i64, off: i64) -> MemArg {
MemArg::reg_plus_off(super::stack_reg(), base + off, MemFlags::trusted())
}
#[inline]
fn memarg_symbol(&mut self, name: ExternalName, offset: i32, flags: MemFlags) -> MemArg {
MemArg::Symbol {