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