Coverage Report

Created: 2026-03-26 07:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/cranelift-codegen-0.129.1/src/ir/extfunc.rs
Line
Count
Source
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.
39
433k
    pub fn new(call_conv: CallConv) -> Self {
40
433k
        Self {
41
433k
            params: Vec::new(),
42
433k
            returns: Vec::new(),
43
433k
            call_conv,
44
433k
        }
45
433k
    }
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::Signature>::new
<cranelift_codegen::ir::extfunc::Signature>::new
Line
Count
Source
39
433k
    pub fn new(call_conv: CallConv) -> Self {
40
433k
        Self {
41
433k
            params: Vec::new(),
42
433k
            returns: Vec::new(),
43
433k
            call_conv,
44
433k
        }
45
433k
    }
46
47
    /// Clear the signature so it is identical to a fresh one returned by `new()`.
48
0
    pub fn clear(&mut self, call_conv: CallConv) {
49
0
        self.params.clear();
50
0
        self.returns.clear();
51
0
        self.call_conv = call_conv;
52
0
    }
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::Signature>::clear
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::Signature>::clear
53
54
    /// Find the index of a presumed unique special-purpose parameter.
55
2.79M
    pub fn special_param_index(&self, purpose: ArgumentPurpose) -> Option<usize> {
56
35.9M
        self.params.iter().rposition(|arg| arg.purpose == purpose)
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::Signature>::special_param_index::{closure#0}
<cranelift_codegen::ir::extfunc::Signature>::special_param_index::{closure#0}
Line
Count
Source
56
35.9M
        self.params.iter().rposition(|arg| arg.purpose == purpose)
57
2.79M
    }
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::Signature>::special_param_index
<cranelift_codegen::ir::extfunc::Signature>::special_param_index
Line
Count
Source
55
2.79M
    pub fn special_param_index(&self, purpose: ArgumentPurpose) -> Option<usize> {
56
2.79M
        self.params.iter().rposition(|arg| arg.purpose == purpose)
57
2.79M
    }
58
59
    /// Find the index of a presumed unique special-purpose parameter.
60
494k
    pub fn special_return_index(&self, purpose: ArgumentPurpose) -> Option<usize> {
61
4.10M
        self.returns.iter().rposition(|arg| arg.purpose == purpose)
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::Signature>::special_return_index::{closure#0}
<cranelift_codegen::ir::extfunc::Signature>::special_return_index::{closure#0}
Line
Count
Source
61
4.10M
        self.returns.iter().rposition(|arg| arg.purpose == purpose)
62
494k
    }
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::Signature>::special_return_index
<cranelift_codegen::ir::extfunc::Signature>::special_return_index
Line
Count
Source
60
494k
    pub fn special_return_index(&self, purpose: ArgumentPurpose) -> Option<usize> {
61
494k
        self.returns.iter().rposition(|arg| arg.purpose == purpose)
62
494k
    }
63
64
    /// Does this signature have a parameter whose `ArgumentPurpose` is
65
    /// `purpose`?
66
132k
    pub fn uses_special_param(&self, purpose: ArgumentPurpose) -> bool {
67
132k
        self.special_param_index(purpose).is_some()
68
132k
    }
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::Signature>::uses_special_param
<cranelift_codegen::ir::extfunc::Signature>::uses_special_param
Line
Count
Source
66
132k
    pub fn uses_special_param(&self, purpose: ArgumentPurpose) -> bool {
67
132k
        self.special_param_index(purpose).is_some()
68
132k
    }
69
70
    /// Does this signature have a return whose `ArgumentPurpose` is `purpose`?
71
494k
    pub fn uses_special_return(&self, purpose: ArgumentPurpose) -> bool {
72
494k
        self.special_return_index(purpose).is_some()
73
494k
    }
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::Signature>::uses_special_return
<cranelift_codegen::ir::extfunc::Signature>::uses_special_return
Line
Count
Source
71
494k
    pub fn uses_special_return(&self, purpose: ArgumentPurpose) -> bool {
72
494k
        self.special_return_index(purpose).is_some()
73
494k
    }
74
75
    /// How many special parameters does this function have?
76
0
    pub fn num_special_params(&self) -> usize {
77
0
        self.params
78
0
            .iter()
79
0
            .filter(|p| p.purpose != ArgumentPurpose::Normal)
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::Signature>::num_special_params::{closure#0}
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::Signature>::num_special_params::{closure#0}
80
0
            .count()
81
0
    }
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::Signature>::num_special_params
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::Signature>::num_special_params
82
83
    /// How many special returns does this function have?
84
0
    pub fn num_special_returns(&self) -> usize {
85
0
        self.returns
86
0
            .iter()
87
0
            .filter(|r| r.purpose != ArgumentPurpose::Normal)
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::Signature>::num_special_returns::{closure#0}
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::Signature>::num_special_returns::{closure#0}
88
0
            .count()
89
0
    }
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::Signature>::num_special_returns
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::Signature>::num_special_returns
90
91
    /// Does this signature take an struct return pointer parameter?
92
0
    pub fn uses_struct_return_param(&self) -> bool {
93
0
        self.uses_special_param(ArgumentPurpose::StructReturn)
94
0
    }
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::Signature>::uses_struct_return_param
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::Signature>::uses_struct_return_param
95
96
    /// Does this return more than one normal value? (Pre-struct return
97
    /// legalization)
98
0
    pub fn is_multi_return(&self) -> bool {
99
0
        self.returns
100
0
            .iter()
101
0
            .filter(|r| r.purpose == ArgumentPurpose::Normal)
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::Signature>::is_multi_return::{closure#0}
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::Signature>::is_multi_return::{closure#0}
102
0
            .count()
103
            > 1
104
0
    }
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::Signature>::is_multi_return
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::Signature>::is_multi_return
105
}
106
107
0
fn write_list(f: &mut fmt::Formatter, args: &[AbiParam]) -> fmt::Result {
108
0
    match args.split_first() {
109
0
        None => {}
110
0
        Some((first, rest)) => {
111
0
            write!(f, "{first}")?;
112
0
            for arg in rest {
113
0
                write!(f, ", {arg}")?;
114
            }
115
        }
116
    }
117
0
    Ok(())
118
0
}
Unexecuted instantiation: cranelift_codegen::ir::extfunc::write_list
Unexecuted instantiation: cranelift_codegen::ir::extfunc::write_list
119
120
impl fmt::Display for Signature {
121
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
122
0
        write!(f, "(")?;
123
0
        write_list(f, &self.params)?;
124
0
        write!(f, ")")?;
125
0
        if !self.returns.is_empty() {
126
0
            write!(f, " -> ")?;
127
0
            write_list(f, &self.returns)?;
128
0
        }
129
0
        write!(f, " {}", self.call_conv)
130
0
    }
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::Signature as core::fmt::Display>::fmt
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::Signature as core::fmt::Display>::fmt
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.
150
3.75M
    pub fn new(vt: Type) -> Self {
151
3.75M
        Self {
152
3.75M
            value_type: vt,
153
3.75M
            extension: ArgumentExtension::None,
154
3.75M
            purpose: ArgumentPurpose::Normal,
155
3.75M
        }
156
3.75M
    }
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::AbiParam>::new
<cranelift_codegen::ir::extfunc::AbiParam>::new
Line
Count
Source
150
3.75M
    pub fn new(vt: Type) -> Self {
151
3.75M
        Self {
152
3.75M
            value_type: vt,
153
3.75M
            extension: ArgumentExtension::None,
154
3.75M
            purpose: ArgumentPurpose::Normal,
155
3.75M
        }
156
3.75M
    }
157
158
    /// Create a special-purpose parameter that is not (yet) bound to a specific register.
159
251k
    pub fn special(vt: Type, purpose: ArgumentPurpose) -> Self {
160
251k
        Self {
161
251k
            value_type: vt,
162
251k
            extension: ArgumentExtension::None,
163
251k
            purpose,
164
251k
        }
165
251k
    }
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::AbiParam>::special
<cranelift_codegen::ir::extfunc::AbiParam>::special
Line
Count
Source
159
251k
    pub fn special(vt: Type, purpose: ArgumentPurpose) -> Self {
160
251k
        Self {
161
251k
            value_type: vt,
162
251k
            extension: ArgumentExtension::None,
163
251k
            purpose,
164
251k
        }
165
251k
    }
166
167
    /// Convert `self` to a parameter with the `uext` flag set.
168
0
    pub fn uext(self) -> Self {
169
0
        debug_assert!(self.value_type.is_int(), "uext on {} arg", self.value_type);
170
0
        Self {
171
0
            extension: ArgumentExtension::Uext,
172
0
            ..self
173
0
        }
174
0
    }
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::AbiParam>::uext
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::AbiParam>::uext
175
176
    /// Convert `self` to a parameter type with the `sext` flag set.
177
0
    pub fn sext(self) -> Self {
178
0
        debug_assert!(self.value_type.is_int(), "sext on {} arg", self.value_type);
179
0
        Self {
180
0
            extension: ArgumentExtension::Sext,
181
0
            ..self
182
0
        }
183
0
    }
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::AbiParam>::sext
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::AbiParam>::sext
184
}
185
186
impl fmt::Display for AbiParam {
187
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
188
0
        write!(f, "{}", self.value_type)?;
189
0
        match self.extension {
190
0
            ArgumentExtension::None => {}
191
0
            ArgumentExtension::Uext => write!(f, " uext")?,
192
0
            ArgumentExtension::Sext => write!(f, " sext")?,
193
        }
194
0
        if self.purpose != ArgumentPurpose::Normal {
195
0
            write!(f, " {}", self.purpose)?;
196
0
        }
197
0
        Ok(())
198
0
    }
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::AbiParam as core::fmt::Display>::fmt
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::AbiParam as core::fmt::Display>::fmt
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 {
262
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
263
0
        f.write_str(match self {
264
0
            Self::Normal => "normal",
265
0
            Self::StructArgument(size) => return write!(f, "sarg({size})"),
266
0
            Self::StructReturn => "sret",
267
0
            Self::VMContext => "vmctx",
268
        })
269
0
    }
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::ArgumentPurpose as core::fmt::Display>::fmt
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::ArgumentPurpose as core::fmt::Display>::fmt
270
}
271
272
impl FromStr for ArgumentPurpose {
273
    type Err = ();
274
0
    fn from_str(s: &str) -> Result<Self, ()> {
275
0
        match s {
276
0
            "normal" => Ok(Self::Normal),
277
0
            "sret" => Ok(Self::StructReturn),
278
0
            "vmctx" => Ok(Self::VMContext),
279
0
            _ if s.starts_with("sarg(") => {
280
0
                if !s.ends_with(")") {
281
0
                    return Err(());
282
0
                }
283
                // Parse 'sarg(size)'
284
0
                let size: u32 = s["sarg(".len()..s.len() - 1].parse().map_err(|_| ())?;
285
0
                Ok(Self::StructArgument(size))
286
            }
287
0
            _ => Err(()),
288
        }
289
0
    }
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::ArgumentPurpose as core::str::traits::FromStr>::from_str
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::ArgumentPurpose as core::str::traits::FromStr>::from_str
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.
329
0
    pub fn display<'a>(
330
0
        &'a self,
331
0
        params: Option<&'a FunctionParameters>,
332
0
    ) -> DisplayableExtFuncData<'a> {
333
0
        DisplayableExtFuncData {
334
0
            ext_func: self,
335
0
            params,
336
0
        }
337
0
    }
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::ExtFuncData>::display
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::ExtFuncData>::display
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> {
347
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
348
0
        if self.ext_func.colocated {
349
0
            write!(f, "colocated ")?;
350
0
        }
351
0
        if self.ext_func.patchable {
352
0
            write!(f, "patchable ")?;
353
0
        }
354
0
        write!(
355
0
            f,
356
            "{} {}",
357
0
            self.ext_func.name.display(self.params),
358
            self.ext_func.signature
359
        )
360
0
    }
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::DisplayableExtFuncData as core::fmt::Display>::fmt
Unexecuted instantiation: <cranelift_codegen::ir::extfunc::DisplayableExtFuncData as core::fmt::Display>::fmt
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]
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
}