Allow for special purpose function arguments and return values.

Enumerate a set of special purposes for function arguments that general
purpose code needs to know about. Some of these argument purposes will
only appear in the signature of the current function, representing
things the prologue and epilogues need to know about like the link
register and callee-saved registers.

Get rid of the 'inreg' argument flag. Arguments can be pre-assigned to a
specific register instead.
This commit is contained in:
Jakob Stoklund Olesen
2017-04-17 15:04:00 -07:00
parent d3235eb81f
commit 7e9bdcf059
6 changed files with 116 additions and 18 deletions

View File

@@ -42,8 +42,7 @@ class CretonneLexer(RegexLexer):
(r'[-+]?(\d+\.\d+([eE]\d+)?|s?NaN|Inf)', Number.Float), (r'[-+]?(\d+\.\d+([eE]\d+)?|s?NaN|Inf)', Number.Float),
(r'[-+]?\d+', Number.Integer), (r'[-+]?\d+', Number.Integer),
# Known attributes. # Known attributes.
(keywords('align', 'aligntrap', 'uext', 'sext', 'inreg'), (keywords('uext', 'sext'), Name.Attribute),
Name.Attribute),
# Well known value types. # Well known value types.
(r'\b(b\d+|i\d+|f32|f64)(x\d+)?\b', Keyword.Type), (r'\b(b\d+|i\d+|f32|f64)(x\d+)?\b', Keyword.Type),
# v<nn> = value # v<nn> = value

View File

@@ -364,8 +364,9 @@ calling convention:
signature : "(" [arglist] ")" ["->" retlist] [call_conv] signature : "(" [arglist] ")" ["->" retlist] [call_conv]
arglist : arg { "," arg } arglist : arg { "," arg }
retlist : arglist retlist : arglist
arg : type { flag } arg : type [argext] [argspecial]
flag : "uext" | "sext" | "inreg" argext : "uext" | "sext"
argspecial: "sret" | "link" | "fp" | "csr"
callconv : `string` callconv : `string`
Arguments and return values have flags whose meaning is mostly target Arguments and return values have flags whose meaning is mostly target

View File

@@ -68,3 +68,13 @@ ebb0(v0: i64):
; check: call_indirect $sig0, $v1($v0) ; check: call_indirect $sig0, $v1($v0)
; check: $v3, $v4 = call_indirect $sig2, $v1() ; check: $v3, $v4 = call_indirect $sig2, $v1()
; check: return ; 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: }

View File

@@ -9,6 +9,7 @@ use ir::{Type, FunctionName, SigRef, ArgumentLoc};
use isa::RegInfo; use isa::RegInfo;
use std::cmp; use std::cmp;
use std::fmt; use std::fmt;
use std::str::FromStr;
/// Function signature. /// Function signature.
/// ///
@@ -111,10 +112,10 @@ impl fmt::Display for Signature {
pub struct ArgumentType { pub struct ArgumentType {
/// Type of the argument value. /// Type of the argument value.
pub value_type: Type, pub value_type: Type,
/// Special purpose of argument, or `Normal`.
pub purpose: ArgumentPurpose,
/// Method for extending argument to a full register. /// Method for extending argument to a full register.
pub extension: ArgumentExtension, 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 /// ABI-specific location of this argument, or `Unassigned` for arguments that have not yet
/// been legalized. /// been legalized.
@@ -127,7 +128,7 @@ impl ArgumentType {
ArgumentType { ArgumentType {
value_type: vt, value_type: vt,
extension: ArgumentExtension::None, extension: ArgumentExtension::None,
inreg: false, purpose: ArgumentPurpose::Normal,
location: Default::default(), location: Default::default(),
} }
} }
@@ -149,8 +150,8 @@ impl<'a> fmt::Display for DisplayArgumentType<'a> {
ArgumentExtension::Uext => write!(f, " uext")?, ArgumentExtension::Uext => write!(f, " uext")?,
ArgumentExtension::Sext => write!(f, " sext")?, ArgumentExtension::Sext => write!(f, " sext")?,
} }
if self.0.inreg { if self.0.purpose != ArgumentPurpose::Normal {
write!(f, " inreg")?; write!(f, " {}", self.0.purpose)?;
} }
if self.0.location.is_assigned() { if self.0.location.is_assigned() {
@@ -181,6 +182,75 @@ pub enum ArgumentExtension {
Sext, 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<ArgumentPurpose, ()> {
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. /// An external function.
/// ///
/// Information about a function that can be called directly with a direct `call` instruction. /// 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"); assert_eq!(t.to_string(), "i32");
t.extension = ArgumentExtension::Uext; t.extension = ArgumentExtension::Uext;
assert_eq!(t.to_string(), "i32 uext"); assert_eq!(t.to_string(), "i32 uext");
t.inreg = true; t.purpose = ArgumentPurpose::StructReturn;
assert_eq!(t.to_string(), "i32 uext inreg"); 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] #[test]

View File

@@ -18,7 +18,7 @@ mod progpoint;
mod valueloc; mod valueloc;
pub use ir::funcname::FunctionName; 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::types::Type;
pub use ir::entities::{Ebb, Inst, Value, StackSlot, JumpTable, FuncRef, SigRef}; pub use ir::entities::{Ebb, Inst, Value, StackSlot, JumpTable, FuncRef, SigRef};
pub use ir::instructions::{Opcode, InstructionData, VariableArgs, ValueList, ValueListPool}; pub use ir::instructions::{Opcode, InstructionData, VariableArgs, ValueList, ValueListPool};

View File

@@ -767,8 +767,13 @@ 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,
"inreg" => arg.inreg = true, _ => {
_ => break, if let Ok(purpose) = s.parse() {
arg.purpose = purpose;
} else {
break;
}
}
} }
self.consume(); self.consume();
} }
@@ -1672,7 +1677,7 @@ impl<'a> Parser<'a> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use cretonne::ir::ArgumentExtension; use cretonne::ir::{ArgumentExtension, ArgumentPurpose};
use cretonne::ir::types; use cretonne::ir::types;
use cretonne::ir::entities::AnyEntity; use cretonne::ir::entities::AnyEntity;
use testfile::{Details, Comment}; use testfile::{Details, Comment};
@@ -1685,7 +1690,7 @@ mod tests {
let arg = p.parse_argument_type(None).unwrap(); let arg = p.parse_argument_type(None).unwrap();
assert_eq!(arg.value_type, types::I32); assert_eq!(arg.value_type, types::I32);
assert_eq!(arg.extension, ArgumentExtension::Sext); 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(); let Error { location, message } = p.parse_argument_type(None).unwrap_err();
assert_eq!(location.line_number, 1); assert_eq!(location.line_number, 1);
assert_eq!(message, "expected argument type"); assert_eq!(message, "expected argument type");
@@ -1721,11 +1726,11 @@ mod tests {
assert_eq!(sig.argument_types.len(), 0); assert_eq!(sig.argument_types.len(), 0);
assert_eq!(sig.return_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) .parse_signature(None)
.unwrap(); .unwrap();
assert_eq!(sig2.to_string(), 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. // `void` is not recognized as a type by the lexer. It should not appear in files.
assert_eq!(Parser::new("() -> void") assert_eq!(Parser::new("() -> void")