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 
8 use crate::ir::{ExternalName, SigRef, Type};
9 use crate::isa::CallConv;
10 use alloc::vec::Vec;
11 use core::fmt;
12 use core::str::FromStr;
13 #[cfg(feature = "enable-serde")]
14 use serde_derive::{Deserialize, Serialize};
15 
16 use 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))]
27 pub 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 
37 impl Signature {
38     /// Create a new blank signature.
new(call_conv: CallConv) -> Self39     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()`.
clear(&mut self, call_conv: CallConv)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.
special_param_index(&self, purpose: ArgumentPurpose) -> Option<usize>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.
special_return_index(&self, purpose: ArgumentPurpose) -> Option<usize>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`?
uses_special_param(&self, purpose: ArgumentPurpose) -> bool66     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`?
uses_special_return(&self, purpose: ArgumentPurpose) -> bool71     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?
num_special_params(&self) -> usize76     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?
num_special_returns(&self) -> usize84     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?
uses_struct_return_param(&self) -> bool92     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)
is_multi_return(&self) -> bool98     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 
write_list(f: &mut fmt::Formatter, args: &[AbiParam]) -> fmt::Result107 fn 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 
120 impl fmt::Display for Signature {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result121     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))]
139 pub 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 
148 impl AbiParam {
149     /// Create a parameter with default flags.
new(vt: Type) -> Self150     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.
special(vt: Type, purpose: ArgumentPurpose) -> Self159     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.
uext(self) -> Self168     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.
sext(self) -> Self177     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 
186 impl fmt::Display for AbiParam {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result187     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))]
213 pub 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))]
231 pub 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 
261 impl fmt::Display for ArgumentPurpose {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result262     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 
272 impl FromStr for ArgumentPurpose {
273     type Err = ();
from_str(s: &str) -> Result<Self, ()>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))]
297 pub 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     /// Is this function "patchable"? If so, calls to this function will
317     /// emit additional metadata indicating how to patch them in or out.
318     ///
319     /// Calls to functions of any calling convention may be patchable,
320     /// *but* only calls with no return values are patchable. This is
321     /// because every SSA value must always be defined, and return
322     /// values would not be if the call were patched out.
323     pub patchable: bool,
324 }
325 
326 impl ExtFuncData {
327     /// Returns a displayable version of the `ExtFuncData`, with or without extra context to
328     /// prettify the output.
display<'a>( &'a self, params: Option<&'a FunctionParameters>, ) -> DisplayableExtFuncData<'a>329     pub fn display<'a>(
330         &'a self,
331         params: Option<&'a FunctionParameters>,
332     ) -> DisplayableExtFuncData<'a> {
333         DisplayableExtFuncData {
334             ext_func: self,
335             params,
336         }
337     }
338 }
339 
340 /// A displayable `ExtFuncData`, with extra context to prettify the output.
341 pub struct DisplayableExtFuncData<'a> {
342     ext_func: &'a ExtFuncData,
343     params: Option<&'a FunctionParameters>,
344 }
345 
346 impl<'a> fmt::Display for DisplayableExtFuncData<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result347     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
348         if self.ext_func.colocated {
349             write!(f, "colocated ")?;
350         }
351         if self.ext_func.patchable {
352             write!(f, "patchable ")?;
353         }
354         write!(
355             f,
356             "{} {}",
357             self.ext_func.name.display(self.params),
358             self.ext_func.signature
359         )
360     }
361 }
362 
363 #[cfg(test)]
364 mod tests {
365     use super::*;
366     use crate::ir::types::{F32, I8, I32};
367     use alloc::string::ToString;
368 
369     #[test]
argument_type()370     fn argument_type() {
371         let t = AbiParam::new(I32);
372         assert_eq!(t.to_string(), "i32");
373         let mut t = t.uext();
374         assert_eq!(t.to_string(), "i32 uext");
375         assert_eq!(t.sext().to_string(), "i32 sext");
376         t.purpose = ArgumentPurpose::StructReturn;
377         assert_eq!(t.to_string(), "i32 uext sret");
378     }
379 
380     #[test]
argument_purpose()381     fn argument_purpose() {
382         let all_purpose = [
383             (ArgumentPurpose::Normal, "normal"),
384             (ArgumentPurpose::StructReturn, "sret"),
385             (ArgumentPurpose::VMContext, "vmctx"),
386             (ArgumentPurpose::StructArgument(42), "sarg(42)"),
387         ];
388         for &(e, n) in &all_purpose {
389             assert_eq!(e.to_string(), n);
390             assert_eq!(Ok(e), n.parse());
391         }
392     }
393 
394     #[test]
call_conv()395     fn call_conv() {
396         for &cc in &[
397             CallConv::Fast,
398             CallConv::PreserveAll,
399             CallConv::Tail,
400             CallConv::SystemV,
401             CallConv::WindowsFastcall,
402         ] {
403             assert_eq!(Ok(cc), cc.to_string().parse())
404         }
405     }
406 
407     #[test]
signatures()408     fn signatures() {
409         let mut sig = Signature::new(CallConv::WindowsFastcall);
410         assert_eq!(sig.to_string(), "() windows_fastcall");
411         sig.params.push(AbiParam::new(I32));
412         assert_eq!(sig.to_string(), "(i32) windows_fastcall");
413         sig.returns.push(AbiParam::new(F32));
414         assert_eq!(sig.to_string(), "(i32) -> f32 windows_fastcall");
415         sig.params.push(AbiParam::new(I32.by(4).unwrap()));
416         assert_eq!(sig.to_string(), "(i32, i32x4) -> f32 windows_fastcall");
417         sig.returns.push(AbiParam::new(I8));
418         assert_eq!(sig.to_string(), "(i32, i32x4) -> f32, i8 windows_fastcall");
419     }
420 }
421