| 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 | fn 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 | |
| 104 | impl 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)] |
| 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 | 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 | for ty in func.results.iter_types() { |
| 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 | } |
| 352 | |