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 /// 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
326impl ExtFuncData {
327 /// Returns a displayable version of the `ExtFuncData`, with or without extra context to
328 /// prettify the output.
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.
341pub struct DisplayableExtFuncData<'a> {
342 ext_func: &'a ExtFuncData,
343 params: Option<&'a FunctionParameters>,
344}
345
346impl<'a> fmt::Display for DisplayableExtFuncData<'a> {
347 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)]
364mod tests {
365 use super::*;
366 use crate::ir::types::{F32, I8, I32};
367 use alloc::string::ToString;
368
369 #[test]
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]
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]
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]
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}