Implement SystemV struct argument passing
This commit is contained in:
@@ -407,6 +407,8 @@ impl fmt::Debug for VectorType {
|
|||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub(crate) enum SpecialType {
|
pub(crate) enum SpecialType {
|
||||||
Flag(shared_types::Flag),
|
Flag(shared_types::Flag),
|
||||||
|
// FIXME remove once the old style backends are removed.
|
||||||
|
StructArgument,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpecialType {
|
impl SpecialType {
|
||||||
@@ -421,6 +423,9 @@ impl SpecialType {
|
|||||||
"CPU flags representing the result of a floating point comparison. These
|
"CPU flags representing the result of a floating point comparison. These
|
||||||
flags can be tested with a :type:`floatcc` condition code.",
|
flags can be tested with a :type:`floatcc` condition code.",
|
||||||
),
|
),
|
||||||
|
SpecialType::StructArgument => {
|
||||||
|
String::from("After legalization sarg__ arguments will get this type.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,6 +433,7 @@ impl SpecialType {
|
|||||||
pub fn lane_bits(self) -> u64 {
|
pub fn lane_bits(self) -> u64 {
|
||||||
match self {
|
match self {
|
||||||
SpecialType::Flag(_) => 0,
|
SpecialType::Flag(_) => 0,
|
||||||
|
SpecialType::StructArgument => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,6 +442,7 @@ impl SpecialType {
|
|||||||
match self {
|
match self {
|
||||||
SpecialType::Flag(shared_types::Flag::IFlags) => 1,
|
SpecialType::Flag(shared_types::Flag::IFlags) => 1,
|
||||||
SpecialType::Flag(shared_types::Flag::FFlags) => 2,
|
SpecialType::Flag(shared_types::Flag::FFlags) => 2,
|
||||||
|
SpecialType::StructArgument => 3,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -445,6 +452,7 @@ impl fmt::Display for SpecialType {
|
|||||||
match *self {
|
match *self {
|
||||||
SpecialType::Flag(shared_types::Flag::IFlags) => write!(f, "iflags"),
|
SpecialType::Flag(shared_types::Flag::IFlags) => write!(f, "iflags"),
|
||||||
SpecialType::Flag(shared_types::Flag::FFlags) => write!(f, "fflags"),
|
SpecialType::Flag(shared_types::Flag::FFlags) => write!(f, "fflags"),
|
||||||
|
SpecialType::StructArgument => write!(f, "sarg__"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -456,6 +464,7 @@ impl fmt::Debug for SpecialType {
|
|||||||
"{}",
|
"{}",
|
||||||
match *self {
|
match *self {
|
||||||
SpecialType::Flag(_) => format!("FlagsType({})", self),
|
SpecialType::Flag(_) => format!("FlagsType({})", self),
|
||||||
|
SpecialType::StructArgument => format!("StructArgument"),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -469,12 +478,14 @@ impl From<shared_types::Flag> for SpecialType {
|
|||||||
|
|
||||||
pub(crate) struct SpecialTypeIterator {
|
pub(crate) struct SpecialTypeIterator {
|
||||||
flag_iter: shared_types::FlagIterator,
|
flag_iter: shared_types::FlagIterator,
|
||||||
|
done: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpecialTypeIterator {
|
impl SpecialTypeIterator {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
flag_iter: shared_types::FlagIterator::new(),
|
flag_iter: shared_types::FlagIterator::new(),
|
||||||
|
done: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -484,11 +495,16 @@ impl Iterator for SpecialTypeIterator {
|
|||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if let Some(f) = self.flag_iter.next() {
|
if let Some(f) = self.flag_iter.next() {
|
||||||
Some(SpecialType::from(f))
|
Some(SpecialType::from(f))
|
||||||
|
} else {
|
||||||
|
if !self.done {
|
||||||
|
self.done = true;
|
||||||
|
Some(SpecialType::StructArgument)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Reference type is scalar type, but not lane type.
|
/// Reference type is scalar type, but not lane type.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
|||||||
@@ -465,6 +465,7 @@ fn define_moves(e: &mut PerCpuModeEncodings, shared_defs: &SharedDefinitions, r:
|
|||||||
let sextend = shared.by_name("sextend");
|
let sextend = shared.by_name("sextend");
|
||||||
let set_pinned_reg = shared.by_name("set_pinned_reg");
|
let set_pinned_reg = shared.by_name("set_pinned_reg");
|
||||||
let uextend = shared.by_name("uextend");
|
let uextend = shared.by_name("uextend");
|
||||||
|
let dummy_sarg__ = shared.by_name("dummy_sarg__");
|
||||||
|
|
||||||
// Shorthands for recipes.
|
// Shorthands for recipes.
|
||||||
let rec_copysp = r.template("copysp");
|
let rec_copysp = r.template("copysp");
|
||||||
@@ -482,6 +483,7 @@ fn define_moves(e: &mut PerCpuModeEncodings, shared_defs: &SharedDefinitions, r:
|
|||||||
let rec_umr_reg_to_ssa = r.template("umr_reg_to_ssa");
|
let rec_umr_reg_to_ssa = r.template("umr_reg_to_ssa");
|
||||||
let rec_urm_noflags = r.template("urm_noflags");
|
let rec_urm_noflags = r.template("urm_noflags");
|
||||||
let rec_urm_noflags_abcd = r.template("urm_noflags_abcd");
|
let rec_urm_noflags_abcd = r.template("urm_noflags_abcd");
|
||||||
|
let rec_dummy_sarg__ = r.recipe("dummy_sarg__");
|
||||||
|
|
||||||
// The pinned reg is fixed to a certain value entirely user-controlled, so it generates nothing!
|
// The pinned reg is fixed to a certain value entirely user-controlled, so it generates nothing!
|
||||||
e.enc64_rec(get_pinned_reg.bind(I64), rec_get_pinned_reg, 0);
|
e.enc64_rec(get_pinned_reg.bind(I64), rec_get_pinned_reg, 0);
|
||||||
@@ -747,6 +749,8 @@ fn define_moves(e: &mut PerCpuModeEncodings, shared_defs: &SharedDefinitions, r:
|
|||||||
copy_to_ssa.bind(F32),
|
copy_to_ssa.bind(F32),
|
||||||
rec_furm_reg_to_ssa.opcodes(&MOVSS_LOAD),
|
rec_furm_reg_to_ssa.opcodes(&MOVSS_LOAD),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
e.enc_32_64_rec(dummy_sarg__, rec_dummy_sarg__, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
|
|||||||
@@ -1270,6 +1270,12 @@ pub(crate) fn define<'shared>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
recipes.add_recipe(
|
||||||
|
EncodingRecipeBuilder::new("dummy_sarg__", &formats.nullary, 0)
|
||||||
|
.operands_out(vec![Stack::new(gpr)])
|
||||||
|
.emit(""),
|
||||||
|
);
|
||||||
|
|
||||||
// XX+rd id with Abs4 function relocation.
|
// XX+rd id with Abs4 function relocation.
|
||||||
recipes.add_template_recipe(
|
recipes.add_template_recipe(
|
||||||
EncodingRecipeBuilder::new("fnaddr4", &formats.func_addr, 4)
|
EncodingRecipeBuilder::new("fnaddr4", &formats.func_addr, 4)
|
||||||
|
|||||||
@@ -1863,6 +1863,31 @@ pub(crate) fn define(
|
|||||||
.can_load(true),
|
.can_load(true),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let Sarg = &TypeVar::new(
|
||||||
|
"Sarg",
|
||||||
|
"Any scalar or vector type with as most 128 lanes",
|
||||||
|
TypeSetBuilder::new()
|
||||||
|
.specials(vec![crate::cdsl::types::SpecialType::StructArgument])
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
let sarg__ = &Operand::new("sarg__", Sarg);
|
||||||
|
|
||||||
|
// FIXME remove once the old style codegen backends are removed.
|
||||||
|
ig.push(
|
||||||
|
Inst::new(
|
||||||
|
"dummy_sarg__",
|
||||||
|
r#"
|
||||||
|
This creates a sarg__
|
||||||
|
|
||||||
|
This instruction is internal and should not be created by
|
||||||
|
Cranelift users.
|
||||||
|
"#,
|
||||||
|
&formats.nullary,
|
||||||
|
)
|
||||||
|
.operands_in(vec![])
|
||||||
|
.operands_out(vec![sarg__]),
|
||||||
|
);
|
||||||
|
|
||||||
let src = &Operand::new("src", &imm.regunit);
|
let src = &Operand::new("src", &imm.regunit);
|
||||||
let dst = &Operand::new("dst", &imm.regunit);
|
let dst = &Operand::new("dst", &imm.regunit);
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ pub enum ArgAction {
|
|||||||
/// Assign the argument to the given location.
|
/// Assign the argument to the given location.
|
||||||
Assign(ArgumentLoc),
|
Assign(ArgumentLoc),
|
||||||
|
|
||||||
|
/// Assign the argument to the given location and change the type to the specified type.
|
||||||
|
/// This is used by [`ArgumentPurpose::StructArgument`].
|
||||||
|
AssignAndChangeType(ArgumentLoc, Type),
|
||||||
|
|
||||||
/// Convert the argument, then call again.
|
/// Convert the argument, then call again.
|
||||||
///
|
///
|
||||||
/// This action can split an integer type into two smaller integer arguments, or it can split a
|
/// This action can split an integer type into two smaller integer arguments, or it can split a
|
||||||
@@ -119,6 +123,13 @@ pub fn legalize_args<AA: ArgAssigner>(args: &[AbiParam], aa: &mut AA) -> Option<
|
|||||||
args.to_mut()[argno].location = loc;
|
args.to_mut()[argno].location = loc;
|
||||||
argno += 1;
|
argno += 1;
|
||||||
}
|
}
|
||||||
|
// Assign argument to a location, change type to `INVALID` and move on to the next one.
|
||||||
|
ArgAction::AssignAndChangeType(loc, ty) => {
|
||||||
|
let arg = &mut args.to_mut()[argno];
|
||||||
|
arg.location = loc;
|
||||||
|
arg.value_type = ty;
|
||||||
|
argno += 1;
|
||||||
|
}
|
||||||
// Split this argument into two smaller ones. Then revisit both.
|
// Split this argument into two smaller ones. Then revisit both.
|
||||||
ArgAction::Convert(conv) => {
|
ArgAction::Convert(conv) => {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
|
|||||||
@@ -282,6 +282,9 @@ pub enum ArgumentPurpose {
|
|||||||
/// A normal user program value passed to or from a function.
|
/// A normal user program value passed to or from a function.
|
||||||
Normal,
|
Normal,
|
||||||
|
|
||||||
|
/// A C struct passed as argument.
|
||||||
|
StructArgument(u32),
|
||||||
|
|
||||||
/// Struct return pointer.
|
/// Struct return pointer.
|
||||||
///
|
///
|
||||||
/// When a function needs to return more data than will fit in registers, the caller passes a
|
/// When a function needs to return more data than will fit in registers, the caller passes a
|
||||||
@@ -334,21 +337,19 @@ pub enum ArgumentPurpose {
|
|||||||
StackLimit,
|
StackLimit,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Text format names of the `ArgumentPurpose` variants.
|
|
||||||
static PURPOSE_NAMES: [&str; 8] = [
|
|
||||||
"normal",
|
|
||||||
"sret",
|
|
||||||
"link",
|
|
||||||
"fp",
|
|
||||||
"csr",
|
|
||||||
"vmctx",
|
|
||||||
"sigid",
|
|
||||||
"stack_limit",
|
|
||||||
];
|
|
||||||
|
|
||||||
impl fmt::Display for ArgumentPurpose {
|
impl fmt::Display for ArgumentPurpose {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
f.write_str(PURPOSE_NAMES[*self as usize])
|
f.write_str(match self {
|
||||||
|
Self::Normal => "normal",
|
||||||
|
Self::StructArgument(size) => return write!(f, "sarg({})", size),
|
||||||
|
Self::StructReturn => "sret",
|
||||||
|
Self::Link => "link",
|
||||||
|
Self::FramePointer => "fp",
|
||||||
|
Self::CalleeSaved => "csr",
|
||||||
|
Self::VMContext => "vmctx",
|
||||||
|
Self::SignatureId => "sigid",
|
||||||
|
Self::StackLimit => "stack_limit",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -364,6 +365,13 @@ impl FromStr for ArgumentPurpose {
|
|||||||
"vmctx" => Ok(Self::VMContext),
|
"vmctx" => Ok(Self::VMContext),
|
||||||
"sigid" => Ok(Self::SignatureId),
|
"sigid" => Ok(Self::SignatureId),
|
||||||
"stack_limit" => Ok(Self::StackLimit),
|
"stack_limit" => Ok(Self::StackLimit),
|
||||||
|
_ if s.starts_with("sarg(") => {
|
||||||
|
if !s.ends_with(")") {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let size: u32 = s["sarg(".len()..s.len() - 1].parse().map_err(|_| ())?;
|
||||||
|
Ok(Self::StructArgument(size))
|
||||||
|
}
|
||||||
_ => Err(()),
|
_ => Err(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -436,16 +444,17 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn argument_purpose() {
|
fn argument_purpose() {
|
||||||
let all_purpose = [
|
let all_purpose = [
|
||||||
ArgumentPurpose::Normal,
|
(ArgumentPurpose::Normal, "normal"),
|
||||||
ArgumentPurpose::StructReturn,
|
(ArgumentPurpose::StructReturn, "sret"),
|
||||||
ArgumentPurpose::Link,
|
(ArgumentPurpose::Link, "link"),
|
||||||
ArgumentPurpose::FramePointer,
|
(ArgumentPurpose::FramePointer, "fp"),
|
||||||
ArgumentPurpose::CalleeSaved,
|
(ArgumentPurpose::CalleeSaved, "csr"),
|
||||||
ArgumentPurpose::VMContext,
|
(ArgumentPurpose::VMContext, "vmctx"),
|
||||||
ArgumentPurpose::SignatureId,
|
(ArgumentPurpose::SignatureId, "sigid"),
|
||||||
ArgumentPurpose::StackLimit,
|
(ArgumentPurpose::StackLimit, "stack_limit"),
|
||||||
|
(ArgumentPurpose::StructArgument(42), "sarg(42)"),
|
||||||
];
|
];
|
||||||
for (&e, &n) in all_purpose.iter().zip(PURPOSE_NAMES.iter()) {
|
for &(e, n) in &all_purpose {
|
||||||
assert_eq!(e.to_string(), n);
|
assert_eq!(e.to_string(), n);
|
||||||
assert_eq!(Ok(e), n.parse());
|
assert_eq!(Ok(e), n.parse());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -287,7 +287,12 @@ impl StackSlots {
|
|||||||
|
|
||||||
/// Create a stack slot representing an incoming function argument.
|
/// Create a stack slot representing an incoming function argument.
|
||||||
pub fn make_incoming_arg(&mut self, ty: Type, offset: StackOffset) -> StackSlot {
|
pub fn make_incoming_arg(&mut self, ty: Type, offset: StackOffset) -> StackSlot {
|
||||||
let mut data = StackSlotData::new(StackSlotKind::IncomingArg, ty.bytes());
|
self.make_incoming_struct_arg(ty.bytes(), offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a stack slot representing an incoming struct function argument.
|
||||||
|
pub fn make_incoming_struct_arg(&mut self, size: u32, offset: StackOffset) -> StackSlot {
|
||||||
|
let mut data = StackSlotData::new(StackSlotKind::IncomingArg, size);
|
||||||
debug_assert!(offset <= StackOffset::max_value() - data.size as StackOffset);
|
debug_assert!(offset <= StackOffset::max_value() - data.size as StackOffset);
|
||||||
data.offset = Some(offset);
|
data.offset = Some(offset);
|
||||||
self.push(data)
|
self.push(data)
|
||||||
@@ -301,8 +306,11 @@ impl StackSlots {
|
|||||||
/// The requested offset is relative to this function's stack pointer immediately before making
|
/// The requested offset is relative to this function's stack pointer immediately before making
|
||||||
/// the call.
|
/// the call.
|
||||||
pub fn get_outgoing_arg(&mut self, ty: Type, offset: StackOffset) -> StackSlot {
|
pub fn get_outgoing_arg(&mut self, ty: Type, offset: StackOffset) -> StackSlot {
|
||||||
let size = ty.bytes();
|
self.get_outgoing_struct_arg(ty.bytes(), offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// FIXME
|
||||||
|
pub fn get_outgoing_struct_arg(&mut self, size: u32, offset: StackOffset) -> StackSlot {
|
||||||
// Look for an existing outgoing stack slot with the same offset and size.
|
// Look for an existing outgoing stack slot with the same offset and size.
|
||||||
let inspos = match self.outgoing.binary_search_by_key(&(offset, size), |&ss| {
|
let inspos = match self.outgoing.binary_search_by_key(&(offset, size), |&ss| {
|
||||||
(self[ss].offset.unwrap(), self[ss].size)
|
(self[ss].offset.unwrap(), self[ss].size)
|
||||||
|
|||||||
@@ -343,6 +343,7 @@ impl Display for Type {
|
|||||||
f.write_str(match *self {
|
f.write_str(match *self {
|
||||||
IFLAGS => "iflags",
|
IFLAGS => "iflags",
|
||||||
FFLAGS => "fflags",
|
FFLAGS => "fflags",
|
||||||
|
SARG__ => "sarg__",
|
||||||
INVALID => panic!("INVALID encountered"),
|
INVALID => panic!("INVALID encountered"),
|
||||||
_ => panic!("Unknown Type(0x{:x})", self.0),
|
_ => panic!("Unknown Type(0x{:x})", self.0),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2152,6 +2152,8 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||||||
panic!("x86-specific opcode in supposedly arch-neutral IR!");
|
panic!("x86-specific opcode in supposedly arch-neutral IR!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Opcode::DummySarg => unreachable!(),
|
||||||
|
|
||||||
Opcode::AvgRound => unimplemented!(),
|
Opcode::AvgRound => unimplemented!(),
|
||||||
Opcode::Iabs => unimplemented!(),
|
Opcode::Iabs => unimplemented!(),
|
||||||
Opcode::Snarrow
|
Opcode::Snarrow
|
||||||
|
|||||||
@@ -108,6 +108,19 @@ impl Args {
|
|||||||
|
|
||||||
impl ArgAssigner for Args {
|
impl ArgAssigner for Args {
|
||||||
fn assign(&mut self, arg: &AbiParam) -> ArgAction {
|
fn assign(&mut self, arg: &AbiParam) -> ArgAction {
|
||||||
|
if let ArgumentPurpose::StructArgument(size) = arg.purpose {
|
||||||
|
if self.call_conv != CallConv::SystemV {
|
||||||
|
panic!(
|
||||||
|
"The sarg argument purpose is not yet implemented for non-systemv call conv {:?}",
|
||||||
|
self.call_conv,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let loc = ArgumentLoc::Stack(self.offset as i32);
|
||||||
|
self.offset += size;
|
||||||
|
debug_assert!(self.offset <= i32::MAX as u32);
|
||||||
|
return ArgAction::AssignAndChangeType(loc, types::SARG__);
|
||||||
|
}
|
||||||
|
|
||||||
let ty = arg.value_type;
|
let ty = arg.value_type;
|
||||||
|
|
||||||
if ty.bits() > u16::from(self.pointer_bits) {
|
if ty.bits() > u16::from(self.pointer_bits) {
|
||||||
|
|||||||
@@ -22,8 +22,9 @@ use crate::cursor::{Cursor, FuncCursor};
|
|||||||
use crate::flowgraph::ControlFlowGraph;
|
use crate::flowgraph::ControlFlowGraph;
|
||||||
use crate::ir::instructions::CallInfo;
|
use crate::ir::instructions::CallInfo;
|
||||||
use crate::ir::{
|
use crate::ir::{
|
||||||
AbiParam, ArgumentLoc, ArgumentPurpose, Block, DataFlowGraph, Function, Inst, InstBuilder,
|
AbiParam, ArgumentLoc, ArgumentPurpose, Block, DataFlowGraph, ExtFuncData, ExternalName,
|
||||||
MemFlags, SigRef, Signature, StackSlotData, StackSlotKind, Type, Value, ValueLoc,
|
Function, Inst, InstBuilder, LibCall, MemFlags, SigRef, Signature, StackSlotData,
|
||||||
|
StackSlotKind, Type, Value, ValueLoc,
|
||||||
};
|
};
|
||||||
use crate::isa::TargetIsa;
|
use crate::isa::TargetIsa;
|
||||||
use crate::legalizer::split::{isplit, vsplit};
|
use crate::legalizer::split::{isplit, vsplit};
|
||||||
@@ -113,12 +114,33 @@ fn legalize_entry_params(func: &mut Function, entry: Block) {
|
|||||||
|
|
||||||
let abi_type = pos.func.signature.params[abi_arg];
|
let abi_type = pos.func.signature.params[abi_arg];
|
||||||
let arg_type = pos.func.dfg.value_type(arg);
|
let arg_type = pos.func.dfg.value_type(arg);
|
||||||
|
match abi_type.purpose {
|
||||||
|
ArgumentPurpose::StructArgument(size) => {
|
||||||
|
let offset = if let ArgumentLoc::Stack(offset) = abi_type.location {
|
||||||
|
offset
|
||||||
|
} else {
|
||||||
|
unreachable!("StructArgument must already have a Stack ArgumentLoc assigned");
|
||||||
|
};
|
||||||
|
let ss = pos.func.stack_slots.make_incoming_struct_arg(size, offset);
|
||||||
|
let struct_arg = pos.ins().stack_addr(arg_type, ss, 0);
|
||||||
|
pos.func.dfg.change_to_alias(arg, struct_arg);
|
||||||
|
let dummy = pos
|
||||||
|
.func
|
||||||
|
.dfg
|
||||||
|
.append_block_param(entry, crate::ir::types::SARG__);
|
||||||
|
pos.func.locations[dummy] = ValueLoc::Stack(ss);
|
||||||
|
abi_arg += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
if arg_type == abi_type.value_type {
|
if arg_type == abi_type.value_type {
|
||||||
// No value translation is necessary, this argument matches the ABI type.
|
// No value translation is necessary, this argument matches the ABI type.
|
||||||
// Just use the original block argument value. This is the most common case.
|
// Just use the original block argument value. This is the most common case.
|
||||||
pos.func.dfg.attach_block_param(entry, arg);
|
pos.func.dfg.attach_block_param(entry, arg);
|
||||||
match abi_type.purpose {
|
match abi_type.purpose {
|
||||||
ArgumentPurpose::Normal => {}
|
ArgumentPurpose::Normal => {}
|
||||||
|
ArgumentPurpose::StructArgument(_) => unreachable!("Handled above"),
|
||||||
ArgumentPurpose::FramePointer => {}
|
ArgumentPurpose::FramePointer => {}
|
||||||
ArgumentPurpose::CalleeSaved => {}
|
ArgumentPurpose::CalleeSaved => {}
|
||||||
ArgumentPurpose::StructReturn => {
|
ArgumentPurpose::StructReturn => {
|
||||||
@@ -137,7 +159,7 @@ fn legalize_entry_params(func: &mut Function, entry: Block) {
|
|||||||
debug_assert!(!has_stack_limit, "Multiple stack_limit arguments found");
|
debug_assert!(!has_stack_limit, "Multiple stack_limit arguments found");
|
||||||
has_stack_limit = true;
|
has_stack_limit = true;
|
||||||
}
|
}
|
||||||
_ => panic!("Unexpected special-purpose arg {}", abi_type),
|
ArgumentPurpose::Link => panic!("Unexpected link arg {}", abi_type),
|
||||||
}
|
}
|
||||||
abi_arg += 1;
|
abi_arg += 1;
|
||||||
} else {
|
} else {
|
||||||
@@ -168,7 +190,7 @@ fn legalize_entry_params(func: &mut Function, entry: Block) {
|
|||||||
for &arg in &pos.func.signature.params[abi_arg..] {
|
for &arg in &pos.func.signature.params[abi_arg..] {
|
||||||
match arg.purpose {
|
match arg.purpose {
|
||||||
// Any normal parameters should have been processed above.
|
// Any normal parameters should have been processed above.
|
||||||
ArgumentPurpose::Normal => {
|
ArgumentPurpose::Normal | ArgumentPurpose::StructArgument(_) => {
|
||||||
panic!("Leftover arg: {}", arg);
|
panic!("Leftover arg: {}", arg);
|
||||||
}
|
}
|
||||||
// The callee-save parameters should not appear until after register allocation is
|
// The callee-save parameters should not appear until after register allocation is
|
||||||
@@ -587,9 +609,20 @@ fn convert_to_abi<PutArg>(
|
|||||||
|
|
||||||
/// Check if a sequence of arguments match a desired sequence of argument types.
|
/// Check if a sequence of arguments match a desired sequence of argument types.
|
||||||
fn check_arg_types(dfg: &DataFlowGraph, args: &[Value], types: &[AbiParam]) -> bool {
|
fn check_arg_types(dfg: &DataFlowGraph, args: &[Value], types: &[AbiParam]) -> bool {
|
||||||
let arg_types = args.iter().map(|&v| dfg.value_type(v));
|
let mut args = args.iter();
|
||||||
let sig_types = types.iter().map(|&at| at.value_type);
|
let mut types = types.iter();
|
||||||
arg_types.eq(sig_types)
|
loop {
|
||||||
|
match (args.next(), types.next()) {
|
||||||
|
(Some(&v), Some(at)) => {
|
||||||
|
if let ArgumentPurpose::StructArgument(_) = at.purpose {
|
||||||
|
} else if dfg.value_type(v) != at.value_type {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Some(_), None) | (None, Some(_)) => return false,
|
||||||
|
(None, None) => return true,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the arguments of the call `inst` match the signature.
|
/// Check if the arguments of the call `inst` match the signature.
|
||||||
@@ -699,7 +732,12 @@ fn legalize_inst_arguments<ArgType>(
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
let mut put_arg = |func: &mut Function, arg| {
|
let mut put_arg = |func: &mut Function, arg| {
|
||||||
let abi_type = get_abi_type(func, abi_arg);
|
let abi_type = get_abi_type(func, abi_arg);
|
||||||
if func.dfg.value_type(arg) == abi_type.value_type {
|
let struct_argument = if let ArgumentPurpose::StructArgument(_) = abi_type.purpose {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
if func.dfg.value_type(arg) == abi_type.value_type || struct_argument {
|
||||||
// This is the argument type we need.
|
// This is the argument type we need.
|
||||||
vlist.as_mut_slice(&mut func.dfg.value_lists)[num_fixed_values + abi_arg] = arg;
|
vlist.as_mut_slice(&mut func.dfg.value_lists)[num_fixed_values + abi_arg] = arg;
|
||||||
abi_arg += 1;
|
abi_arg += 1;
|
||||||
@@ -762,7 +800,7 @@ pub fn handle_call_abi(
|
|||||||
|
|
||||||
// Start by checking if the argument types already match the signature.
|
// Start by checking if the argument types already match the signature.
|
||||||
let sig_ref = match check_call_signature(&pos.func.dfg, inst) {
|
let sig_ref = match check_call_signature(&pos.func.dfg, inst) {
|
||||||
Ok(_) => return spill_call_arguments(pos),
|
Ok(_) => return spill_call_arguments(pos, isa),
|
||||||
Err(s) => s,
|
Err(s) => s,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -800,7 +838,7 @@ pub fn handle_call_abi(
|
|||||||
|
|
||||||
// Go back and insert spills for any stack arguments.
|
// Go back and insert spills for any stack arguments.
|
||||||
pos.goto_inst(inst);
|
pos.goto_inst(inst);
|
||||||
spill_call_arguments(pos);
|
spill_call_arguments(pos, isa);
|
||||||
|
|
||||||
// Yes, we changed stuff.
|
// Yes, we changed stuff.
|
||||||
true
|
true
|
||||||
@@ -990,7 +1028,8 @@ fn spill_entry_params(func: &mut Function, entry: Block) {
|
|||||||
.iter()
|
.iter()
|
||||||
.zip(func.dfg.block_params(entry))
|
.zip(func.dfg.block_params(entry))
|
||||||
{
|
{
|
||||||
if let ArgumentLoc::Stack(offset) = abi.location {
|
if let ArgumentPurpose::StructArgument(_) = abi.purpose {
|
||||||
|
} else if let ArgumentLoc::Stack(offset) = abi.location {
|
||||||
let ss = func.stack_slots.make_incoming_arg(abi.value_type, offset);
|
let ss = func.stack_slots.make_incoming_arg(abi.value_type, offset);
|
||||||
func.locations[arg] = ValueLoc::Stack(ss);
|
func.locations[arg] = ValueLoc::Stack(ss);
|
||||||
}
|
}
|
||||||
@@ -1005,7 +1044,7 @@ fn spill_entry_params(func: &mut Function, entry: Block) {
|
|||||||
/// TODO: The outgoing stack slots can be written a bit earlier, as long as there are no branches
|
/// TODO: The outgoing stack slots can be written a bit earlier, as long as there are no branches
|
||||||
/// or calls between writing the stack slots and the call instruction. Writing the slots earlier
|
/// or calls between writing the stack slots and the call instruction. Writing the slots earlier
|
||||||
/// could help reduce register pressure before the call.
|
/// could help reduce register pressure before the call.
|
||||||
fn spill_call_arguments(pos: &mut FuncCursor) -> bool {
|
fn spill_call_arguments(pos: &mut FuncCursor, isa: &dyn TargetIsa) -> bool {
|
||||||
let inst = pos
|
let inst = pos
|
||||||
.current_inst()
|
.current_inst()
|
||||||
.expect("Cursor must point to a call instruction");
|
.expect("Cursor must point to a call instruction");
|
||||||
@@ -1032,9 +1071,15 @@ fn spill_call_arguments(pos: &mut FuncCursor) -> bool {
|
|||||||
// Assign `arg` to a new stack slot, unless it's already in the correct
|
// Assign `arg` to a new stack slot, unless it's already in the correct
|
||||||
// slot. The legalization needs to be idempotent, so we should see a
|
// slot. The legalization needs to be idempotent, so we should see a
|
||||||
// correct outgoing slot on the second pass.
|
// correct outgoing slot on the second pass.
|
||||||
let ss = stack_slots.get_outgoing_arg(abi.value_type, offset);
|
let (ss, size) = match abi.purpose {
|
||||||
|
ArgumentPurpose::StructArgument(size) => (
|
||||||
|
stack_slots.get_outgoing_struct_arg(size, offset),
|
||||||
|
Some(size),
|
||||||
|
),
|
||||||
|
_ => (stack_slots.get_outgoing_arg(abi.value_type, offset), None),
|
||||||
|
};
|
||||||
if locations[arg] != ValueLoc::Stack(ss) {
|
if locations[arg] != ValueLoc::Stack(ss) {
|
||||||
Some((idx, arg, ss))
|
Some((idx, arg, ss, size))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -1050,8 +1095,34 @@ fn spill_call_arguments(pos: &mut FuncCursor) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Insert the spill instructions and rewrite call arguments.
|
// Insert the spill instructions and rewrite call arguments.
|
||||||
for (idx, arg, ss) in arglist {
|
for (idx, arg, ss, size) in arglist {
|
||||||
let stack_val = pos.ins().spill(arg);
|
let stack_val = if let Some(size) = size {
|
||||||
|
// Struct argument
|
||||||
|
let pointer_type = pos.func.dfg.value_type(arg);
|
||||||
|
let src = arg;
|
||||||
|
let dest = pos.ins().stack_addr(pointer_type, ss, 0);
|
||||||
|
let size = pos.ins().iconst(pointer_type, i64::from(size));
|
||||||
|
let signature = {
|
||||||
|
let mut s = Signature::new(isa.default_call_conv());
|
||||||
|
s.params.push(AbiParam::new(pointer_type));
|
||||||
|
s.params.push(AbiParam::new(pointer_type));
|
||||||
|
s.params.push(AbiParam::new(pointer_type));
|
||||||
|
legalize_libcall_signature(&mut s, isa);
|
||||||
|
pos.func.import_signature(s)
|
||||||
|
};
|
||||||
|
|
||||||
|
let libc_memcpy = pos.func.import_function(ExtFuncData {
|
||||||
|
name: ExternalName::LibCall(LibCall::Memcpy),
|
||||||
|
signature,
|
||||||
|
colocated: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
pos.ins().call(libc_memcpy, &[dest, src, size]);
|
||||||
|
pos.ins().dummy_sarg__()
|
||||||
|
} else {
|
||||||
|
// Non struct argument
|
||||||
|
pos.ins().spill(arg)
|
||||||
|
};
|
||||||
pos.func.locations[stack_val] = ValueLoc::Stack(ss);
|
pos.func.locations[stack_val] = ValueLoc::Stack(ss);
|
||||||
pos.func.dfg.inst_variable_args_mut(inst)[idx] = stack_val;
|
pos.func.dfg.inst_variable_args_mut(inst)[idx] = stack_val;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,9 +65,9 @@ use crate::ir;
|
|||||||
use crate::ir::entities::AnyEntity;
|
use crate::ir::entities::AnyEntity;
|
||||||
use crate::ir::instructions::{BranchInfo, CallInfo, InstructionFormat, ResolvedConstraint};
|
use crate::ir::instructions::{BranchInfo, CallInfo, InstructionFormat, ResolvedConstraint};
|
||||||
use crate::ir::{
|
use crate::ir::{
|
||||||
types, ArgumentLoc, Block, Constant, FuncRef, Function, GlobalValue, Inst, InstructionData,
|
types, ArgumentLoc, ArgumentPurpose, Block, Constant, FuncRef, Function, GlobalValue, Inst,
|
||||||
JumpTable, Opcode, SigRef, StackSlot, StackSlotKind, Type, Value, ValueDef, ValueList,
|
InstructionData, JumpTable, Opcode, SigRef, StackSlot, StackSlotKind, Type, Value, ValueDef,
|
||||||
ValueLoc,
|
ValueList, ValueLoc,
|
||||||
};
|
};
|
||||||
use crate::isa::TargetIsa;
|
use crate::isa::TargetIsa;
|
||||||
use crate::iterators::IteratorExtras;
|
use crate::iterators::IteratorExtras;
|
||||||
@@ -1473,7 +1473,8 @@ impl<'a> Verifier<'a> {
|
|||||||
),
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if slot.size != abi.value_type.bytes() {
|
if abi.purpose == ArgumentPurpose::StructArgument(slot.size) {
|
||||||
|
} else if slot.size != abi.value_type.bytes() {
|
||||||
return errors.fatal((
|
return errors.fatal((
|
||||||
inst,
|
inst,
|
||||||
self.context(inst),
|
self.context(inst),
|
||||||
@@ -1986,6 +1987,20 @@ impl<'a> Verifier<'a> {
|
|||||||
))
|
))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
self.func
|
||||||
|
.signature
|
||||||
|
.returns
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.for_each(|(i, ret)| {
|
||||||
|
if let ArgumentPurpose::StructArgument(_) = ret.purpose {
|
||||||
|
errors.report((
|
||||||
|
AnyEntity::Function,
|
||||||
|
format!("Return value at position {} can't be an struct argument", i),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if errors.has_error() {
|
if errors.has_error() {
|
||||||
Err(())
|
Err(())
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
57
cranelift/filetests/filetests/isa/x86/struct-arg.clif
Normal file
57
cranelift/filetests/filetests/isa/x86/struct-arg.clif
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
test compile
|
||||||
|
set is_pic
|
||||||
|
target x86_64
|
||||||
|
|
||||||
|
function u0:0(i64 sarg(64)) -> i8 system_v {
|
||||||
|
block0(v0: i64):
|
||||||
|
v1 = load.i8 v0
|
||||||
|
return v1
|
||||||
|
}
|
||||||
|
|
||||||
|
; check: function u0:0(sarg__ sarg(64) [0], i64 fp [%rbp]) -> i8 [%rax], i64 fp [%rbp] system_v {
|
||||||
|
; nextln: ss0 = incoming_arg 64, offset 0
|
||||||
|
; nextln: ss1 = incoming_arg 16, offset -16
|
||||||
|
|
||||||
|
; check: block0(v3: sarg__ [ss0], v5: i64 [%rbp]):
|
||||||
|
; nextln: [RexOp1pushq#50] x86_push v5
|
||||||
|
; nextln: [RexOp1copysp#8089] copy_special %rsp -> %rbp
|
||||||
|
; nextln: [RexOp1spaddr_id#808d,%rax] v2 = stack_addr.i64 ss0
|
||||||
|
; nextln: v0 -> v2
|
||||||
|
; nextln: [RexOp2ld#4b6,%rax] v4 = uload8.i32 v2
|
||||||
|
; nextln: [null#00,%rax] v1 = ireduce.i8 v4
|
||||||
|
; nextln: [RexOp1popq#58,%rbp] v6 = x86_pop.i64
|
||||||
|
; nextln: [Op1ret#c3] return v1, v6
|
||||||
|
; nextln: }
|
||||||
|
|
||||||
|
function u0:1(i64) -> i8 system_v {
|
||||||
|
fn1 = u0:0(i64 sarg(64)) -> i8 system_v
|
||||||
|
|
||||||
|
block0(v0: i64):
|
||||||
|
v1 = call fn1(v0)
|
||||||
|
return v1
|
||||||
|
}
|
||||||
|
|
||||||
|
; check: function u0:1(i64 [%rdi], i64 fp [%rbp]) -> i8 [%rax], i64 fp [%rbp] system_v {
|
||||||
|
; nextln: ss0 = outgoing_arg 64, offset 0
|
||||||
|
; nextln: ss1 = incoming_arg 16, offset -16
|
||||||
|
; nextln: sig0 = (sarg__ sarg(64) [0]) -> i8 [%rax] system_v
|
||||||
|
; nextln: sig1 = (i64 [%rdi], i64 [%rsi], i64 [%rdx]) system_v
|
||||||
|
; nextln: fn1 = u0:0 sig0
|
||||||
|
; nextln: fn2 = %Memcpy sig1
|
||||||
|
|
||||||
|
; check: block0(v0: i64 [%rdi], v5: i64 [%rbp]):
|
||||||
|
; nextln: [RexOp1pushq#50] x86_push v5
|
||||||
|
; nextln: [RexOp1copysp#8089] copy_special %rsp -> %rbp
|
||||||
|
; nextln: [RexOp1adjustsp_ib#d083] adjust_sp_down_imm 64
|
||||||
|
; nextln: [RexOp1spaddr_id#808d,%rax] v2 = stack_addr.i64 ss0
|
||||||
|
; nextln: [RexOp1pu_id#b8,%rcx] v3 = iconst.i64 64
|
||||||
|
; nextln: [RexOp1rmov#8089] regmove v0, %rdi -> %rsi
|
||||||
|
; nextln: [RexOp1rmov#8089] regmove v2, %rax -> %rdi
|
||||||
|
; nextln: [RexOp1rmov#8089] regmove v3, %rcx -> %rdx
|
||||||
|
; nextln: [Op1call_plt_id#e8] call fn2(v2, v0, v3)
|
||||||
|
; nextln: [dummy_sarg__#00,ss0] v4 = dummy_sarg__
|
||||||
|
; nextln: [Op1call_plt_id#e8,%rax] v1 = call fn1(v4)
|
||||||
|
; nextln: [RexOp1adjustsp_ib#8083] adjust_sp_up_imm 64
|
||||||
|
; nextln: [RexOp1popq#58,%rbp] v6 = x86_pop.i64
|
||||||
|
; nextln: [Op1ret#c3] return v1, v6
|
||||||
|
; nextln: }
|
||||||
@@ -329,6 +329,7 @@ impl<'a> Lexer<'a> {
|
|||||||
.unwrap_or_else(|| match text {
|
.unwrap_or_else(|| match text {
|
||||||
"iflags" => Token::Type(types::IFLAGS),
|
"iflags" => Token::Type(types::IFLAGS),
|
||||||
"fflags" => Token::Type(types::FFLAGS),
|
"fflags" => Token::Type(types::FFLAGS),
|
||||||
|
"sarg__" => Token::Type(types::SARG__),
|
||||||
_ => Token::Identifier(text),
|
_ => Token::Identifier(text),
|
||||||
}),
|
}),
|
||||||
loc,
|
loc,
|
||||||
@@ -620,7 +621,7 @@ mod tests {
|
|||||||
let mut lex = Lexer::new(
|
let mut lex = Lexer::new(
|
||||||
"v0 v00 vx01 block1234567890 block5234567890 v1x vx1 vxvx4 \
|
"v0 v00 vx01 block1234567890 block5234567890 v1x vx1 vxvx4 \
|
||||||
function0 function b1 i32x4 f32x5 \
|
function0 function b1 i32x4 f32x5 \
|
||||||
iflags fflags iflagss",
|
iflags fflags sarg__ iflagss",
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lex.next(),
|
lex.next(),
|
||||||
@@ -643,6 +644,7 @@ mod tests {
|
|||||||
assert_eq!(lex.next(), token(Token::Identifier("f32x5"), 1));
|
assert_eq!(lex.next(), token(Token::Identifier("f32x5"), 1));
|
||||||
assert_eq!(lex.next(), token(Token::Type(types::IFLAGS), 1));
|
assert_eq!(lex.next(), token(Token::Type(types::IFLAGS), 1));
|
||||||
assert_eq!(lex.next(), token(Token::Type(types::FFLAGS), 1));
|
assert_eq!(lex.next(), token(Token::Type(types::FFLAGS), 1));
|
||||||
|
assert_eq!(lex.next(), token(Token::Type(types::SARG__), 1));
|
||||||
assert_eq!(lex.next(), token(Token::Identifier("iflagss"), 1));
|
assert_eq!(lex.next(), token(Token::Identifier("iflagss"), 1));
|
||||||
assert_eq!(lex.next(), None);
|
assert_eq!(lex.next(), None);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,10 @@ use cranelift_codegen::ir::instructions::{InstructionData, InstructionFormat, Va
|
|||||||
use cranelift_codegen::ir::types::INVALID;
|
use cranelift_codegen::ir::types::INVALID;
|
||||||
use cranelift_codegen::ir::types::*;
|
use cranelift_codegen::ir::types::*;
|
||||||
use cranelift_codegen::ir::{
|
use cranelift_codegen::ir::{
|
||||||
AbiParam, ArgumentExtension, ArgumentLoc, Block, Constant, ConstantData, ExtFuncData,
|
AbiParam, ArgumentExtension, ArgumentLoc, ArgumentPurpose, Block, Constant, ConstantData,
|
||||||
ExternalName, FuncRef, Function, GlobalValue, GlobalValueData, Heap, HeapData, HeapStyle,
|
ExtFuncData, ExternalName, FuncRef, Function, GlobalValue, GlobalValueData, Heap, HeapData,
|
||||||
JumpTable, JumpTableData, MemFlags, Opcode, SigRef, Signature, StackSlot, StackSlotData,
|
HeapStyle, JumpTable, JumpTableData, MemFlags, Opcode, SigRef, Signature, StackSlot,
|
||||||
StackSlotKind, Table, TableData, Type, Value, ValueLoc,
|
StackSlotData, StackSlotKind, Table, TableData, Type, Value, ValueLoc,
|
||||||
};
|
};
|
||||||
use cranelift_codegen::isa::{self, CallConv, Encoding, RegUnit, TargetIsa};
|
use cranelift_codegen::isa::{self, CallConv, Encoding, RegUnit, TargetIsa};
|
||||||
use cranelift_codegen::packed_option::ReservedValue;
|
use cranelift_codegen::packed_option::ReservedValue;
|
||||||
@@ -1423,6 +1423,14 @@ impl<'a> Parser<'a> {
|
|||||||
match s {
|
match s {
|
||||||
"uext" => arg.extension = ArgumentExtension::Uext,
|
"uext" => arg.extension = ArgumentExtension::Uext,
|
||||||
"sext" => arg.extension = ArgumentExtension::Sext,
|
"sext" => arg.extension = ArgumentExtension::Sext,
|
||||||
|
"sarg" => {
|
||||||
|
self.consume();
|
||||||
|
self.match_token(Token::LPar, "expected '(' to begin sarg size")?;
|
||||||
|
let size = self.match_uimm32("expected byte-size in sarg decl")?;
|
||||||
|
self.match_token(Token::RPar, "expected ')' to end sarg size")?;
|
||||||
|
arg.purpose = ArgumentPurpose::StructArgument(size.into());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if let Ok(purpose) = s.parse() {
|
if let Ok(purpose) = s.parse() {
|
||||||
arg.purpose = purpose;
|
arg.purpose = purpose;
|
||||||
|
|||||||
Reference in New Issue
Block a user