cranelift_codegen/ir/
extfunc.rs

1//! External function calls.
2//!
3//! To a Cranelift function, all functions are "external". Directly called functions must be
4//! declared in the preamble, and all function calls must have a signature.
5//!
6//! This module declares the data types used to represent external functions and call signatures.
7
8use crate::ir::{ExternalName, SigRef, Type};
9use crate::isa::CallConv;
10use crate::machinst::RelocDistance;
11use alloc::vec::Vec;
12use core::fmt;
13use core::str::FromStr;
14#[cfg(feature = "enable-serde")]
15use serde::{Deserialize, Serialize};
16
17use super::function::FunctionParameters;
18
19/// Function signature.
20///
21/// The function signature describes the types of formal parameters and return values along with
22/// other details that are needed to call a function correctly.
23///
24/// A signature can optionally include ISA-specific ABI information which specifies exactly how
25/// arguments and return values are passed.
26#[derive(Clone, Debug, PartialEq, Eq, Hash)]
27#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
28pub struct Signature {
29    /// The arguments passed to the function.
30    pub params: Vec<AbiParam>,
31    /// Values returned from the function.
32    pub returns: Vec<AbiParam>,
33
34    /// Calling convention.
35    pub call_conv: CallConv,
36}
37
38impl Signature {
39    /// Create a new blank signature.
40    pub fn new(call_conv: CallConv) -> Self {
41        Self {
42            params: Vec::new(),
43            returns: Vec::new(),
44            call_conv,
45        }
46    }
47
48    /// Clear the signature so it is identical to a fresh one returned by `new()`.
49    pub fn clear(&mut self, call_conv: CallConv) {
50        self.params.clear();
51        self.returns.clear();
52        self.call_conv = call_conv;
53    }
54
55    /// Find the index of a presumed unique special-purpose parameter.
56    pub fn special_param_index(&self, purpose: ArgumentPurpose) -> Option<usize> {
57        self.params.iter().rposition(|arg| arg.purpose == purpose)
58    }
59
60    /// Find the index of a presumed unique special-purpose parameter.
61    pub fn special_return_index(&self, purpose: ArgumentPurpose) -> Option<usize> {
62        self.returns.iter().rposition(|arg| arg.purpose == purpose)
63    }
64
65    /// Does this signature have a parameter whose `ArgumentPurpose` is
66    /// `purpose`?
67    pub fn uses_special_param(&self, purpose: ArgumentPurpose) -> bool {
68        self.special_param_index(purpose).is_some()
69    }
70
71    /// Does this signature have a return whose `ArgumentPurpose` is `purpose`?
72    pub fn uses_special_return(&self, purpose: ArgumentPurpose) -> bool {
73        self.special_return_index(purpose).is_some()
74    }
75
76    /// How many special parameters does this function have?
77    pub fn num_special_params(&self) -> usize {
78        self.params
79            .iter()
80            .filter(|p| p.purpose != ArgumentPurpose::Normal)
81            .count()
82    }
83
84    /// How many special returns does this function have?
85    pub fn num_special_returns(&self) -> usize {
86        self.returns
87            .iter()
88            .filter(|r| r.purpose != ArgumentPurpose::Normal)
89            .count()
90    }
91
92    /// Does this signature take an struct return pointer parameter?
93    pub fn uses_struct_return_param(&self) -> bool {
94        self.uses_special_param(ArgumentPurpose::StructReturn)
95    }
96
97    /// Does this return more than one normal value? (Pre-struct return
98    /// legalization)
99    pub fn is_multi_return(&self) -> bool {
100        self.returns
101            .iter()
102            .filter(|r| r.purpose == ArgumentPurpose::Normal)
103            .count()
104            > 1
105    }
106}
107
108fn write_list(f: &mut fmt::Formatter, args: &[AbiParam]) -> fmt::Result {
109    match args.split_first() {
110        None => {}
111        Some((first, rest)) => {
112            write!(f, "{}", first)?;
113            for arg in rest {
114                write!(f, ", {}", arg)?;
115            }
116        }
117    }
118    Ok(())
119}
120
121impl fmt::Display for Signature {
122    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
123        write!(f, "(")?;
124        write_list(f, &self.params)?;
125        write!(f, ")")?;
126        if !self.returns.is_empty() {
127            write!(f, " -> ")?;
128            write_list(f, &self.returns)?;
129        }
130        write!(f, " {}", self.call_conv)
131    }
132}
133
134/// Function parameter or return value descriptor.
135///
136/// This describes the value type being passed to or from a function along with flags that affect
137/// how the argument is passed.
138#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
139#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
140pub struct AbiParam {
141    /// Type of the argument value.
142    pub value_type: Type,
143    /// Special purpose of argument, or `Normal`.
144    pub purpose: ArgumentPurpose,
145    /// Method for extending argument to a full register.
146    pub extension: ArgumentExtension,
147}
148
149impl AbiParam {
150    /// Create a parameter with default flags.
151    pub fn new(vt: Type) -> Self {
152        Self {
153            value_type: vt,
154            extension: ArgumentExtension::None,
155            purpose: ArgumentPurpose::Normal,
156        }
157    }
158
159    /// Create a special-purpose parameter that is not (yet) bound to a specific register.
160    pub fn special(vt: Type, purpose: ArgumentPurpose) -> Self {
161        Self {
162            value_type: vt,
163            extension: ArgumentExtension::None,
164            purpose,
165        }
166    }
167
168    /// Convert `self` to a parameter with the `uext` flag set.
169    pub fn uext(self) -> Self {
170        debug_assert!(self.value_type.is_int(), "uext on {} arg", self.value_type);
171        Self {
172            extension: ArgumentExtension::Uext,
173            ..self
174        }
175    }
176
177    /// Convert `self` to a parameter type with the `sext` flag set.
178    pub fn sext(self) -> Self {
179        debug_assert!(self.value_type.is_int(), "sext on {} arg", self.value_type);
180        Self {
181            extension: ArgumentExtension::Sext,
182            ..self
183        }
184    }
185}
186
187impl fmt::Display for AbiParam {
188    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
189        write!(f, "{}", self.value_type)?;
190        match self.extension {
191            ArgumentExtension::None => {}
192            ArgumentExtension::Uext => write!(f, " uext")?,
193            ArgumentExtension::Sext => write!(f, " sext")?,
194        }
195        if self.purpose != ArgumentPurpose::Normal {
196            write!(f, " {}", self.purpose)?;
197        }
198        Ok(())
199    }
200}
201
202/// Function argument extension options.
203///
204/// On some architectures, small integer function arguments and/or return values are extended to
205/// the width of a general-purpose register.
206///
207/// This attribute specifies how an argument or return value should be extended *if the platform
208/// and ABI require it*. Because the frontend (CLIF generator) does not know anything about the
209/// particulars of the target's ABI, and the CLIF should be platform-independent, these attributes
210/// specify *how* to extend (according to the signedness of the original program) rather than
211/// *whether* to extend.
212#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
213#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
214pub enum ArgumentExtension {
215    /// No extension, high bits are indeterminate.
216    None,
217    /// Unsigned extension: high bits in register are 0.
218    Uext,
219    /// Signed extension: high bits in register replicate sign bit.
220    Sext,
221}
222
223/// The special purpose of a function argument.
224///
225/// Function arguments and return values are used to pass user program values between functions,
226/// but they are also used to represent special registers with significance to the ABI such as
227/// frame pointers and callee-saved registers.
228///
229/// The argument purpose is used to indicate any special meaning of an argument or return value.
230#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
231#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
232pub enum ArgumentPurpose {
233    /// A normal user program value passed to or from a function.
234    Normal,
235
236    /// A C struct passed as argument.
237    StructArgument(u32),
238
239    /// Struct return pointer.
240    ///
241    /// When a function needs to return more data than will fit in registers, the caller passes a
242    /// pointer to a memory location where the return value can be written. In some ABIs, this
243    /// struct return pointer is passed in a specific register.
244    ///
245    /// This argument kind can also appear as a return value for ABIs that require a function with
246    /// a `StructReturn` pointer argument to also return that pointer in a register.
247    StructReturn,
248
249    /// A VM context pointer.
250    ///
251    /// This is a pointer to a context struct containing details about the current sandbox. It is
252    /// used as a base pointer for `vmctx` global values.
253    VMContext,
254
255    /// A stack limit pointer.
256    ///
257    /// This is a pointer to a stack limit. It is used to check the current stack pointer
258    /// against. Can only appear once in a signature.
259    StackLimit,
260}
261
262impl fmt::Display for ArgumentPurpose {
263    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
264        f.write_str(match self {
265            Self::Normal => "normal",
266            Self::StructArgument(size) => return write!(f, "sarg({})", size),
267            Self::StructReturn => "sret",
268            Self::VMContext => "vmctx",
269            Self::StackLimit => "stack_limit",
270        })
271    }
272}
273
274impl FromStr for ArgumentPurpose {
275    type Err = ();
276    fn from_str(s: &str) -> Result<Self, ()> {
277        match s {
278            "normal" => Ok(Self::Normal),
279            "sret" => Ok(Self::StructReturn),
280            "vmctx" => Ok(Self::VMContext),
281            "stack_limit" => Ok(Self::StackLimit),
282            _ if s.starts_with("sarg(") => {
283                if !s.ends_with(")") {
284                    return Err(());
285                }
286                // Parse 'sarg(size)'
287                let size: u32 = s["sarg(".len()..s.len() - 1].parse().map_err(|_| ())?;
288                Ok(Self::StructArgument(size))
289            }
290            _ => Err(()),
291        }
292    }
293}
294
295/// An external function.
296///
297/// Information about a function that can be called directly with a direct `call` instruction.
298#[derive(Clone, Debug, PartialEq, Hash)]
299#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
300pub struct ExtFuncData {
301    /// Name of the external function.
302    pub name: ExternalName,
303    /// Call signature of function.
304    pub signature: SigRef,
305    /// Will this function be defined nearby, such that it will always be a certain distance away,
306    /// after linking? If so, references to it can avoid going through a GOT or PLT. Note that
307    /// symbols meant to be preemptible cannot be considered colocated.
308    ///
309    /// If `true`, some backends may use relocation forms that have limited range. The exact
310    /// distance depends on the code model in use. Currently on AArch64, for example, Cranelift
311    /// uses a custom code model supporting up to +/- 128MB displacements. If it is unknown how
312    /// far away the target will be, it is best not to set the `colocated` flag; in general, this
313    /// flag is best used when the target is known to be in the same unit of code generation, such
314    /// as a Wasm module.
315    ///
316    /// See the documentation for `RelocDistance` for more details. A `colocated` flag value of
317    /// `true` implies `RelocDistance::Near`.
318    pub colocated: bool,
319}
320
321impl ExtFuncData {
322    /// Return an estimate of the distance to the referred-to function symbol.
323    pub fn reloc_distance(&self) -> RelocDistance {
324        if self.colocated {
325            RelocDistance::Near
326        } else {
327            RelocDistance::Far
328        }
329    }
330
331    /// Returns a displayable version of the `ExtFuncData`, with or without extra context to
332    /// prettify the output.
333    pub fn display<'a>(
334        &'a self,
335        params: Option<&'a FunctionParameters>,
336    ) -> DisplayableExtFuncData<'a> {
337        DisplayableExtFuncData {
338            ext_func: self,
339            params,
340        }
341    }
342}
343
344/// A displayable `ExtFuncData`, with extra context to prettify the output.
345pub struct DisplayableExtFuncData<'a> {
346    ext_func: &'a ExtFuncData,
347    params: Option<&'a FunctionParameters>,
348}
349
350impl<'a> fmt::Display for DisplayableExtFuncData<'a> {
351    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
352        if self.ext_func.colocated {
353            write!(f, "colocated ")?;
354        }
355        write!(
356            f,
357            "{} {}",
358            self.ext_func.name.display(self.params),
359            self.ext_func.signature
360        )
361    }
362}
363
364#[cfg(test)]
365mod tests {
366    use super::*;
367    use crate::ir::types::{F32, I32, I8};
368    use alloc::string::ToString;
369
370    #[test]
371    fn argument_type() {
372        let t = AbiParam::new(I32);
373        assert_eq!(t.to_string(), "i32");
374        let mut t = t.uext();
375        assert_eq!(t.to_string(), "i32 uext");
376        assert_eq!(t.sext().to_string(), "i32 sext");
377        t.purpose = ArgumentPurpose::StructReturn;
378        assert_eq!(t.to_string(), "i32 uext sret");
379    }
380
381    #[test]
382    fn argument_purpose() {
383        let all_purpose = [
384            (ArgumentPurpose::Normal, "normal"),
385            (ArgumentPurpose::StructReturn, "sret"),
386            (ArgumentPurpose::VMContext, "vmctx"),
387            (ArgumentPurpose::StackLimit, "stack_limit"),
388            (ArgumentPurpose::StructArgument(42), "sarg(42)"),
389        ];
390        for &(e, n) in &all_purpose {
391            assert_eq!(e.to_string(), n);
392            assert_eq!(Ok(e), n.parse());
393        }
394    }
395
396    #[test]
397    fn call_conv() {
398        for &cc in &[
399            CallConv::Fast,
400            CallConv::Cold,
401            CallConv::SystemV,
402            CallConv::WindowsFastcall,
403        ] {
404            assert_eq!(Ok(cc), cc.to_string().parse())
405        }
406    }
407
408    #[test]
409    fn signatures() {
410        let mut sig = Signature::new(CallConv::WindowsFastcall);
411        assert_eq!(sig.to_string(), "() windows_fastcall");
412        sig.params.push(AbiParam::new(I32));
413        assert_eq!(sig.to_string(), "(i32) windows_fastcall");
414        sig.returns.push(AbiParam::new(F32));
415        assert_eq!(sig.to_string(), "(i32) -> f32 windows_fastcall");
416        sig.params.push(AbiParam::new(I32.by(4).unwrap()));
417        assert_eq!(sig.to_string(), "(i32, i32x4) -> f32 windows_fastcall");
418        sig.returns.push(AbiParam::new(I8));
419        assert_eq!(sig.to_string(), "(i32, i32x4) -> f32, i8 windows_fastcall");
420    }
421}