diff --git a/docs/cton_lexer.py b/docs/cton_lexer.py index 1c7224b593..1024765cbe 100644 --- a/docs/cton_lexer.py +++ b/docs/cton_lexer.py @@ -42,8 +42,7 @@ class CretonneLexer(RegexLexer): (r'[-+]?(\d+\.\d+([eE]\d+)?|s?NaN|Inf)', Number.Float), (r'[-+]?\d+', Number.Integer), # Known attributes. - (keywords('align', 'aligntrap', 'uext', 'sext', 'inreg'), - Name.Attribute), + (keywords('uext', 'sext'), Name.Attribute), # Well known value types. (r'\b(b\d+|i\d+|f32|f64)(x\d+)?\b', Keyword.Type), # v = value diff --git a/docs/langref.rst b/docs/langref.rst index d4b30780b4..fce84d0cdf 100644 --- a/docs/langref.rst +++ b/docs/langref.rst @@ -364,8 +364,9 @@ calling convention: signature : "(" [arglist] ")" ["->" retlist] [call_conv] arglist : arg { "," arg } retlist : arglist - arg : type { flag } - flag : "uext" | "sext" | "inreg" + arg : type [argext] [argspecial] + argext : "uext" | "sext" + argspecial: "sret" | "link" | "fp" | "csr" callconv : `string` Arguments and return values have flags whose meaning is mostly target diff --git a/filetests/parser/call.cton b/filetests/parser/call.cton index 027749f562..7626524110 100644 --- a/filetests/parser/call.cton +++ b/filetests/parser/call.cton @@ -68,3 +68,13 @@ ebb0(v0: i64): ; check: call_indirect $sig0, $v1($v0) ; check: $v3, $v4 = call_indirect $sig2, $v1() ; check: return + +; Special purpose function arguments +function special1(i32 sret, i32 fp, i32 csr, i32 link) -> i32 link, i32 fp, i32 csr, i32 sret { +ebb0(v1: i32, v2: i32, v3: i32): + return v4, v2, v3, v1 +} +; check: function special1(i32 sret, i32 fp, i32 link) -> i32 link, i32 fp, i32 sret { +; check: ebb0($v1: i32, $v2: i32, $v3: i32): +; check: return $v3, $v2, $v1 +; check: } diff --git a/lib/cretonne/src/ir/extfunc.rs b/lib/cretonne/src/ir/extfunc.rs index 43daaed192..a21b5911a9 100644 --- a/lib/cretonne/src/ir/extfunc.rs +++ b/lib/cretonne/src/ir/extfunc.rs @@ -9,6 +9,7 @@ use ir::{Type, FunctionName, SigRef, ArgumentLoc}; use isa::RegInfo; use std::cmp; use std::fmt; +use std::str::FromStr; /// Function signature. /// @@ -111,10 +112,10 @@ impl fmt::Display for Signature { pub struct ArgumentType { /// Type of the argument value. pub value_type: Type, + /// Special purpose of argument, or `Normal`. + pub purpose: ArgumentPurpose, /// Method for extending argument to a full register. 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. @@ -127,7 +128,7 @@ impl ArgumentType { ArgumentType { value_type: vt, extension: ArgumentExtension::None, - inreg: false, + purpose: ArgumentPurpose::Normal, location: Default::default(), } } @@ -149,8 +150,8 @@ impl<'a> fmt::Display for DisplayArgumentType<'a> { ArgumentExtension::Uext => write!(f, " uext")?, ArgumentExtension::Sext => write!(f, " sext")?, } - if self.0.inreg { - write!(f, " inreg")?; + if self.0.purpose != ArgumentPurpose::Normal { + write!(f, " {}", self.0.purpose)?; } if self.0.location.is_assigned() { @@ -181,6 +182,75 @@ pub enum ArgumentExtension { Sext, } +/// The special purpose of a function argument. +/// +/// Function arguments and return values are used to pass user program values between functions, +/// but they are also used to represent special registers with significance to the ABI such as +/// frame pointers and callee-saved registers. +/// +/// The argument purpose is used to indicate any special meaning of an argument or return value. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum ArgumentPurpose { + /// A normal user program value passed to or from a function. + Normal, + + /// Struct return pointer. + /// + /// When a function needs to return more data than will fit in registers, the caller passes a + /// pointer to a memory location where the return value can be written. In some ABIs, this + /// struct return pointer is passed in a specific register. + /// + /// This argument kind can also appear as a return value for ABIs that require a function with + /// a `StructReturn` pointer argument to also return that pointer in a register. + StructReturn, + + /// The link register. + /// + /// Most RISC architectures implement calls by saving the return address in a designated + /// register rather than pushing it on the stack. This is represented with a `Link` argument. + /// + /// Similarly, some return instructions expect the return address in a register represented as + /// a `Link` return value. + Link, + + /// The frame pointer. + /// + /// This indicates the frame pointer register which has a special meaning in some ABIs. + /// + /// The frame pointer appears as an argument and as a return value since it is a callee-saved + /// register. + FramePointer, + + /// A callee-saved register. + /// + /// Some calling conventions have registers that must be saved by the callee. These registers + /// are represented as `CalleeSaved` arguments and return values. + CalleeSaved, +} + +/// Text format names of the `ArgumentPurpose` variants. +static PURPOSE_NAMES: [&'static str; 5] = ["normal", "sret", "link", "fp", "csr"]; + +impl fmt::Display for ArgumentPurpose { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(PURPOSE_NAMES[*self as usize]) + } +} + +impl FromStr for ArgumentPurpose { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "normal" => Ok(ArgumentPurpose::Normal), + "sret" => Ok(ArgumentPurpose::StructReturn), + "link" => Ok(ArgumentPurpose::Link), + "fp" => Ok(ArgumentPurpose::FramePointer), + "csr" => Ok(ArgumentPurpose::CalleeSaved), + _ => Err(()), + } + } +} + /// An external function. /// /// Information about a function that can be called directly with a direct `call` instruction. @@ -209,8 +279,21 @@ mod tests { assert_eq!(t.to_string(), "i32"); t.extension = ArgumentExtension::Uext; assert_eq!(t.to_string(), "i32 uext"); - t.inreg = true; - assert_eq!(t.to_string(), "i32 uext inreg"); + t.purpose = ArgumentPurpose::StructReturn; + assert_eq!(t.to_string(), "i32 uext sret"); + } + + #[test] + fn argument_purpose() { + let all_purpose = [ArgumentPurpose::Normal, + ArgumentPurpose::StructReturn, + ArgumentPurpose::Link, + ArgumentPurpose::FramePointer, + ArgumentPurpose::CalleeSaved]; + for (&e, &n) in all_purpose.iter().zip(PURPOSE_NAMES.iter()) { + assert_eq!(e.to_string(), n); + assert_eq!(Ok(e), n.parse()); + } } #[test] diff --git a/lib/cretonne/src/ir/mod.rs b/lib/cretonne/src/ir/mod.rs index 488b4dab32..49617165e9 100644 --- a/lib/cretonne/src/ir/mod.rs +++ b/lib/cretonne/src/ir/mod.rs @@ -18,7 +18,7 @@ mod progpoint; mod valueloc; pub use ir::funcname::FunctionName; -pub use ir::extfunc::{Signature, ArgumentType, ArgumentExtension, ExtFuncData}; +pub use ir::extfunc::{Signature, ArgumentType, ArgumentExtension, ArgumentPurpose, ExtFuncData}; pub use ir::types::Type; pub use ir::entities::{Ebb, Inst, Value, StackSlot, JumpTable, FuncRef, SigRef}; pub use ir::instructions::{Opcode, InstructionData, VariableArgs, ValueList, ValueListPool}; diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index f133732e8e..f96d60518a 100644 --- a/lib/reader/src/parser.rs +++ b/lib/reader/src/parser.rs @@ -767,8 +767,13 @@ impl<'a> Parser<'a> { match s { "uext" => arg.extension = ArgumentExtension::Uext, "sext" => arg.extension = ArgumentExtension::Sext, - "inreg" => arg.inreg = true, - _ => break, + _ => { + if let Ok(purpose) = s.parse() { + arg.purpose = purpose; + } else { + break; + } + } } self.consume(); } @@ -1672,7 +1677,7 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { use super::*; - use cretonne::ir::ArgumentExtension; + use cretonne::ir::{ArgumentExtension, ArgumentPurpose}; use cretonne::ir::types; use cretonne::ir::entities::AnyEntity; use testfile::{Details, Comment}; @@ -1685,7 +1690,7 @@ mod tests { let arg = p.parse_argument_type(None).unwrap(); assert_eq!(arg.value_type, types::I32); assert_eq!(arg.extension, ArgumentExtension::Sext); - assert_eq!(arg.inreg, false); + assert_eq!(arg.purpose, ArgumentPurpose::Normal); let Error { location, message } = p.parse_argument_type(None).unwrap_err(); assert_eq!(location.line_number, 1); assert_eq!(message, "expected argument type"); @@ -1721,11 +1726,11 @@ mod tests { assert_eq!(sig.argument_types.len(), 0); assert_eq!(sig.return_types.len(), 0); - let sig2 = Parser::new("(i8 inreg uext, f32, f64) -> i32 sext, f64") + let sig2 = Parser::new("(i8 uext, f32, f64, i32 sret) -> i32 sext, f64") .parse_signature(None) .unwrap(); assert_eq!(sig2.to_string(), - "(i8 uext inreg, f32, f64) -> i32 sext, f64"); + "(i8 uext, f32, f64, i32 sret) -> i32 sext, f64"); // `void` is not recognized as a type by the lexer. It should not appear in files. assert_eq!(Parser::new("() -> void")