[s390x, abi_impl] Add i128 support (#4598)
This adds full i128 support to the s390x target, including new filetests and enabling the existing i128 runtest on s390x. The ABI requires that i128 is passed and returned via implicit pointer, but the front end still generates direct i128 types in call. This means we have to implement ABI support to implicitly convert i128 types to pointers when passing arguments. To do so, we add a new variant ABIArg::ImplicitArg. This acts like StructArg, except that the value type is the actual target type, not a pointer type. The required conversions have to be inserted in the prologue and at function call sites. Note that when dereferencing the implicit pointer in the prologue, we may require a temp register: the pointer may be passed on the stack so it needs to be loaded first, but the value register may be in the wrong class for pointer values. In this case, we use the "stack limit" register, which should be available at this point in the prologue. For return values, we use a mechanism similar to the one used for supporting multiple return values in the Wasmtime ABI. The only difference is that the hidden pointer to the return buffer must be the *first*, not last, argument in this case. (This implements the second half of issue #4565.)
This commit is contained in:
@@ -17,14 +17,14 @@ pub trait ABICallee {
|
||||
/// The instruction type for the ISA associated with this ABI.
|
||||
type I: VCodeInst;
|
||||
|
||||
/// Does the ABI-body code need a temp reg (and if so, of what type)? One
|
||||
/// will be provided to `init()` as the `maybe_tmp` arg if so.
|
||||
fn temp_needed(&self) -> Option<Type>;
|
||||
/// Does the ABI-body code need temp registers (and if so, of what type)?
|
||||
/// They will be provided to `init()` as the `temps` arg if so.
|
||||
fn temps_needed(&self) -> Vec<Type>;
|
||||
|
||||
/// Initialize. This is called after the ABICallee is constructed because it
|
||||
/// may be provided with a temp vreg, which can only be allocated once the
|
||||
/// lowering context exists.
|
||||
fn init(&mut self, maybe_tmp: Option<Writable<Reg>>);
|
||||
/// may be provided with a vector of temp vregs, which can only be allocated
|
||||
/// once the lowering context exists.
|
||||
fn init(&mut self, temps: Vec<Writable<Reg>>);
|
||||
|
||||
/// Access the (possibly legalized) signature.
|
||||
fn signature(&self) -> &Signature;
|
||||
|
||||
@@ -196,6 +196,19 @@ pub enum ABIArg {
|
||||
/// Purpose of this arg.
|
||||
purpose: ir::ArgumentPurpose,
|
||||
},
|
||||
/// Implicit argument. Similar to a StructArg, except that we have the
|
||||
/// target type, not a pointer type, at the CLIF-level. This argument is
|
||||
/// still being passed via reference implicitly.
|
||||
ImplicitPtrArg {
|
||||
/// Register or stack slot holding a pointer to the buffer.
|
||||
pointer: ABIArgSlot,
|
||||
/// Offset of the argument buffer.
|
||||
offset: i64,
|
||||
/// Type of the implicit argument.
|
||||
ty: Type,
|
||||
/// Purpose of this arg.
|
||||
purpose: ir::ArgumentPurpose,
|
||||
},
|
||||
}
|
||||
|
||||
impl ABIArg {
|
||||
@@ -604,6 +617,12 @@ impl ABISig {
|
||||
}
|
||||
}
|
||||
}
|
||||
&ABIArg::ImplicitPtrArg { ref pointer, .. } => match pointer {
|
||||
&ABIArgSlot::Reg { reg, .. } => {
|
||||
uses.push(Reg::from(reg));
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -707,6 +726,8 @@ pub struct ABICalleeImpl<M: ABIMachineSpec> {
|
||||
total_frame_size: Option<u32>,
|
||||
/// The register holding the return-area pointer, if needed.
|
||||
ret_area_ptr: Option<Writable<Reg>>,
|
||||
/// Temp registers required for argument setup, if needed.
|
||||
arg_temp_reg: Vec<Option<Writable<Reg>>>,
|
||||
/// Calling convention this function expects.
|
||||
call_conv: isa::CallConv,
|
||||
/// The settings controlling this function's compilation.
|
||||
@@ -845,6 +866,7 @@ impl<M: ABIMachineSpec> ABICalleeImpl<M> {
|
||||
fixed_frame_storage_size: 0,
|
||||
total_frame_size: None,
|
||||
ret_area_ptr: None,
|
||||
arg_temp_reg: vec![],
|
||||
call_conv,
|
||||
flags,
|
||||
isa_flags: isa_flags.clone(),
|
||||
@@ -1033,18 +1055,39 @@ impl<M: ABIMachineSpec> ABICallee for ABICalleeImpl<M> {
|
||||
&self.ir_sig
|
||||
}
|
||||
|
||||
fn temp_needed(&self) -> Option<Type> {
|
||||
if self.sig.stack_ret_arg.is_some() {
|
||||
Some(M::word_type())
|
||||
} else {
|
||||
None
|
||||
fn temps_needed(&self) -> Vec<Type> {
|
||||
let mut temp_tys = vec![];
|
||||
for arg in &self.sig.args {
|
||||
match arg {
|
||||
&ABIArg::ImplicitPtrArg { pointer, .. } => match &pointer {
|
||||
&ABIArgSlot::Reg { .. } => {}
|
||||
&ABIArgSlot::Stack { ty, .. } => {
|
||||
temp_tys.push(ty);
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if self.sig.stack_ret_arg.is_some() {
|
||||
temp_tys.push(M::word_type());
|
||||
}
|
||||
temp_tys
|
||||
}
|
||||
|
||||
fn init(&mut self, maybe_tmp: Option<Writable<Reg>>) {
|
||||
fn init(&mut self, temps: Vec<Writable<Reg>>) {
|
||||
let mut temps_iter = temps.into_iter();
|
||||
for arg in &self.sig.args {
|
||||
let temp = match arg {
|
||||
&ABIArg::ImplicitPtrArg { pointer, .. } => match &pointer {
|
||||
&ABIArgSlot::Reg { .. } => None,
|
||||
&ABIArgSlot::Stack { .. } => Some(temps_iter.next().unwrap()),
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
self.arg_temp_reg.push(temp);
|
||||
}
|
||||
if self.sig.stack_ret_arg.is_some() {
|
||||
assert!(maybe_tmp.is_some());
|
||||
self.ret_area_ptr = maybe_tmp;
|
||||
self.ret_area_ptr = Some(temps_iter.next().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1150,6 +1193,28 @@ impl<M: ABIMachineSpec> ABICallee for ABICalleeImpl<M> {
|
||||
));
|
||||
}
|
||||
}
|
||||
&ABIArg::ImplicitPtrArg { pointer, ty, .. } => {
|
||||
let into_reg = into_regs.only_reg().unwrap();
|
||||
// We need to dereference the pointer.
|
||||
let base = match &pointer {
|
||||
&ABIArgSlot::Reg { reg, .. } => Reg::from(reg),
|
||||
&ABIArgSlot::Stack { offset, ty, .. } => {
|
||||
// In this case we need a temp register to hold the address.
|
||||
// This was allocated in the `init` routine.
|
||||
let addr_reg = self.arg_temp_reg[idx].unwrap();
|
||||
insts.push(M::gen_load_stack(
|
||||
StackAMode::FPOffset(
|
||||
M::fp_to_arg_offset(self.call_conv, &self.flags) + offset,
|
||||
ty,
|
||||
),
|
||||
addr_reg,
|
||||
ty,
|
||||
));
|
||||
addr_reg.to_reg()
|
||||
}
|
||||
};
|
||||
insts.push(M::gen_load_base_offset(into_reg, base, 0, ty));
|
||||
}
|
||||
}
|
||||
insts
|
||||
}
|
||||
@@ -1241,6 +1306,9 @@ impl<M: ABIMachineSpec> ABICallee for ABICalleeImpl<M> {
|
||||
&ABIArg::StructArg { .. } => {
|
||||
panic!("StructArg in return position is unsupported");
|
||||
}
|
||||
&ABIArg::ImplicitPtrArg { .. } => {
|
||||
panic!("ImplicitPtrArg in return position is unsupported");
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
@@ -1690,6 +1758,7 @@ impl<M: ABIMachineSpec> ABICaller for ABICallerImpl<M> {
|
||||
ctx.emit(insn);
|
||||
}
|
||||
}
|
||||
&ABIArg::ImplicitPtrArg { .. } => unimplemented!(), // Only supported via ISLE.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1772,6 +1841,7 @@ impl<M: ABIMachineSpec> ABICaller for ABICallerImpl<M> {
|
||||
&ABIArg::StructArg { pointer, .. } => {
|
||||
assert!(pointer.is_none()); // Only supported via ISLE.
|
||||
}
|
||||
&ABIArg::ImplicitPtrArg { .. } => unimplemented!(), // Only supported via ISLE.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1805,6 +1875,9 @@ impl<M: ABIMachineSpec> ABICaller for ABICallerImpl<M> {
|
||||
&ABIArg::StructArg { .. } => {
|
||||
panic!("StructArg not supported in return position");
|
||||
}
|
||||
&ABIArg::ImplicitPtrArg { .. } => {
|
||||
panic!("ImplicitPtrArg not supported in return position");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -847,6 +847,18 @@ macro_rules! isle_prelude_methods {
|
||||
}
|
||||
}
|
||||
|
||||
fn abi_arg_implicit_pointer(&mut self, arg: &ABIArg) -> Option<(ABIArgSlot, i64, Type)> {
|
||||
match arg {
|
||||
&ABIArg::ImplicitPtrArg {
|
||||
pointer,
|
||||
offset,
|
||||
ty,
|
||||
..
|
||||
} => Some((pointer, offset, ty)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn abi_stackslot_addr(
|
||||
&mut self,
|
||||
dst: WritableReg,
|
||||
|
||||
@@ -1051,13 +1051,15 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
|
||||
pub fn lower<B: LowerBackend<MInst = I>>(mut self, backend: &B) -> CodegenResult<VCode<I>> {
|
||||
trace!("about to lower function: {:?}", self.f);
|
||||
|
||||
// Initialize the ABI object, giving it a temp if requested.
|
||||
let maybe_tmp = if let Some(temp_ty) = self.vcode.abi().temp_needed() {
|
||||
Some(self.alloc_tmp(temp_ty).only_reg().unwrap())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.vcode.abi().init(maybe_tmp);
|
||||
// Initialize the ABI object, giving it temps if requested.
|
||||
let temps = self
|
||||
.vcode
|
||||
.abi()
|
||||
.temps_needed()
|
||||
.into_iter()
|
||||
.map(|temp_ty| self.alloc_tmp(temp_ty).only_reg().unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
self.vcode.abi().init(temps);
|
||||
|
||||
// Get the pinned reg here (we only parameterize this function on `B`,
|
||||
// not the whole `Lower` impl).
|
||||
|
||||
Reference in New Issue
Block a user