Coverage Report

Created: 2025-01-09 07:53

/src/wasm-tools/crates/wit-parser/src/abi.rs
Line
Count
Source (jump to first uncovered line)
1
use crate::{Function, Handle, Int, Resolve, Type, TypeDefKind};
2
3
/// A core WebAssembly signature with params and results.
4
#[derive(Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
5
pub struct WasmSignature {
6
    /// The WebAssembly parameters of this function.
7
    pub params: Vec<WasmType>,
8
9
    /// The WebAssembly results of this function.
10
    pub results: Vec<WasmType>,
11
12
    /// Whether or not this signature is passing all of its parameters
13
    /// indirectly through a pointer within `params`.
14
    ///
15
    /// Note that `params` still reflects the true wasm paramters of this
16
    /// function, this is auxiliary information for code generators if
17
    /// necessary.
18
    pub indirect_params: bool,
19
20
    /// Whether or not this signature is using a return pointer to store the
21
    /// result of the function, which is reflected either in `params` or
22
    /// `results` depending on the context this function is used (e.g. an import
23
    /// or an export).
24
    pub retptr: bool,
25
}
26
27
/// Enumerates wasm types used by interface types when lowering/lifting.
28
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
29
pub enum WasmType {
30
    I32,
31
    I64,
32
    F32,
33
    F64,
34
35
    /// A pointer type. In core Wasm this typically lowers to either `i32` or
36
    /// `i64` depending on the index type of the exported linear memory,
37
    /// however bindings can use different source-level types to preserve
38
    /// provenance.
39
    ///
40
    /// Users that don't do anything special for pointers can treat this as
41
    /// `i32`.
42
    Pointer,
43
44
    /// A type for values which can be either pointers or 64-bit integers.
45
    /// This occurs in variants, when pointers and non-pointers are unified.
46
    ///
47
    /// Users that don't do anything special for pointers can treat this as
48
    /// `i64`.
49
    PointerOrI64,
50
51
    /// An array length type. In core Wasm this lowers to either `i32` or `i64`
52
    /// depending on the index type of the exported linear memory.
53
    ///
54
    /// Users that don't do anything special for pointers can treat this as
55
    /// `i32`.
56
    Length,
57
    // NOTE: we don't lower interface types to any other Wasm type,
58
    // e.g. externref, so we don't need to define them here.
59
}
60
61
7.13k
fn join(a: WasmType, b: WasmType) -> WasmType {
62
    use WasmType::*;
63
64
7.13k
    match (a, b) {
65
        (I32, I32)
66
        | (I64, I64)
67
        | (F32, F32)
68
        | (F64, F64)
69
        | (Pointer, Pointer)
70
        | (PointerOrI64, PointerOrI64)
71
5.40k
        | (Length, Length) => a,
72
73
291
        (I32, F32) | (F32, I32) => I32,
74
75
        // A length is at least an `i32`, maybe more, so it wins over
76
        // 32-bit types.
77
58
        (Length, I32 | F32) => Length,
78
83
        (I32 | F32, Length) => Length,
79
80
        // A length might be an `i64`, but might not be, so if we have
81
        // 64-bit types, they win.
82
0
        (Length, I64 | F64) => I64,
83
12
        (I64 | F64, Length) => I64,
84
85
        // Pointers have provenance and are at least an `i32`, so they
86
        // win over 32-bit and length types.
87
157
        (Pointer, I32 | F32 | Length) => Pointer,
88
205
        (I32 | F32 | Length, Pointer) => Pointer,
89
90
        // If we need 64 bits and provenance, we need to use the special
91
        // `PointerOrI64`.
92
0
        (Pointer, I64 | F64) => PointerOrI64,
93
7
        (I64 | F64, Pointer) => PointerOrI64,
94
95
        // PointerOrI64 wins over everything.
96
0
        (PointerOrI64, _) => PointerOrI64,
97
0
        (_, PointerOrI64) => PointerOrI64,
98
99
        // Otherwise, `i64` wins.
100
666
        (_, I64 | F64) | (I64 | F64, _) => I64,
101
    }
102
6.88k
}
103
104
impl From<Int> for WasmType {
105
2.61k
    fn from(i: Int) -> WasmType {
106
2.61k
        match i {
107
2.61k
            Int::U8 | Int::U16 | Int::U32 => WasmType::I32,
108
0
            Int::U64 => WasmType::I64,
109
        }
110
2.61k
    }
111
}
112
113
/// We use a different ABI for wasm importing functions exported by the host
114
/// than for wasm exporting functions imported by the host.
115
///
116
/// Note that this reflects the flavor of ABI we generate, and not necessarily
117
/// the way the resulting bindings will be used by end users. See the comments
118
/// on the `Direction` enum in gen-core for details.
119
///
120
/// The bindings ABI has a concept of a "guest" and a "host". There are two
121
/// variants of the ABI, one specialized for the "guest" importing and calling
122
/// a function defined and exported in the "host", and the other specialized for
123
/// the "host" importing and calling a function defined and exported in the "guest".
124
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
125
pub enum AbiVariant {
126
    /// The guest is importing and calling the function.
127
    GuestImport,
128
    /// The guest is defining and exporting the function.
129
    GuestExport,
130
    GuestImportAsync,
131
    GuestExportAsync,
132
    GuestExportAsyncStackful,
133
}
134
135
impl Resolve {
136
    /// Get the WebAssembly type signature for this interface function
137
    ///
138
    /// The first entry returned is the list of parameters and the second entry
139
    /// is the list of results for the wasm function signature.
140
138k
    pub fn wasm_signature(&self, variant: AbiVariant, func: &Function) -> WasmSignature {
141
138k
        if let AbiVariant::GuestImportAsync = variant {
142
17.9k
            return WasmSignature {
143
17.9k
                params: vec![WasmType::Pointer; 2],
144
17.9k
                indirect_params: true,
145
17.9k
                results: vec![WasmType::I32],
146
17.9k
                retptr: true,
147
17.9k
            };
148
120k
        }
149
150
        const MAX_FLAT_PARAMS: usize = 16;
151
        const MAX_FLAT_RESULTS: usize = 1;
152
153
120k
        let mut params = Vec::new();
154
120k
        let mut indirect_params = false;
155
267k
        for (_, param) in func.params.iter() {
156
267k
            self.push_flat(param, &mut params);
157
267k
        }
158
159
120k
        if params.len() > MAX_FLAT_PARAMS {
160
3.58k
            params.truncate(0);
161
3.58k
            params.push(WasmType::Pointer);
162
3.58k
            indirect_params = true;
163
3.58k
        } else {
164
103k
            if matches!(
165
116k
                (&func.kind, variant),
166
                (
167
                    crate::FunctionKind::Method(_),
168
                    AbiVariant::GuestExport
169
                        | AbiVariant::GuestExportAsync
170
                        | AbiVariant::GuestExportAsyncStackful
171
                )
172
            ) {
173
                // Guest exported methods always receive resource rep as first argument
174
                //
175
                // TODO: Ideally you would distinguish between imported and exported
176
                // resource Handles and then use either I32 or Pointer in abi::push_flat().
177
                // But this contextual information isn't available, yet.
178
                // See https://github.com/bytecodealliance/wasm-tools/pull/1438 for more details.
179
13.0k
                assert!(matches!(params[0], WasmType::I32));
180
13.0k
                params[0] = WasmType::Pointer;
181
103k
            }
182
        }
183
184
120k
        match variant {
185
            AbiVariant::GuestExportAsync => {
186
11.8k
                return WasmSignature {
187
11.8k
                    params,
188
11.8k
                    indirect_params,
189
11.8k
                    results: vec![WasmType::Pointer],
190
11.8k
                    retptr: false,
191
11.8k
                };
192
            }
193
            AbiVariant::GuestExportAsyncStackful => {
194
2.61k
                return WasmSignature {
195
2.61k
                    params,
196
2.61k
                    indirect_params,
197
2.61k
                    results: Vec::new(),
198
2.61k
                    retptr: false,
199
2.61k
                };
200
            }
201
105k
            _ => {}
202
105k
        }
203
105k
204
105k
        let mut results = Vec::new();
205
105k
        for ty in func.results.iter_types() {
206
94.3k
            self.push_flat(ty, &mut results)
207
        }
208
209
105k
        let mut retptr = false;
210
105k
211
105k
        // Rust/C don't support multi-value well right now, so if a function
212
105k
        // would have multiple results then instead truncate it. Imports take a
213
105k
        // return pointer to write into and exports return a pointer they wrote
214
105k
        // into.
215
105k
        if results.len() > MAX_FLAT_RESULTS {
216
2.51k
            retptr = true;
217
2.51k
            results.truncate(0);
218
2.51k
            match variant {
219
1.55k
                AbiVariant::GuestImport => {
220
1.55k
                    params.push(WasmType::Pointer);
221
1.55k
                }
222
960
                AbiVariant::GuestExport => {
223
960
                    results.push(WasmType::Pointer);
224
960
                }
225
0
                _ => unreachable!(),
226
            }
227
103k
        }
228
229
105k
        WasmSignature {
230
105k
            params,
231
105k
            indirect_params,
232
105k
            results,
233
105k
            retptr,
234
105k
        }
235
138k
    }
236
237
    /// Appends the flat wasm types representing `ty` onto the `result`
238
    /// list provided.
239
622k
    pub fn push_flat(&self, ty: &Type, result: &mut Vec<WasmType>) {
240
622k
        match ty {
241
            Type::Bool
242
            | Type::S8
243
            | Type::U8
244
            | Type::S16
245
            | Type::U16
246
            | Type::S32
247
            | Type::U32
248
282k
            | Type::Char => result.push(WasmType::I32),
249
250
11.8k
            Type::U64 | Type::S64 => result.push(WasmType::I64),
251
8.10k
            Type::F32 => result.push(WasmType::F32),
252
61.5k
            Type::F64 => result.push(WasmType::F64),
253
2.93k
            Type::String => {
254
2.93k
                result.push(WasmType::Pointer);
255
2.93k
                result.push(WasmType::Length);
256
2.93k
            }
257
258
255k
            Type::Id(id) => match &self.types[*id].kind {
259
695
                TypeDefKind::Type(t) => self.push_flat(t, result),
260
261
34.0k
                TypeDefKind::Handle(Handle::Own(_) | Handle::Borrow(_)) => {
262
34.0k
                    result.push(WasmType::I32);
263
34.0k
                }
264
265
0
                TypeDefKind::Resource => todo!(),
266
267
168
                TypeDefKind::Record(r) => {
268
1.39k
                    for field in r.fields.iter() {
269
1.39k
                        self.push_flat(&field.ty, result);
270
1.39k
                    }
271
                }
272
273
56.4k
                TypeDefKind::Tuple(t) => {
274
203k
                    for ty in t.types.iter() {
275
203k
                        self.push_flat(ty, result);
276
203k
                    }
277
                }
278
279
165
                TypeDefKind::Flags(r) => {
280
165
                    for _ in 0..r.repr().count() {
281
165
                        result.push(WasmType::I32);
282
165
                    }
283
                }
284
285
1.93k
                TypeDefKind::List(_) => {
286
1.93k
                    result.push(WasmType::Pointer);
287
1.93k
                    result.push(WasmType::Length);
288
1.93k
                }
289
290
458
                TypeDefKind::Variant(v) => {
291
458
                    result.push(v.tag().into());
292
2.06k
                    self.push_flat_variants(v.cases.iter().map(|c| c.ty.as_ref()), result);
293
458
                }
294
295
2.15k
                TypeDefKind::Enum(e) => result.push(e.tag().into()),
296
297
43.8k
                TypeDefKind::Option(t) => {
298
43.8k
                    result.push(WasmType::I32);
299
43.8k
                    self.push_flat_variants([None, Some(t)], result);
300
43.8k
                }
301
302
6.28k
                TypeDefKind::Result(r) => {
303
6.28k
                    result.push(WasmType::I32);
304
6.28k
                    self.push_flat_variants([r.ok.as_ref(), r.err.as_ref()], result);
305
6.28k
                }
306
307
11.4k
                TypeDefKind::Future(_) => {
308
11.4k
                    result.push(WasmType::I32);
309
11.4k
                }
310
311
3.61k
                TypeDefKind::Stream(_) => {
312
3.61k
                    result.push(WasmType::I32);
313
3.61k
                }
314
315
94.2k
                TypeDefKind::ErrorContext => {
316
94.2k
                    result.push(WasmType::I32);
317
94.2k
                }
318
319
0
                TypeDefKind::Unknown => unreachable!(),
320
            },
321
        }
322
622k
    }
323
324
50.5k
    fn push_flat_variants<'a>(
325
50.5k
        &self,
326
50.5k
        tys: impl IntoIterator<Item = Option<&'a Type>>,
327
50.5k
        result: &mut Vec<WasmType>,
328
50.5k
    ) {
329
50.5k
        let mut temp = Vec::new();
330
50.5k
        let start = result.len();
331
332
        // Push each case's type onto a temporary vector, and then
333
        // merge that vector into our final list starting at
334
        // `start`. Note that this requires some degree of
335
        // "unification" so we can handle things like `Result<i32,
336
        // f32>` where that turns into `[i32 i32]` where the second
337
        // `i32` might be the `f32` bitcasted.
338
152k
        for ty in tys {
339
102k
            if let Some(ty) = ty {
340
55.2k
                self.push_flat(ty, &mut temp);
341
342
403k
                for (i, ty) in temp.drain(..).enumerate() {
343
403k
                    match result.get_mut(start + i) {
344
7.13k
                        Some(prev) => *prev = join(*prev, ty),
345
396k
                        None => result.push(ty),
346
                    }
347
                }
348
46.9k
            }
349
        }
350
50.5k
    }
<wit_parser::resolve::Resolve>::push_flat_variants::<[core::option::Option<&wit_parser::Type>; 2]>
Line
Count
Source
324
50.1k
    fn push_flat_variants<'a>(
325
50.1k
        &self,
326
50.1k
        tys: impl IntoIterator<Item = Option<&'a Type>>,
327
50.1k
        result: &mut Vec<WasmType>,
328
50.1k
    ) {
329
50.1k
        let mut temp = Vec::new();
330
50.1k
        let start = result.len();
331
332
        // Push each case's type onto a temporary vector, and then
333
        // merge that vector into our final list starting at
334
        // `start`. Note that this requires some degree of
335
        // "unification" so we can handle things like `Result<i32,
336
        // f32>` where that turns into `[i32 i32]` where the second
337
        // `i32` might be the `f32` bitcasted.
338
150k
        for ty in tys {
339
100k
            if let Some(ty) = ty {
340
53.3k
                self.push_flat(ty, &mut temp);
341
342
396k
                for (i, ty) in temp.drain(..).enumerate() {
343
396k
                    match result.get_mut(start + i) {
344
3.95k
                        Some(prev) => *prev = join(*prev, ty),
345
392k
                        None => result.push(ty),
346
                    }
347
                }
348
46.8k
            }
349
        }
350
50.1k
    }
<wit_parser::resolve::Resolve>::push_flat_variants::<core::iter::adapters::map::Map<core::slice::iter::Iter<wit_parser::Case>, <wit_parser::resolve::Resolve>::push_flat::{closure#0}>>
Line
Count
Source
324
458
    fn push_flat_variants<'a>(
325
458
        &self,
326
458
        tys: impl IntoIterator<Item = Option<&'a Type>>,
327
458
        result: &mut Vec<WasmType>,
328
458
    ) {
329
458
        let mut temp = Vec::new();
330
458
        let start = result.len();
331
332
        // Push each case's type onto a temporary vector, and then
333
        // merge that vector into our final list starting at
334
        // `start`. Note that this requires some degree of
335
        // "unification" so we can handle things like `Result<i32,
336
        // f32>` where that turns into `[i32 i32]` where the second
337
        // `i32` might be the `f32` bitcasted.
338
2.51k
        for ty in tys {
339
2.06k
            if let Some(ty) = ty {
340
1.94k
                self.push_flat(ty, &mut temp);
341
342
7.02k
                for (i, ty) in temp.drain(..).enumerate() {
343
7.02k
                    match result.get_mut(start + i) {
344
3.17k
                        Some(prev) => *prev = join(*prev, ty),
345
3.84k
                        None => result.push(ty),
346
                    }
347
                }
348
112
            }
349
        }
350
458
    }
351
}