Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/ir/extfunc.rs
1693 views
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
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
107
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 {
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))]
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
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
186
impl 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))]
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
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 = ();
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
}
317
318
impl ExtFuncData {
319
/// Returns a displayable version of the `ExtFuncData`, with or without extra context to
320
/// prettify the output.
321
pub fn display<'a>(
322
&'a self,
323
params: Option<&'a FunctionParameters>,
324
) -> DisplayableExtFuncData<'a> {
325
DisplayableExtFuncData {
326
ext_func: self,
327
params,
328
}
329
}
330
}
331
332
/// A displayable `ExtFuncData`, with extra context to prettify the output.
333
pub struct DisplayableExtFuncData<'a> {
334
ext_func: &'a ExtFuncData,
335
params: Option<&'a FunctionParameters>,
336
}
337
338
impl<'a> fmt::Display for DisplayableExtFuncData<'a> {
339
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
340
if self.ext_func.colocated {
341
write!(f, "colocated ")?;
342
}
343
write!(
344
f,
345
"{} {}",
346
self.ext_func.name.display(self.params),
347
self.ext_func.signature
348
)
349
}
350
}
351
352
#[cfg(test)]
353
mod tests {
354
use super::*;
355
use crate::ir::types::{F32, I8, I32};
356
use alloc::string::ToString;
357
358
#[test]
359
fn argument_type() {
360
let t = AbiParam::new(I32);
361
assert_eq!(t.to_string(), "i32");
362
let mut t = t.uext();
363
assert_eq!(t.to_string(), "i32 uext");
364
assert_eq!(t.sext().to_string(), "i32 sext");
365
t.purpose = ArgumentPurpose::StructReturn;
366
assert_eq!(t.to_string(), "i32 uext sret");
367
}
368
369
#[test]
370
fn argument_purpose() {
371
let all_purpose = [
372
(ArgumentPurpose::Normal, "normal"),
373
(ArgumentPurpose::StructReturn, "sret"),
374
(ArgumentPurpose::VMContext, "vmctx"),
375
(ArgumentPurpose::StructArgument(42), "sarg(42)"),
376
];
377
for &(e, n) in &all_purpose {
378
assert_eq!(e.to_string(), n);
379
assert_eq!(Ok(e), n.parse());
380
}
381
}
382
383
#[test]
384
fn call_conv() {
385
for &cc in &[
386
CallConv::Fast,
387
CallConv::Cold,
388
CallConv::SystemV,
389
CallConv::WindowsFastcall,
390
] {
391
assert_eq!(Ok(cc), cc.to_string().parse())
392
}
393
}
394
395
#[test]
396
fn signatures() {
397
let mut sig = Signature::new(CallConv::WindowsFastcall);
398
assert_eq!(sig.to_string(), "() windows_fastcall");
399
sig.params.push(AbiParam::new(I32));
400
assert_eq!(sig.to_string(), "(i32) windows_fastcall");
401
sig.returns.push(AbiParam::new(F32));
402
assert_eq!(sig.to_string(), "(i32) -> f32 windows_fastcall");
403
sig.params.push(AbiParam::new(I32.by(4).unwrap()));
404
assert_eq!(sig.to_string(), "(i32, i32x4) -> f32 windows_fastcall");
405
sig.returns.push(AbiParam::new(I8));
406
assert_eq!(sig.to_string(), "(i32, i32x4) -> f32, i8 windows_fastcall");
407
}
408
}
409
410