wit_parser/abi.rs
1use 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)]
5pub 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)]
29pub 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
61fn join(a: WasmType, b: WasmType) -> WasmType {
62 use WasmType::*;
63
64 match (a, b) {
65 (I32, I32)
66 | (I64, I64)
67 | (F32, F32)
68 | (F64, F64)
69 | (Pointer, Pointer)
70 | (PointerOrI64, PointerOrI64)
71 | (Length, Length) => a,
72
73 (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 (Length, I32 | F32) => Length,
78 (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 (Length, I64 | F64) => I64,
83 (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 (Pointer, I32 | F32 | Length) => Pointer,
88 (I32 | F32 | Length, Pointer) => Pointer,
89
90 // If we need 64 bits and provenance, we need to use the special
91 // `PointerOrI64`.
92 (Pointer, I64 | F64) => PointerOrI64,
93 (I64 | F64, Pointer) => PointerOrI64,
94
95 // PointerOrI64 wins over everything.
96 (PointerOrI64, _) => PointerOrI64,
97 (_, PointerOrI64) => PointerOrI64,
98
99 // Otherwise, `i64` wins.
100 (_, I64 | F64) | (I64 | F64, _) => I64,
101 }
102}
103
104impl From<Int> for WasmType {
105 fn from(i: Int) -> WasmType {
106 match i {
107 Int::U8 | Int::U16 | Int::U32 => WasmType::I32,
108 Int::U64 => WasmType::I64,
109 }
110 }
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)]
125pub 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
135impl 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 pub fn wasm_signature(&self, variant: AbiVariant, func: &Function) -> WasmSignature {
141 if let AbiVariant::GuestImportAsync = variant {
142 return WasmSignature {
143 params: vec![WasmType::Pointer; 2],
144 indirect_params: true,
145 results: vec![WasmType::I32],
146 retptr: true,
147 };
148 }
149
150 const MAX_FLAT_PARAMS: usize = 16;
151 const MAX_FLAT_RESULTS: usize = 1;
152
153 let mut params = Vec::new();
154 let mut indirect_params = false;
155 for (_, param) in func.params.iter() {
156 self.push_flat(param, &mut params);
157 }
158
159 if params.len() > MAX_FLAT_PARAMS {
160 params.truncate(0);
161 params.push(WasmType::Pointer);
162 indirect_params = true;
163 } else {
164 if matches!(
165 (&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 assert!(matches!(params[0], WasmType::I32));
180 params[0] = WasmType::Pointer;
181 }
182 }
183
184 match variant {
185 AbiVariant::GuestExportAsync => {
186 return WasmSignature {
187 params,
188 indirect_params,
189 results: vec![WasmType::Pointer],
190 retptr: false,
191 };
192 }
193 AbiVariant::GuestExportAsyncStackful => {
194 return WasmSignature {
195 params,
196 indirect_params,
197 results: Vec::new(),
198 retptr: false,
199 };
200 }
201 _ => {}
202 }
203
204 let mut results = Vec::new();
205 if let Some(ty) = &func.result {
206 self.push_flat(ty, &mut results)
207 }
208
209 let mut retptr = false;
210
211 // Rust/C don't support multi-value well right now, so if a function
212 // would have multiple results then instead truncate it. Imports take a
213 // return pointer to write into and exports return a pointer they wrote
214 // into.
215 if results.len() > MAX_FLAT_RESULTS {
216 retptr = true;
217 results.truncate(0);
218 match variant {
219 AbiVariant::GuestImport => {
220 params.push(WasmType::Pointer);
221 }
222 AbiVariant::GuestExport => {
223 results.push(WasmType::Pointer);
224 }
225 _ => unreachable!(),
226 }
227 }
228
229 WasmSignature {
230 params,
231 indirect_params,
232 results,
233 retptr,
234 }
235 }
236
237 /// Appends the flat wasm types representing `ty` onto the `result`
238 /// list provided.
239 pub fn push_flat(&self, ty: &Type, result: &mut Vec<WasmType>) {
240 match ty {
241 Type::Bool
242 | Type::S8
243 | Type::U8
244 | Type::S16
245 | Type::U16
246 | Type::S32
247 | Type::U32
248 | Type::Char => result.push(WasmType::I32),
249
250 Type::U64 | Type::S64 => result.push(WasmType::I64),
251 Type::F32 => result.push(WasmType::F32),
252 Type::F64 => result.push(WasmType::F64),
253 Type::String => {
254 result.push(WasmType::Pointer);
255 result.push(WasmType::Length);
256 }
257
258 Type::Id(id) => match &self.types[*id].kind {
259 TypeDefKind::Type(t) => self.push_flat(t, result),
260
261 TypeDefKind::Handle(Handle::Own(_) | Handle::Borrow(_)) => {
262 result.push(WasmType::I32);
263 }
264
265 TypeDefKind::Resource => todo!(),
266
267 TypeDefKind::Record(r) => {
268 for field in r.fields.iter() {
269 self.push_flat(&field.ty, result);
270 }
271 }
272
273 TypeDefKind::Tuple(t) => {
274 for ty in t.types.iter() {
275 self.push_flat(ty, result);
276 }
277 }
278
279 TypeDefKind::Flags(r) => {
280 for _ in 0..r.repr().count() {
281 result.push(WasmType::I32);
282 }
283 }
284
285 TypeDefKind::List(_) => {
286 result.push(WasmType::Pointer);
287 result.push(WasmType::Length);
288 }
289
290 TypeDefKind::Variant(v) => {
291 result.push(v.tag().into());
292 self.push_flat_variants(v.cases.iter().map(|c| c.ty.as_ref()), result);
293 }
294
295 TypeDefKind::Enum(e) => result.push(e.tag().into()),
296
297 TypeDefKind::Option(t) => {
298 result.push(WasmType::I32);
299 self.push_flat_variants([None, Some(t)], result);
300 }
301
302 TypeDefKind::Result(r) => {
303 result.push(WasmType::I32);
304 self.push_flat_variants([r.ok.as_ref(), r.err.as_ref()], result);
305 }
306
307 TypeDefKind::Future(_) => {
308 result.push(WasmType::I32);
309 }
310
311 TypeDefKind::Stream(_) => {
312 result.push(WasmType::I32);
313 }
314
315 TypeDefKind::ErrorContext => {
316 result.push(WasmType::I32);
317 }
318
319 TypeDefKind::Unknown => unreachable!(),
320 },
321 }
322 }
323
324 fn push_flat_variants<'a>(
325 &self,
326 tys: impl IntoIterator<Item = Option<&'a Type>>,
327 result: &mut Vec<WasmType>,
328 ) {
329 let mut temp = Vec::new();
330 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 for ty in tys {
339 if let Some(ty) = ty {
340 self.push_flat(ty, &mut temp);
341
342 for (i, ty) in temp.drain(..).enumerate() {
343 match result.get_mut(start + i) {
344 Some(prev) => *prev = join(*prev, ty),
345 None => result.push(ty),
346 }
347 }
348 }
349 }
350 }
351}