Path: blob/main/cranelift/codegen/src/ir/extfunc.rs
1693 views
//! External function calls.1//!2//! To a Cranelift function, all functions are "external". Directly called functions must be3//! declared in the preamble, and all function calls must have a signature.4//!5//! This module declares the data types used to represent external functions and call signatures.67use crate::ir::{ExternalName, SigRef, Type};8use crate::isa::CallConv;9use alloc::vec::Vec;10use core::fmt;11use core::str::FromStr;12#[cfg(feature = "enable-serde")]13use serde_derive::{Deserialize, Serialize};1415use super::function::FunctionParameters;1617/// Function signature.18///19/// The function signature describes the types of formal parameters and return values along with20/// other details that are needed to call a function correctly.21///22/// A signature can optionally include ISA-specific ABI information which specifies exactly how23/// arguments and return values are passed.24#[derive(Clone, Debug, PartialEq, Eq, Hash)]25#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]26pub struct Signature {27/// The arguments passed to the function.28pub params: Vec<AbiParam>,29/// Values returned from the function.30pub returns: Vec<AbiParam>,3132/// Calling convention.33pub call_conv: CallConv,34}3536impl Signature {37/// Create a new blank signature.38pub fn new(call_conv: CallConv) -> Self {39Self {40params: Vec::new(),41returns: Vec::new(),42call_conv,43}44}4546/// Clear the signature so it is identical to a fresh one returned by `new()`.47pub fn clear(&mut self, call_conv: CallConv) {48self.params.clear();49self.returns.clear();50self.call_conv = call_conv;51}5253/// Find the index of a presumed unique special-purpose parameter.54pub fn special_param_index(&self, purpose: ArgumentPurpose) -> Option<usize> {55self.params.iter().rposition(|arg| arg.purpose == purpose)56}5758/// Find the index of a presumed unique special-purpose parameter.59pub fn special_return_index(&self, purpose: ArgumentPurpose) -> Option<usize> {60self.returns.iter().rposition(|arg| arg.purpose == purpose)61}6263/// Does this signature have a parameter whose `ArgumentPurpose` is64/// `purpose`?65pub fn uses_special_param(&self, purpose: ArgumentPurpose) -> bool {66self.special_param_index(purpose).is_some()67}6869/// Does this signature have a return whose `ArgumentPurpose` is `purpose`?70pub fn uses_special_return(&self, purpose: ArgumentPurpose) -> bool {71self.special_return_index(purpose).is_some()72}7374/// How many special parameters does this function have?75pub fn num_special_params(&self) -> usize {76self.params77.iter()78.filter(|p| p.purpose != ArgumentPurpose::Normal)79.count()80}8182/// How many special returns does this function have?83pub fn num_special_returns(&self) -> usize {84self.returns85.iter()86.filter(|r| r.purpose != ArgumentPurpose::Normal)87.count()88}8990/// Does this signature take an struct return pointer parameter?91pub fn uses_struct_return_param(&self) -> bool {92self.uses_special_param(ArgumentPurpose::StructReturn)93}9495/// Does this return more than one normal value? (Pre-struct return96/// legalization)97pub fn is_multi_return(&self) -> bool {98self.returns99.iter()100.filter(|r| r.purpose == ArgumentPurpose::Normal)101.count()102> 1103}104}105106fn write_list(f: &mut fmt::Formatter, args: &[AbiParam]) -> fmt::Result {107match args.split_first() {108None => {}109Some((first, rest)) => {110write!(f, "{first}")?;111for arg in rest {112write!(f, ", {arg}")?;113}114}115}116Ok(())117}118119impl fmt::Display for Signature {120fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {121write!(f, "(")?;122write_list(f, &self.params)?;123write!(f, ")")?;124if !self.returns.is_empty() {125write!(f, " -> ")?;126write_list(f, &self.returns)?;127}128write!(f, " {}", self.call_conv)129}130}131132/// Function parameter or return value descriptor.133///134/// This describes the value type being passed to or from a function along with flags that affect135/// how the argument is passed.136#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]137#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]138pub struct AbiParam {139/// Type of the argument value.140pub value_type: Type,141/// Special purpose of argument, or `Normal`.142pub purpose: ArgumentPurpose,143/// Method for extending argument to a full register.144pub extension: ArgumentExtension,145}146147impl AbiParam {148/// Create a parameter with default flags.149pub fn new(vt: Type) -> Self {150Self {151value_type: vt,152extension: ArgumentExtension::None,153purpose: ArgumentPurpose::Normal,154}155}156157/// Create a special-purpose parameter that is not (yet) bound to a specific register.158pub fn special(vt: Type, purpose: ArgumentPurpose) -> Self {159Self {160value_type: vt,161extension: ArgumentExtension::None,162purpose,163}164}165166/// Convert `self` to a parameter with the `uext` flag set.167pub fn uext(self) -> Self {168debug_assert!(self.value_type.is_int(), "uext on {} arg", self.value_type);169Self {170extension: ArgumentExtension::Uext,171..self172}173}174175/// Convert `self` to a parameter type with the `sext` flag set.176pub fn sext(self) -> Self {177debug_assert!(self.value_type.is_int(), "sext on {} arg", self.value_type);178Self {179extension: ArgumentExtension::Sext,180..self181}182}183}184185impl fmt::Display for AbiParam {186fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {187write!(f, "{}", self.value_type)?;188match self.extension {189ArgumentExtension::None => {}190ArgumentExtension::Uext => write!(f, " uext")?,191ArgumentExtension::Sext => write!(f, " sext")?,192}193if self.purpose != ArgumentPurpose::Normal {194write!(f, " {}", self.purpose)?;195}196Ok(())197}198}199200/// Function argument extension options.201///202/// On some architectures, small integer function arguments and/or return values are extended to203/// the width of a general-purpose register.204///205/// This attribute specifies how an argument or return value should be extended *if the platform206/// and ABI require it*. Because the frontend (CLIF generator) does not know anything about the207/// particulars of the target's ABI, and the CLIF should be platform-independent, these attributes208/// specify *how* to extend (according to the signedness of the original program) rather than209/// *whether* to extend.210#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]211#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]212pub enum ArgumentExtension {213/// No extension, high bits are indeterminate.214None,215/// Unsigned extension: high bits in register are 0.216Uext,217/// Signed extension: high bits in register replicate sign bit.218Sext,219}220221/// The special purpose of a function argument.222///223/// Function arguments and return values are used to pass user program values between functions,224/// but they are also used to represent special registers with significance to the ABI such as225/// frame pointers and callee-saved registers.226///227/// The argument purpose is used to indicate any special meaning of an argument or return value.228#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]229#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]230pub enum ArgumentPurpose {231/// A normal user program value passed to or from a function.232Normal,233234/// A C struct passed as argument.235///236/// Note that this should only be used when interacting with code following237/// a C ABI which is expecting a struct passed *by value*.238StructArgument(239/// The size, in bytes, of the struct.240u32,241),242243/// Struct return pointer.244///245/// When a function needs to return more data than will fit in registers, the caller passes a246/// pointer to a memory location where the return value can be written. In some ABIs, this247/// struct return pointer is passed in a specific register.248///249/// This argument kind can also appear as a return value for ABIs that require a function with250/// a `StructReturn` pointer argument to also return that pointer in a register.251StructReturn,252253/// A VM context pointer.254///255/// This is a pointer to a context struct containing details about the current sandbox. It is256/// used as a base pointer for `vmctx` global values.257VMContext,258}259260impl fmt::Display for ArgumentPurpose {261fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {262f.write_str(match self {263Self::Normal => "normal",264Self::StructArgument(size) => return write!(f, "sarg({size})"),265Self::StructReturn => "sret",266Self::VMContext => "vmctx",267})268}269}270271impl FromStr for ArgumentPurpose {272type Err = ();273fn from_str(s: &str) -> Result<Self, ()> {274match s {275"normal" => Ok(Self::Normal),276"sret" => Ok(Self::StructReturn),277"vmctx" => Ok(Self::VMContext),278_ if s.starts_with("sarg(") => {279if !s.ends_with(")") {280return Err(());281}282// Parse 'sarg(size)'283let size: u32 = s["sarg(".len()..s.len() - 1].parse().map_err(|_| ())?;284Ok(Self::StructArgument(size))285}286_ => Err(()),287}288}289}290291/// An external function.292///293/// Information about a function that can be called directly with a direct `call` instruction.294#[derive(Clone, Debug, PartialEq, Hash)]295#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]296pub struct ExtFuncData {297/// Name of the external function.298pub name: ExternalName,299/// Call signature of function.300pub signature: SigRef,301/// Will this function be defined nearby, such that it will always be a certain distance away,302/// after linking? If so, references to it can avoid going through a GOT or PLT. Note that303/// symbols meant to be preemptible cannot be considered colocated.304///305/// If `true`, some backends may use relocation forms that have limited range. The exact306/// distance depends on the code model in use. Currently on AArch64, for example, Cranelift307/// uses a custom code model supporting up to +/- 128MB displacements. If it is unknown how308/// far away the target will be, it is best not to set the `colocated` flag; in general, this309/// flag is best used when the target is known to be in the same unit of code generation, such310/// as a Wasm module.311///312/// See the documentation for `RelocDistance` for more details. A `colocated` flag value of313/// `true` implies `RelocDistance::Near`.314pub colocated: bool,315}316317impl ExtFuncData {318/// Returns a displayable version of the `ExtFuncData`, with or without extra context to319/// prettify the output.320pub fn display<'a>(321&'a self,322params: Option<&'a FunctionParameters>,323) -> DisplayableExtFuncData<'a> {324DisplayableExtFuncData {325ext_func: self,326params,327}328}329}330331/// A displayable `ExtFuncData`, with extra context to prettify the output.332pub struct DisplayableExtFuncData<'a> {333ext_func: &'a ExtFuncData,334params: Option<&'a FunctionParameters>,335}336337impl<'a> fmt::Display for DisplayableExtFuncData<'a> {338fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {339if self.ext_func.colocated {340write!(f, "colocated ")?;341}342write!(343f,344"{} {}",345self.ext_func.name.display(self.params),346self.ext_func.signature347)348}349}350351#[cfg(test)]352mod tests {353use super::*;354use crate::ir::types::{F32, I8, I32};355use alloc::string::ToString;356357#[test]358fn argument_type() {359let t = AbiParam::new(I32);360assert_eq!(t.to_string(), "i32");361let mut t = t.uext();362assert_eq!(t.to_string(), "i32 uext");363assert_eq!(t.sext().to_string(), "i32 sext");364t.purpose = ArgumentPurpose::StructReturn;365assert_eq!(t.to_string(), "i32 uext sret");366}367368#[test]369fn argument_purpose() {370let all_purpose = [371(ArgumentPurpose::Normal, "normal"),372(ArgumentPurpose::StructReturn, "sret"),373(ArgumentPurpose::VMContext, "vmctx"),374(ArgumentPurpose::StructArgument(42), "sarg(42)"),375];376for &(e, n) in &all_purpose {377assert_eq!(e.to_string(), n);378assert_eq!(Ok(e), n.parse());379}380}381382#[test]383fn call_conv() {384for &cc in &[385CallConv::Fast,386CallConv::Cold,387CallConv::SystemV,388CallConv::WindowsFastcall,389] {390assert_eq!(Ok(cc), cc.to_string().parse())391}392}393394#[test]395fn signatures() {396let mut sig = Signature::new(CallConv::WindowsFastcall);397assert_eq!(sig.to_string(), "() windows_fastcall");398sig.params.push(AbiParam::new(I32));399assert_eq!(sig.to_string(), "(i32) windows_fastcall");400sig.returns.push(AbiParam::new(F32));401assert_eq!(sig.to_string(), "(i32) -> f32 windows_fastcall");402sig.params.push(AbiParam::new(I32.by(4).unwrap()));403assert_eq!(sig.to_string(), "(i32, i32x4) -> f32 windows_fastcall");404sig.returns.push(AbiParam::new(I8));405assert_eq!(sig.to_string(), "(i32, i32x4) -> f32, i8 windows_fastcall");406}407}408409410