diff --git a/docs/langref.rst b/docs/langref.rst index 5e6d999b09..23cc225d7f 100644 --- a/docs/langref.rst +++ b/docs/langref.rst @@ -893,6 +893,11 @@ Number of arguments to a function incoming frame pointer, and callee-saved registers that are saved in the prologue. +Size of function call arguments on the stack + At most :math:`2^{32} - 1` bytes. + + This is probably not possible to achieve given the limit on the number of + arguments, except by requiring extremely large offsets for stack arguments. Glossary ======== diff --git a/lib/cretonne/src/ir/extfunc.rs b/lib/cretonne/src/ir/extfunc.rs index ae012e7530..5c4d1d1c71 100644 --- a/lib/cretonne/src/ir/extfunc.rs +++ b/lib/cretonne/src/ir/extfunc.rs @@ -5,19 +5,30 @@ //! //! This module declares the data types used to represent external functions and call signatures. +use ir::{Type, FunctionName, SigRef, ArgumentLoc}; +use std::cmp; use std::fmt::{self, Display, Formatter}; -use ir::{Type, FunctionName, SigRef}; /// Function signature. /// /// The function signature describes the types of arguments and return values along with other /// details that are needed to call a function correctly. -#[derive(Clone, PartialEq, Eq, Debug)] +/// +/// A signature can optionally include ISA-specific ABI information which specifies exactly how +/// arguments and return values are passed. +#[derive(Clone, Debug)] pub struct Signature { /// Types of the arguments passed to the function. pub argument_types: Vec, /// Types returned from the function. pub return_types: Vec, + + /// When the signature has been legalized to a specific ISA, this holds the size of the + /// argument array on the stack. Before legalization, this is `None`. + /// + /// This can be computed from the legalized `argument_types` array as the maximum (offset plus + /// byte size) of the `ArgumentLoc::Stack(offset)` argument. + pub argument_bytes: Option, } impl Signature { @@ -26,8 +37,24 @@ impl Signature { Signature { argument_types: Vec::new(), return_types: Vec::new(), + argument_bytes: None, } } + + /// Compute the size of the stack arguments and mark signature as legalized. + /// + /// Even if there are no stack arguments, this will set `argument_types` to `Some(0)` instead + /// of `None`. This indicates that the signature has been legalized. + pub fn compute_argument_bytes(&mut self) { + let bytes = self.argument_types + .iter() + .filter_map(|arg| match arg.location { + ArgumentLoc::Stack(offset) => Some(offset + arg.value_type.bits() as u32 / 8), + _ => None, + }) + .fold(0, cmp::max); + self.argument_bytes = Some(bytes); + } } fn write_list(f: &mut Formatter, args: &Vec) -> fmt::Result { @@ -60,7 +87,7 @@ impl Display for Signature { /// /// This describes the value type being passed to or from a function along with flags that affect /// how the argument is passed. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[derive(Copy, Clone, Debug)] pub struct ArgumentType { /// Type of the argument value. pub value_type: Type, @@ -68,6 +95,10 @@ pub struct ArgumentType { pub extension: ArgumentExtension, /// Place this argument in a register if possible. pub inreg: bool, + + /// ABI-specific location of this argument, or `Unassigned` for arguments that have not yet + /// been legalized. + pub location: ArgumentLoc, } impl ArgumentType { @@ -77,6 +108,7 @@ impl ArgumentType { value_type: vt, extension: ArgumentExtension::None, inreg: false, + location: Default::default(), } } } @@ -92,7 +124,13 @@ impl Display for ArgumentType { if self.inreg { write!(f, " inreg")?; } - Ok(()) + + // This really needs a `&TargetAbi` so we can print register units correctly. + match self.location { + ArgumentLoc::Reg(ru) => write!(f, " [%{}]", ru), + ArgumentLoc::Stack(offset) => write!(f, " [{}]", offset), + ArgumentLoc::Unassigned => Ok(()), + } } } @@ -154,5 +192,19 @@ mod tests { assert_eq!(sig.to_string(), "(i32, i32x4) -> f32"); sig.return_types.push(ArgumentType::new(B8)); assert_eq!(sig.to_string(), "(i32, i32x4) -> f32, b8"); + + // Test the offset computation algorithm. + assert_eq!(sig.argument_bytes, None); + sig.argument_types[1].location = ArgumentLoc::Stack(8); + sig.compute_argument_bytes(); + // An `i32x4` at offset 8 requires a 24-byte argument array. + assert_eq!(sig.argument_bytes, Some(24)); + // Order does not matter. + sig.argument_types[0].location = ArgumentLoc::Stack(24); + sig.compute_argument_bytes(); + assert_eq!(sig.argument_bytes, Some(28)); + + // Writing ABI-annotated signatures. + assert_eq!(sig.to_string(), "(i32 [24], i32x4 [8]) -> f32, b8"); } } diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index 204b78c9e5..47ff73a89c 100644 --- a/lib/reader/src/parser.rs +++ b/lib/reader/src/parser.rs @@ -1360,7 +1360,7 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { use super::*; - use cretonne::ir::{ArgumentType, ArgumentExtension}; + use cretonne::ir::ArgumentExtension; use cretonne::ir::types; use cretonne::ir::entities::AnyEntity; use testfile::{Details, Comment}; @@ -1371,12 +1371,9 @@ mod tests { fn argument_type() { let mut p = Parser::new("i32 sext"); let arg = p.parse_argument_type().unwrap(); - assert_eq!(arg, - ArgumentType { - value_type: types::I32, - extension: ArgumentExtension::Sext, - inreg: false, - }); + assert_eq!(arg.value_type, types::I32); + assert_eq!(arg.extension, ArgumentExtension::Sext); + assert_eq!(arg.inreg, false); let Error { location, message } = p.parse_argument_type().unwrap_err(); assert_eq!(location.line_number, 1); assert_eq!(message, "expected argument type");