moved crates in lib/ to src/, renamed crates, modified some files' text (#660)
moved crates in lib/ to src/, renamed crates, modified some files' text (#660)
This commit is contained in:
405
cranelift/codegen/src/ir/extfunc.rs
Normal file
405
cranelift/codegen/src/ir/extfunc.rs
Normal file
@@ -0,0 +1,405 @@
|
||||
//! External function calls.
|
||||
//!
|
||||
//! To a Cranelift function, all functions are "external". Directly called functions must be
|
||||
//! declared in the preamble, and all function calls must have a signature.
|
||||
//!
|
||||
//! This module declares the data types used to represent external functions and call signatures.
|
||||
|
||||
use crate::ir::{ArgumentLoc, ExternalName, SigRef, Type};
|
||||
use crate::isa::{CallConv, RegInfo, RegUnit};
|
||||
use core::fmt;
|
||||
use core::str::FromStr;
|
||||
use std::vec::Vec;
|
||||
|
||||
/// Function signature.
|
||||
///
|
||||
/// The function signature describes the types of formal parameters and return values along with
|
||||
/// other details that are needed to call a function correctly.
|
||||
///
|
||||
/// A signature can optionally include ISA-specific ABI information which specifies exactly how
|
||||
/// arguments and return values are passed.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Signature {
|
||||
/// The arguments passed to the function.
|
||||
pub params: Vec<AbiParam>,
|
||||
/// Values returned from the function.
|
||||
pub returns: Vec<AbiParam>,
|
||||
|
||||
/// Calling convention.
|
||||
pub call_conv: CallConv,
|
||||
}
|
||||
|
||||
impl Signature {
|
||||
/// Create a new blank signature.
|
||||
pub fn new(call_conv: CallConv) -> Self {
|
||||
Self {
|
||||
params: Vec::new(),
|
||||
returns: Vec::new(),
|
||||
call_conv,
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear the signature so it is identical to a fresh one returned by `new()`.
|
||||
pub fn clear(&mut self, call_conv: CallConv) {
|
||||
self.params.clear();
|
||||
self.returns.clear();
|
||||
self.call_conv = call_conv;
|
||||
}
|
||||
|
||||
/// Return an object that can display `self` with correct register names.
|
||||
pub fn display<'a, R: Into<Option<&'a RegInfo>>>(&'a self, regs: R) -> DisplaySignature<'a> {
|
||||
DisplaySignature(self, regs.into())
|
||||
}
|
||||
|
||||
/// Find the index of a presumed unique special-purpose parameter.
|
||||
pub fn special_param_index(&self, purpose: ArgumentPurpose) -> Option<usize> {
|
||||
self.params.iter().rposition(|arg| arg.purpose == purpose)
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper type capable of displaying a `Signature` with correct register names.
|
||||
pub struct DisplaySignature<'a>(&'a Signature, Option<&'a RegInfo>);
|
||||
|
||||
fn write_list(f: &mut fmt::Formatter, args: &[AbiParam], regs: Option<&RegInfo>) -> fmt::Result {
|
||||
match args.split_first() {
|
||||
None => {}
|
||||
Some((first, rest)) => {
|
||||
write!(f, "{}", first.display(regs))?;
|
||||
for arg in rest {
|
||||
write!(f, ", {}", arg.display(regs))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for DisplaySignature<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "(")?;
|
||||
write_list(f, &self.0.params, self.1)?;
|
||||
write!(f, ")")?;
|
||||
if !self.0.returns.is_empty() {
|
||||
write!(f, " -> ")?;
|
||||
write_list(f, &self.0.returns, self.1)?;
|
||||
}
|
||||
write!(f, " {}", self.0.call_conv)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Signature {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.display(None).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Function parameter or return value descriptor.
|
||||
///
|
||||
/// 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, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct AbiParam {
|
||||
/// 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,
|
||||
|
||||
/// ABI-specific location of this argument, or `Unassigned` for arguments that have not yet
|
||||
/// been legalized.
|
||||
pub location: ArgumentLoc,
|
||||
}
|
||||
|
||||
impl AbiParam {
|
||||
/// Create a parameter with default flags.
|
||||
pub fn new(vt: Type) -> Self {
|
||||
Self {
|
||||
value_type: vt,
|
||||
extension: ArgumentExtension::None,
|
||||
purpose: ArgumentPurpose::Normal,
|
||||
location: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a special-purpose parameter that is not (yet) bound to a specific register.
|
||||
pub fn special(vt: Type, purpose: ArgumentPurpose) -> Self {
|
||||
Self {
|
||||
value_type: vt,
|
||||
extension: ArgumentExtension::None,
|
||||
purpose,
|
||||
location: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a parameter for a special-purpose register.
|
||||
pub fn special_reg(vt: Type, purpose: ArgumentPurpose, regunit: RegUnit) -> Self {
|
||||
Self {
|
||||
value_type: vt,
|
||||
extension: ArgumentExtension::None,
|
||||
purpose,
|
||||
location: ArgumentLoc::Reg(regunit),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert `self` to a parameter with the `uext` flag set.
|
||||
pub fn uext(self) -> Self {
|
||||
debug_assert!(self.value_type.is_int(), "uext on {} arg", self.value_type);
|
||||
Self {
|
||||
extension: ArgumentExtension::Uext,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert `self` to a parameter type with the `sext` flag set.
|
||||
pub fn sext(self) -> Self {
|
||||
debug_assert!(self.value_type.is_int(), "sext on {} arg", self.value_type);
|
||||
Self {
|
||||
extension: ArgumentExtension::Sext,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an object that can display `self` with correct register names.
|
||||
pub fn display<'a, R: Into<Option<&'a RegInfo>>>(&'a self, regs: R) -> DisplayAbiParam<'a> {
|
||||
DisplayAbiParam(self, regs.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper type capable of displaying a `AbiParam` with correct register names.
|
||||
pub struct DisplayAbiParam<'a>(&'a AbiParam, Option<&'a RegInfo>);
|
||||
|
||||
impl<'a> fmt::Display for DisplayAbiParam<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.0.value_type)?;
|
||||
match self.0.extension {
|
||||
ArgumentExtension::None => {}
|
||||
ArgumentExtension::Uext => write!(f, " uext")?,
|
||||
ArgumentExtension::Sext => write!(f, " sext")?,
|
||||
}
|
||||
if self.0.purpose != ArgumentPurpose::Normal {
|
||||
write!(f, " {}", self.0.purpose)?;
|
||||
}
|
||||
|
||||
if self.0.location.is_assigned() {
|
||||
write!(f, " [{}]", self.0.location.display(self.1))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for AbiParam {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.display(None).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Function argument extension options.
|
||||
///
|
||||
/// On some architectures, small integer function arguments are extended to the width of a
|
||||
/// general-purpose register.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum ArgumentExtension {
|
||||
/// No extension, high bits are indeterminate.
|
||||
None,
|
||||
/// Unsigned extension: high bits in register are 0.
|
||||
Uext,
|
||||
/// Signed extension: high bits in register replicate sign bit.
|
||||
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, Hash)]
|
||||
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,
|
||||
|
||||
/// A VM context pointer.
|
||||
///
|
||||
/// This is a pointer to a context struct containing details about the current sandbox. It is
|
||||
/// used as a base pointer for `vmctx` global values.
|
||||
VMContext,
|
||||
|
||||
/// A signature identifier.
|
||||
///
|
||||
/// This is a special-purpose argument used to identify the calling convention expected by the
|
||||
/// caller in an indirect call. The callee can verify that the expected signature ID matches.
|
||||
SignatureId,
|
||||
|
||||
/// A stack limit pointer.
|
||||
///
|
||||
/// This is a pointer to a stack limit. It is used to check the current stack pointer
|
||||
/// against. Can only appear once in a signature.
|
||||
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 {
|
||||
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<Self, ()> {
|
||||
match s {
|
||||
"normal" => Ok(ArgumentPurpose::Normal),
|
||||
"sret" => Ok(ArgumentPurpose::StructReturn),
|
||||
"link" => Ok(ArgumentPurpose::Link),
|
||||
"fp" => Ok(ArgumentPurpose::FramePointer),
|
||||
"csr" => Ok(ArgumentPurpose::CalleeSaved),
|
||||
"vmctx" => Ok(ArgumentPurpose::VMContext),
|
||||
"sigid" => Ok(ArgumentPurpose::SignatureId),
|
||||
"stack_limit" => Ok(ArgumentPurpose::StackLimit),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An external function.
|
||||
///
|
||||
/// Information about a function that can be called directly with a direct `call` instruction.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ExtFuncData {
|
||||
/// Name of the external function.
|
||||
pub name: ExternalName,
|
||||
/// Call signature of function.
|
||||
pub signature: SigRef,
|
||||
/// Will this function be defined nearby, such that it will always be a certain distance away,
|
||||
/// after linking? If so, references to it can avoid going through a GOT or PLT. Note that
|
||||
/// symbols meant to be preemptible cannot be considered colocated.
|
||||
pub colocated: bool,
|
||||
}
|
||||
|
||||
impl fmt::Display for ExtFuncData {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.colocated {
|
||||
write!(f, "colocated ")?;
|
||||
}
|
||||
write!(f, "{} {}", self.name, self.signature)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::ir::types::{B8, F32, I32};
|
||||
use std::string::ToString;
|
||||
|
||||
#[test]
|
||||
fn argument_type() {
|
||||
let t = AbiParam::new(I32);
|
||||
assert_eq!(t.to_string(), "i32");
|
||||
let mut t = t.uext();
|
||||
assert_eq!(t.to_string(), "i32 uext");
|
||||
assert_eq!(t.sext().to_string(), "i32 sext");
|
||||
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,
|
||||
ArgumentPurpose::VMContext,
|
||||
ArgumentPurpose::SignatureId,
|
||||
ArgumentPurpose::StackLimit,
|
||||
];
|
||||
for (&e, &n) in all_purpose.iter().zip(PURPOSE_NAMES.iter()) {
|
||||
assert_eq!(e.to_string(), n);
|
||||
assert_eq!(Ok(e), n.parse());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn call_conv() {
|
||||
for &cc in &[
|
||||
CallConv::Fast,
|
||||
CallConv::Cold,
|
||||
CallConv::SystemV,
|
||||
CallConv::WindowsFastcall,
|
||||
CallConv::Baldrdash,
|
||||
] {
|
||||
assert_eq!(Ok(cc), cc.to_string().parse())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signatures() {
|
||||
let mut sig = Signature::new(CallConv::Baldrdash);
|
||||
assert_eq!(sig.to_string(), "() baldrdash");
|
||||
sig.params.push(AbiParam::new(I32));
|
||||
assert_eq!(sig.to_string(), "(i32) baldrdash");
|
||||
sig.returns.push(AbiParam::new(F32));
|
||||
assert_eq!(sig.to_string(), "(i32) -> f32 baldrdash");
|
||||
sig.params.push(AbiParam::new(I32.by(4).unwrap()));
|
||||
assert_eq!(sig.to_string(), "(i32, i32x4) -> f32 baldrdash");
|
||||
sig.returns.push(AbiParam::new(B8));
|
||||
assert_eq!(sig.to_string(), "(i32, i32x4) -> f32, b8 baldrdash");
|
||||
|
||||
// Order does not matter.
|
||||
sig.params[0].location = ArgumentLoc::Stack(24);
|
||||
sig.params[1].location = ArgumentLoc::Stack(8);
|
||||
|
||||
// Writing ABI-annotated signatures.
|
||||
assert_eq!(
|
||||
sig.to_string(),
|
||||
"(i32 [24], i32x4 [8]) -> f32, b8 baldrdash"
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user