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