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 crate::machinst::RelocDistance;
11 use alloc::vec::Vec;
12 use core::fmt;
13 use core::str::FromStr;
14 #[cfg(feature = "enable-serde")]
15 use serde::{Deserialize, Serialize};
16 
17 use 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))]
28 pub 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 
38 impl 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 
108 fn 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 
121 impl 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))]
140 pub 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 
149 impl 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 
187 impl 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))]
214 pub 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))]
232 pub 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 signature identifier.
256     ///
257     /// This is a special-purpose argument used to identify the calling convention expected by the
258     /// caller in an indirect call. The callee can verify that the expected signature ID matches.
259     SignatureId,
260 
261     /// A stack limit pointer.
262     ///
263     /// This is a pointer to a stack limit. It is used to check the current stack pointer
264     /// against. Can only appear once in a signature.
265     StackLimit,
266 }
267 
268 impl fmt::Display for ArgumentPurpose {
269     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
270         f.write_str(match self {
271             Self::Normal => "normal",
272             Self::StructArgument(size) => return write!(f, "sarg({})", size),
273             Self::StructReturn => "sret",
274             Self::VMContext => "vmctx",
275             Self::SignatureId => "sigid",
276             Self::StackLimit => "stack_limit",
277         })
278     }
279 }
280 
281 impl FromStr for ArgumentPurpose {
282     type Err = ();
283     fn from_str(s: &str) -> Result<Self, ()> {
284         match s {
285             "normal" => Ok(Self::Normal),
286             "sret" => Ok(Self::StructReturn),
287             "vmctx" => Ok(Self::VMContext),
288             "sigid" => Ok(Self::SignatureId),
289             "stack_limit" => Ok(Self::StackLimit),
290             _ if s.starts_with("sarg(") => {
291                 if !s.ends_with(")") {
292                     return Err(());
293                 }
294                 // Parse 'sarg(size)'
295                 let size: u32 = s["sarg(".len()..s.len() - 1].parse().map_err(|_| ())?;
296                 Ok(Self::StructArgument(size))
297             }
298             _ => Err(()),
299         }
300     }
301 }
302 
303 /// An external function.
304 ///
305 /// Information about a function that can be called directly with a direct `call` instruction.
306 #[derive(Clone, Debug, PartialEq, Hash)]
307 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
308 pub struct ExtFuncData {
309     /// Name of the external function.
310     pub name: ExternalName,
311     /// Call signature of function.
312     pub signature: SigRef,
313     /// Will this function be defined nearby, such that it will always be a certain distance away,
314     /// after linking? If so, references to it can avoid going through a GOT or PLT. Note that
315     /// symbols meant to be preemptible cannot be considered colocated.
316     ///
317     /// If `true`, some backends may use relocation forms that have limited range. The exact
318     /// distance depends on the code model in use. Currently on AArch64, for example, Cranelift
319     /// uses a custom code model supporting up to +/- 128MB displacements. If it is unknown how
320     /// far away the target will be, it is best not to set the `colocated` flag; in general, this
321     /// flag is best used when the target is known to be in the same unit of code generation, such
322     /// as a Wasm module.
323     ///
324     /// See the documentation for [`RelocDistance`](crate::machinst::RelocDistance) for more details. A
325     /// `colocated` flag value of `true` implies `RelocDistance::Near`.
326     pub colocated: bool,
327 }
328 
329 impl ExtFuncData {
330     /// Return an estimate of the distance to the referred-to function symbol.
331     pub fn reloc_distance(&self) -> RelocDistance {
332         if self.colocated {
333             RelocDistance::Near
334         } else {
335             RelocDistance::Far
336         }
337     }
338 
339     /// Returns a displayable version of the `ExtFuncData`, with or without extra context to
340     /// prettify the output.
341     pub fn display<'a>(
342         &'a self,
343         params: Option<&'a FunctionParameters>,
344     ) -> DisplayableExtFuncData<'a> {
345         DisplayableExtFuncData {
346             ext_func: self,
347             params,
348         }
349     }
350 }
351 
352 /// A displayable `ExtFuncData`, with extra context to prettify the output.
353 pub struct DisplayableExtFuncData<'a> {
354     ext_func: &'a ExtFuncData,
355     params: Option<&'a FunctionParameters>,
356 }
357 
358 impl<'a> fmt::Display for DisplayableExtFuncData<'a> {
359     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
360         if self.ext_func.colocated {
361             write!(f, "colocated ")?;
362         }
363         write!(
364             f,
365             "{} {}",
366             self.ext_func.name.display(self.params),
367             self.ext_func.signature
368         )
369     }
370 }
371 
372 #[cfg(test)]
373 mod tests {
374     use super::*;
375     use crate::ir::types::{B8, F32, I32};
376     use alloc::string::ToString;
377 
378     #[test]
379     fn argument_type() {
380         let t = AbiParam::new(I32);
381         assert_eq!(t.to_string(), "i32");
382         let mut t = t.uext();
383         assert_eq!(t.to_string(), "i32 uext");
384         assert_eq!(t.sext().to_string(), "i32 sext");
385         t.purpose = ArgumentPurpose::StructReturn;
386         assert_eq!(t.to_string(), "i32 uext sret");
387     }
388 
389     #[test]
390     fn argument_purpose() {
391         let all_purpose = [
392             (ArgumentPurpose::Normal, "normal"),
393             (ArgumentPurpose::StructReturn, "sret"),
394             (ArgumentPurpose::VMContext, "vmctx"),
395             (ArgumentPurpose::SignatureId, "sigid"),
396             (ArgumentPurpose::StackLimit, "stack_limit"),
397             (ArgumentPurpose::StructArgument(42), "sarg(42)"),
398         ];
399         for &(e, n) in &all_purpose {
400             assert_eq!(e.to_string(), n);
401             assert_eq!(Ok(e), n.parse());
402         }
403     }
404 
405     #[test]
406     fn call_conv() {
407         for &cc in &[
408             CallConv::Fast,
409             CallConv::Cold,
410             CallConv::SystemV,
411             CallConv::WindowsFastcall,
412         ] {
413             assert_eq!(Ok(cc), cc.to_string().parse())
414         }
415     }
416 
417     #[test]
418     fn signatures() {
419         let mut sig = Signature::new(CallConv::WindowsFastcall);
420         assert_eq!(sig.to_string(), "() windows_fastcall");
421         sig.params.push(AbiParam::new(I32));
422         assert_eq!(sig.to_string(), "(i32) windows_fastcall");
423         sig.returns.push(AbiParam::new(F32));
424         assert_eq!(sig.to_string(), "(i32) -> f32 windows_fastcall");
425         sig.params.push(AbiParam::new(I32.by(4).unwrap()));
426         assert_eq!(sig.to_string(), "(i32, i32x4) -> f32 windows_fastcall");
427         sig.returns.push(AbiParam::new(B8));
428         assert_eq!(sig.to_string(), "(i32, i32x4) -> f32, b8 windows_fastcall");
429     }
430 }
431