/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 | | } |