1 //! Definition of the ABI of witx functions 2 //! 3 //! This module is intended to assist with code generators which are binding or 4 //! implementing APIs defined by `*.witx` files. THis module contains all 5 //! details necessary to implement the actual ABI of these functions so wasm 6 //! modules and hosts can communicate with one another. 7 //! 8 //! Each interface types function (a function defined in `*.witx`) currently has 9 //! a well-known wasm signature associated with it. There's then also a standard 10 //! way to convert from interface-types values (whose representation is defined 11 //! per-language) into this wasm API. This module is intended to assist with 12 //! this definition. 13 //! 14 //! Contained within are two primary functions, [`InterfaceFunc::call_wasm`] and 15 //! [`InterfaceFunc::call_interface`]. These functions implement the two ways to 16 //! interact with an interface types function, namely calling the raw wasm 17 //! version and calling the high-level version with interface types. These two 18 //! functions are fed a structure that implements [`Bindgen`]. An instance of 19 //! [`Bindgen`] receives instructions which are low-level implementation details 20 //! of how to convert to and from wasm types and interface types. Code 21 //! generators will need to implement the various instructions to support APIs. 22 23 use crate::{ 24 BuiltinType, Id, IntRepr, InterfaceFunc, InterfaceFuncParam, NamedType, Type, TypeRef, 25 }; 26 27 /// Enumerates wasm types used by interface types when lowering/lifting. 28 #[derive(Debug, Copy, Clone, PartialEq, Eq)] 29 pub enum WasmType { 30 I32, 31 I64, 32 F32, 33 F64, 34 // NOTE: we don't lower interface types to any other Wasm type, 35 // e.g. externref, so we don't need to define them here. 36 } 37 38 impl From<IntRepr> for WasmType { from(i: IntRepr) -> WasmType39 fn from(i: IntRepr) -> WasmType { 40 match i { 41 IntRepr::U8 | IntRepr::U16 | IntRepr::U32 => WasmType::I32, 42 IntRepr::U64 => WasmType::I64, 43 } 44 } 45 } 46 47 /// Possible ABIs for interface functions to have. 48 /// 49 /// Note that this is a stopgap until we have more of interface types. Interface 50 /// types functions do not have ABIs, they have APIs. For the meantime, however, 51 /// we mandate ABIs to ensure we can all talk to each other. 52 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 53 pub enum Abi { 54 /// Only stable ABI currently, and is the historical WASI ABI since it was 55 /// first created. 56 /// 57 /// Note that this ABI is limited notably in its return values where it can 58 /// only return 0 results or one `Result<T, enum>` lookalike. 59 Preview1, 60 } 61 62 // Helper macro for defining instructions without having to have tons of 63 // exhaustive `match` statements to update 64 macro_rules! def_instruction { 65 ( 66 $( #[$enum_attr:meta] )* 67 pub enum Instruction<'a> { 68 $( 69 $( #[$attr:meta] )* 70 $variant:ident $( { 71 $($field:ident : $field_ty:ty $(,)* )* 72 } )? 73 : 74 [$num_popped:expr] => [$num_pushed:expr], 75 )* 76 } 77 ) => { 78 $( #[$enum_attr] )* 79 pub enum Instruction<'a> { 80 $( 81 $( #[$attr] )* 82 $variant $( { 83 $( 84 $field : $field_ty, 85 )* 86 } )? , 87 )* 88 } 89 90 impl Instruction<'_> { 91 /// How many operands does this instruction pop from the stack? 92 #[allow(unused_variables)] 93 pub fn operands_len(&self) -> usize { 94 match self { 95 $( 96 Self::$variant $( { 97 $( 98 $field, 99 )* 100 } )? => $num_popped, 101 )* 102 } 103 } 104 105 /// How many results does this instruction push onto the stack? 106 #[allow(unused_variables)] 107 pub fn results_len(&self) -> usize { 108 match self { 109 $( 110 Self::$variant $( { 111 $( 112 $field, 113 )* 114 } )? => $num_pushed, 115 )* 116 } 117 } 118 } 119 }; 120 } 121 122 def_instruction! { 123 #[derive(Debug)] 124 pub enum Instruction<'a> { 125 /// Acquires the specified parameter and places it on the stack. 126 /// Depending on the context this may refer to wasm parameters or 127 /// interface types parameters. 128 GetArg { nth: usize } : [0] => [1], 129 /// Takes the value off the top of the stack and writes it into linear 130 /// memory. Pushes the address in linear memory as an `i32`. 131 AddrOf : [1] => [1], 132 /// Converts an interface type `char` value to a 32-bit integer 133 /// representing the unicode scalar value. 134 I32FromChar : [1] => [1], 135 /// Converts an interface type `u64` value to a wasm `i64`. 136 I64FromU64 : [1] => [1], 137 /// Converts an interface type `s64` value to a wasm `i64`. 138 I64FromS64 : [1] => [1], 139 /// Converts an interface type `u32` value to a wasm `i32`. 140 I32FromU32 : [1] => [1], 141 /// Converts an interface type `s32` value to a wasm `i32`. 142 I32FromS32 : [1] => [1], 143 /// Converts a language-specific `usize` value to a wasm `i32`. 144 I32FromUsize : [1] => [1], 145 /// Converts an interface type `u16` value to a wasm `i32`. 146 I32FromU16 : [1] => [1], 147 /// Converts an interface type `s16` value to a wasm `i32`. 148 I32FromS16 : [1] => [1], 149 /// Converts an interface type `u8` value to a wasm `i32`. 150 I32FromU8 : [1] => [1], 151 /// Converts an interface type `s8` value to a wasm `i32`. 152 I32FromS8 : [1] => [1], 153 /// Converts a language-specific C `char` value to a wasm `i32`. 154 I32FromChar8 : [1] => [1], 155 /// Converts a language-specific pointer value to a wasm `i32`. 156 I32FromPointer : [1] => [1], 157 /// Converts a language-specific pointer value to a wasm `i32`. 158 I32FromConstPointer : [1] => [1], 159 /// Converts a language-specific handle value to a wasm `i32`. 160 I32FromHandle { ty: &'a NamedType } : [1] => [1], 161 /// Converts a language-specific record-of-bools to the packed 162 /// representation as an `i32`. 163 I32FromBitflags { ty: &'a NamedType } : [1] => [1], 164 /// Converts a language-specific record-of-bools to the packed 165 /// representation as an `i64`. 166 I64FromBitflags { ty: &'a NamedType } : [1] => [1], 167 /// Converts an interface type list into its pointer/length, pushing 168 /// them both on the stack. 169 ListPointerLength : [1] => [2], 170 /// Pops two `i32` values from the stack and creates a list from them of 171 /// the specified type. The first operand is the pointer in linear 172 /// memory to the start of the list and the second operand is the 173 /// length. 174 ListFromPointerLength { ty: &'a TypeRef } : [2] => [1], 175 /// Conversion an interface type `f32` value to a wasm `f32`. 176 /// 177 /// This may be a noop for some implementations, but it's here in case the 178 /// native language representation of `f32` is different than the wasm 179 /// representation of `f32`. 180 F32FromIf32 : [1] => [1], 181 /// Conversion an interface type `f64` value to a wasm `f64`. 182 /// 183 /// This may be a noop for some implementations, but it's here in case the 184 /// native language representation of `f64` is different than the wasm 185 /// representation of `f64`. 186 F64FromIf64 : [1] => [1], 187 188 /// Represents a call to a raw WebAssembly API. The module/name are 189 /// provided inline as well as the types if necessary. 190 CallWasm { 191 module: &'a str, 192 name: &'a str, 193 params: &'a [WasmType], 194 results: &'a [WasmType], 195 } : [params.len()] => [results.len()], 196 197 /// Same as `CallWasm`, except the dual where an interface is being 198 /// called rather than a raw wasm function. 199 CallInterface { 200 module: &'a str, 201 func: &'a InterfaceFunc, 202 } : [func.params.len()] => [func.results.len()], 203 204 /// Converts a native wasm `i32` to an interface type `s8`. 205 /// 206 /// This will truncate the upper bits of the `i32`. 207 S8FromI32 : [1] => [1], 208 /// Converts a native wasm `i32` to an interface type `u8`. 209 /// 210 /// This will truncate the upper bits of the `i32`. 211 U8FromI32 : [1] => [1], 212 /// Converts a native wasm `i32` to an interface type `s16`. 213 /// 214 /// This will truncate the upper bits of the `i32`. 215 S16FromI32 : [1] => [1], 216 /// Converts a native wasm `i32` to an interface type `u16`. 217 /// 218 /// This will truncate the upper bits of the `i32`. 219 U16FromI32 : [1] => [1], 220 /// Converts a native wasm `i32` to an interface type `s32`. 221 S32FromI32 : [1] => [1], 222 /// Converts a native wasm `i32` to an interface type `u32`. 223 U32FromI32 : [1] => [1], 224 /// Converts a native wasm `i64` to an interface type `s64`. 225 S64FromI64 : [1] => [1], 226 /// Converts a native wasm `i64` to an interface type `u64`. 227 U64FromI64 : [1] => [1], 228 /// Converts a native wasm `i32` to an interface type `char`. 229 /// 230 /// It's safe to assume that the `i32` is indeed a valid unicode code point. 231 CharFromI32 : [1] => [1], 232 /// Converts a native wasm `i32` to a language-specific C `char`. 233 /// 234 /// This will truncate the upper bits of the `i32`. 235 Char8FromI32 : [1] => [1], 236 /// Converts a native wasm `i32` to a language-specific `usize`. 237 UsizeFromI32 : [1] => [1], 238 /// Converts a native wasm `f32` to an interface type `f32`. 239 If32FromF32 : [1] => [1], 240 /// Converts a native wasm `f64` to an interface type `f64`. 241 If64FromF64 : [1] => [1], 242 /// Converts a native wasm `i32` to an interface type `handle`. 243 HandleFromI32 { ty: &'a NamedType } : [1] => [1], 244 /// Converts a native wasm `i32` to a language-specific pointer. 245 PointerFromI32 { ty: &'a TypeRef }: [1] => [1], 246 /// Converts a native wasm `i32` to a language-specific pointer. 247 ConstPointerFromI32 { ty: &'a TypeRef } : [1] => [1], 248 /// Converts a native wasm `i32` to a language-specific record-of-bools. 249 BitflagsFromI32 { ty: &'a NamedType } : [1] => [1], 250 /// Converts a native wasm `i64` to a language-specific record-of-bools. 251 BitflagsFromI64 { ty: &'a NamedType } : [1] => [1], 252 /// Acquires the return pointer `n` and pushes an `i32` on the stack. 253 /// 254 /// Implementations of [`Bindgen`] may have [`Bindgen::allocate_space`] 255 /// called to reserve space in memory for the result of a computation to 256 /// get written. This instruction acquires a pointer to the space 257 /// reserved in `allocate_space`. 258 ReturnPointerGet { n: usize } : [0] => [1], 259 /// Loads the interface types value from an `i32` pointer popped from 260 /// the stack. 261 Load { ty: &'a NamedType } : [1] => [1], 262 /// Stores an interface types value into linear memory. The first 263 /// operand is the value to store and the second operand is the pointer 264 /// in linear memory to store it at. 265 Store { ty: &'a NamedType } : [2] => [0], 266 /// Pops a native wasm `i32` from the stack, as well as two blocks 267 /// internally from the code generator. 268 /// 269 /// If the value is 0 then the first "ok" block value should be used. 270 /// If the value is anything else then the second "err" block value 271 /// should be used, and the value is used as the error enum. 272 /// 273 /// Note that this is a special instruction matching the current ABI of 274 /// WASI and intentionally differs from the type-level grammar of 275 /// interface types results. 276 ResultLift : [1] => [1], 277 /// Pops a native interface value from the stack as well as two blocks 278 /// internally from the code generator. 279 /// 280 /// A `match` is performed on the value popped and the corresponding 281 /// block for ok/err is used depending on value. This pushes a single 282 /// `i32` onto the stack representing the error code for this result. 283 /// 284 /// Note that like `ResultLift` this is specialized to the current WASI 285 /// ABI. 286 ResultLower { 287 ok: Option<&'a TypeRef>, 288 err: Option<&'a TypeRef>, 289 } : [1] => [1], 290 /// Converts a native wasm `i32` to an interface type `enum` value. 291 /// 292 /// It's guaranteed that the interface type integer value is within 293 /// range for this enum's type. Additionally `ty` is guaranteed to be 294 /// enum-like as a `Variant` where all `case` arms have no associated 295 /// type with them. The purpose of this instruction is to convert a 296 /// native wasm integer into the enum type for the interface. 297 EnumLift { ty: &'a NamedType } : [1] => [1], 298 /// Converts an interface types enum value into a wasm `i32`. 299 EnumLower { ty: &'a NamedType } : [1] => [1], 300 /// Creates a tuple from the top `n` elements on the stack, pushing the 301 /// tuple onto the stack. 302 TupleLift { amt: usize } : [*amt] => [1], 303 /// Splits a tuple at the top of the stack into its `n` components, 304 /// pushing them all onto the stack. 305 TupleLower { amt: usize } : [1] => [*amt], 306 /// This is a special instruction specifically for the original ABI of 307 /// WASI. The raw return `i32` of a function is re-pushed onto the 308 /// stack for reuse. 309 ReuseReturn : [0] => [1], 310 /// Returns `amt` values on the stack. This is always the last 311 /// instruction. 312 Return { amt: usize } : [*amt] => [0], 313 /// This is a special instruction used at the entry of blocks used as 314 /// part of `ResultLower`, representing that the payload of that variant 315 /// being matched on should be pushed onto the stack. 316 VariantPayload : [0] => [1], 317 } 318 } 319 320 impl Abi { 321 /// Validates the parameters/results are representable in this ABI. 322 /// 323 /// Returns an error string if they're not representable or returns `Ok` if 324 /// they're indeed representable. validate( &self, _params: &[InterfaceFuncParam], results: &[InterfaceFuncParam], ) -> Result<(), String>325 pub fn validate( 326 &self, 327 _params: &[InterfaceFuncParam], 328 results: &[InterfaceFuncParam], 329 ) -> Result<(), String> { 330 assert_eq!(*self, Abi::Preview1); 331 match results.len() { 332 0 => {} 333 1 => match &**results[0].tref.type_() { 334 Type::Handle(_) | Type::Builtin(_) | Type::ConstPointer(_) | Type::Pointer(_) => {} 335 Type::Variant(v) => { 336 let (ok, err) = match v.as_expected() { 337 Some(pair) => pair, 338 None => return Err("invalid return type".to_string()), 339 }; 340 if let Some(ty) = ok { 341 match &**ty.type_() { 342 Type::Record(r) if r.is_tuple() => { 343 for member in r.members.iter() { 344 if !member.tref.named() { 345 return Err( 346 "only named types are allowed in results".to_string() 347 ); 348 } 349 } 350 } 351 _ => { 352 if !ty.named() { 353 return Err( 354 "only named types are allowed in results".to_string() 355 ); 356 } 357 } 358 } 359 } 360 if let Some(ty) = err { 361 if !ty.named() { 362 return Err("only named types are allowed in results".to_string()); 363 } 364 if let Type::Variant(v) = &**ty.type_() { 365 if v.is_enum() { 366 return Ok(()); 367 } 368 } 369 } 370 } 371 Type::Record(r) if r.bitflags_repr().is_some() => {} 372 Type::Record(_) | Type::List(_) => return Err("invalid return type".to_string()), 373 }, 374 _ => return Err("more than one result".to_string()), 375 } 376 Ok(()) 377 } 378 } 379 380 /// Trait for language implementors to use to generate glue code between native 381 /// WebAssembly signatures and interface types signatures. 382 /// 383 /// This is used as an implementation detail in interpreting the ABI between 384 /// interface types and wasm types. Eventually this will be driven by interface 385 /// types adapters themselves, but for now the ABI of a function dictates what 386 /// instructions are fed in. 387 /// 388 /// Types implementing `Bindgen` are incrementally fed `Instruction` values to 389 /// generate code for. Instructions operate like a stack machine where each 390 /// instruction has a list of inputs and a list of outputs (provided by the 391 /// `emit` function). 392 pub trait Bindgen { 393 /// The intermediate type for fragments of code for this type. 394 /// 395 /// For most languages `String` is a suitable intermediate type. 396 type Operand; 397 398 /// Emit code to implement the given instruction. 399 /// 400 /// Each operand is given in `operands` and can be popped off if ownership 401 /// is required. It's guaranteed that `operands` has the appropriate length 402 /// for the `inst` given, as specified with [`Instruction`]. 403 /// 404 /// Each result variable should be pushed onto `results`. This function must 405 /// push the appropriate number of results or binding generation will panic. emit( &mut self, inst: &Instruction<'_>, operands: &mut Vec<Self::Operand>, results: &mut Vec<Self::Operand>, )406 fn emit( 407 &mut self, 408 inst: &Instruction<'_>, 409 operands: &mut Vec<Self::Operand>, 410 results: &mut Vec<Self::Operand>, 411 ); 412 413 /// Allocates temporary space in linear memory indexed by `slot` with enough 414 /// space to store `ty`. 415 /// 416 /// This is called when calling some wasm functions where a return pointer 417 /// is needed. allocate_space(&mut self, slot: usize, ty: &NamedType)418 fn allocate_space(&mut self, slot: usize, ty: &NamedType); 419 420 /// Enters a new block of code to generate code for. 421 /// 422 /// This is currently exclusively used for constructing variants. When a 423 /// variant is constructed a block here will be pushed for each case of a 424 /// variant, generating the code necessary to translate a variant case. 425 /// 426 /// Blocks are completed with `finish_block` below. It's expected that `emit` 427 /// will always push code (if necessary) into the "current block", which is 428 /// updated by calling this method and `finish_block` below. push_block(&mut self)429 fn push_block(&mut self); 430 431 /// Indicates to the code generator that a block is completed, and the 432 /// `operand` specified was the resulting value of the block. 433 /// 434 /// This method will be used to compute the value of each arm of lifting a 435 /// variant. The `operand` will be `None` if the variant case didn't 436 /// actually have any type associated with it. Otherwise it will be `Some` 437 /// as the last value remaining on the stack representing the value 438 /// associated with a variant's `case`. 439 /// 440 /// It's expected that this will resume code generation in the previous 441 /// block before `push_block` was called. This must also save the results 442 /// of the current block internally for instructions like `ResultLift` to 443 /// use later. finish_block(&mut self, operand: Option<Self::Operand>)444 fn finish_block(&mut self, operand: Option<Self::Operand>); 445 } 446 447 impl InterfaceFunc { 448 /// Get the WebAssembly type signature for this interface function 449 /// 450 /// The first entry returned is the list of parameters and the second entry 451 /// is the list of results for the wasm function signature. wasm_signature(&self) -> (Vec<WasmType>, Vec<WasmType>)452 pub fn wasm_signature(&self) -> (Vec<WasmType>, Vec<WasmType>) { 453 assert_eq!(self.abi, Abi::Preview1); 454 let mut params = Vec::new(); 455 let mut results = Vec::new(); 456 for param in self.params.iter() { 457 match &**param.tref.type_() { 458 Type::Builtin(BuiltinType::S8) 459 | Type::Builtin(BuiltinType::U8 { .. }) 460 | Type::Builtin(BuiltinType::S16) 461 | Type::Builtin(BuiltinType::U16) 462 | Type::Builtin(BuiltinType::S32) 463 | Type::Builtin(BuiltinType::U32 { .. }) 464 | Type::Builtin(BuiltinType::Char) 465 | Type::Pointer(_) 466 | Type::ConstPointer(_) 467 | Type::Handle(_) 468 | Type::Variant(_) => params.push(WasmType::I32), 469 470 Type::Record(r) => match r.bitflags_repr() { 471 Some(repr) => params.push(WasmType::from(repr)), 472 None => params.push(WasmType::I32), 473 }, 474 475 Type::Builtin(BuiltinType::S64) | Type::Builtin(BuiltinType::U64) => { 476 params.push(WasmType::I64) 477 } 478 479 Type::Builtin(BuiltinType::F32) => params.push(WasmType::F32), 480 Type::Builtin(BuiltinType::F64) => params.push(WasmType::F64), 481 482 Type::List(_) => { 483 params.push(WasmType::I32); 484 params.push(WasmType::I32); 485 } 486 } 487 } 488 489 for param in self.results.iter() { 490 match &**param.tref.type_() { 491 Type::Builtin(BuiltinType::S8) 492 | Type::Builtin(BuiltinType::U8 { .. }) 493 | Type::Builtin(BuiltinType::S16) 494 | Type::Builtin(BuiltinType::U16) 495 | Type::Builtin(BuiltinType::S32) 496 | Type::Builtin(BuiltinType::U32 { .. }) 497 | Type::Builtin(BuiltinType::Char) 498 | Type::Pointer(_) 499 | Type::ConstPointer(_) 500 | Type::Handle(_) => results.push(WasmType::I32), 501 502 Type::Builtin(BuiltinType::S64) | Type::Builtin(BuiltinType::U64) => { 503 results.push(WasmType::I64) 504 } 505 506 Type::Builtin(BuiltinType::F32) => results.push(WasmType::F32), 507 Type::Builtin(BuiltinType::F64) => results.push(WasmType::F64), 508 509 Type::Record(r) => match r.bitflags_repr() { 510 Some(repr) => results.push(WasmType::from(repr)), 511 None => unreachable!(), 512 }, 513 Type::List(_) => unreachable!(), 514 515 Type::Variant(v) => { 516 results.push(match v.tag_repr { 517 IntRepr::U64 => WasmType::I64, 518 IntRepr::U32 | IntRepr::U16 | IntRepr::U8 => WasmType::I32, 519 }); 520 if v.is_enum() { 521 continue; 522 } 523 // return pointer 524 if let Some(ty) = &v.cases[0].tref { 525 match &**ty.type_() { 526 Type::Record(r) if r.is_tuple() => { 527 for _ in 0..r.members.len() { 528 params.push(WasmType::I32); 529 } 530 } 531 _ => params.push(WasmType::I32), 532 } 533 } 534 } 535 } 536 } 537 (params, results) 538 } 539 540 /// Generates an abstract sequence of instructions which represents this 541 /// function being adapted as an imported function. 542 /// 543 /// The instructions here, when executed, will emulate a language with 544 /// interface types calling the concrete wasm implementation. The parameters 545 /// for the returned instruction sequence are the language's own 546 /// interface-types parameters. One instruction in the instruction stream 547 /// will be a `Call` which represents calling the actual raw wasm function 548 /// signature. 549 /// 550 /// This function is useful, for example, if you're building a language 551 /// generator for WASI bindings. This will document how to translate 552 /// language-specific values into the wasm types to call a WASI function, 553 /// and it will also automatically convert the results of the WASI function 554 /// back to a language-specific value. call_wasm(&self, module: &Id, bindgen: &mut impl Bindgen)555 pub fn call_wasm(&self, module: &Id, bindgen: &mut impl Bindgen) { 556 assert_eq!(self.abi, Abi::Preview1); 557 Generator { 558 bindgen, 559 operands: vec![], 560 results: vec![], 561 stack: vec![], 562 } 563 .call_wasm(module, self); 564 } 565 566 /// This is the dual of [`InterfaceFunc::call_wasm`], except that instead of 567 /// calling a wasm signature it generates code to come from a wasm signature 568 /// and call an interface types signature. call_interface(&self, module: &Id, bindgen: &mut impl Bindgen)569 pub fn call_interface(&self, module: &Id, bindgen: &mut impl Bindgen) { 570 assert_eq!(self.abi, Abi::Preview1); 571 Generator { 572 bindgen, 573 operands: vec![], 574 results: vec![], 575 stack: vec![], 576 } 577 .call_interface(module, self); 578 } 579 } 580 581 struct Generator<'a, B: Bindgen> { 582 bindgen: &'a mut B, 583 operands: Vec<B::Operand>, 584 results: Vec<B::Operand>, 585 stack: Vec<B::Operand>, 586 } 587 588 impl<B: Bindgen> Generator<'_, B> { call_wasm(&mut self, module: &Id, func: &InterfaceFunc)589 fn call_wasm(&mut self, module: &Id, func: &InterfaceFunc) { 590 // Translate all parameters which are interface values by lowering them 591 // to their wasm types. 592 for (nth, param) in func.params.iter().enumerate() { 593 self.emit(&Instruction::GetArg { nth }); 594 self.lower(¶m.tref, None); 595 } 596 597 // If necessary for our ABI, insert return pointers for any returned 598 // values through a result. 599 assert!(func.results.len() < 2); 600 if let Some(result) = func.results.get(0) { 601 self.prep_return_pointer(&result.tref.type_()); 602 } 603 604 let (params, results) = func.wasm_signature(); 605 self.emit(&Instruction::CallWasm { 606 module: module.as_str(), 607 name: func.name.as_str(), 608 params: ¶ms, 609 results: &results, 610 }); 611 612 // Lift the return value if one is present. 613 if let Some(result) = func.results.get(0) { 614 self.lift(&result.tref, true); 615 } 616 617 self.emit(&Instruction::Return { 618 amt: func.results.len(), 619 }); 620 } 621 call_interface(&mut self, module: &Id, func: &InterfaceFunc)622 fn call_interface(&mut self, module: &Id, func: &InterfaceFunc) { 623 // Lift all wasm parameters into interface types first. 624 // 625 // Note that consuming arguments is somewhat janky right now by manually 626 // giving lists a second argument for their length. In the future we'll 627 // probably want to refactor the `lift` function to internally know how 628 // to consume arguments. 629 let mut nth = 0; 630 for param in func.params.iter() { 631 self.emit(&Instruction::GetArg { nth }); 632 nth += 1; 633 if let Type::List(_) = &**param.tref.type_() { 634 self.emit(&Instruction::GetArg { nth }); 635 nth += 1; 636 } 637 self.lift(¶m.tref, false); 638 } 639 640 self.emit(&Instruction::CallInterface { 641 module: module.as_str(), 642 func, 643 }); 644 645 // Like above the current ABI only has at most one result, so lower it 646 // here if necessary. 647 if let Some(result) = func.results.get(0) { 648 self.lower(&result.tref, Some(&mut nth)); 649 } 650 651 let (_params, results) = func.wasm_signature(); 652 self.emit(&Instruction::Return { amt: results.len() }); 653 } 654 emit(&mut self, inst: &Instruction<'_>)655 fn emit(&mut self, inst: &Instruction<'_>) { 656 self.operands.clear(); 657 self.results.clear(); 658 659 let operands_len = inst.operands_len(); 660 assert!( 661 self.stack.len() >= operands_len, 662 "not enough operands on stack for {:?}", 663 inst 664 ); 665 self.operands 666 .extend(self.stack.drain((self.stack.len() - operands_len)..)); 667 self.results.reserve(inst.results_len()); 668 669 self.bindgen 670 .emit(inst, &mut self.operands, &mut self.results); 671 672 assert_eq!( 673 self.results.len(), 674 inst.results_len(), 675 "{:?} expected {} results, got {}", 676 inst, 677 inst.results_len(), 678 self.results.len() 679 ); 680 self.stack.extend(self.results.drain(..)); 681 } 682 lower(&mut self, ty: &TypeRef, retptr: Option<&mut usize>)683 fn lower(&mut self, ty: &TypeRef, retptr: Option<&mut usize>) { 684 use Instruction::*; 685 match &**ty.type_() { 686 Type::Builtin(BuiltinType::S8) => self.emit(&I32FromS8), 687 Type::Builtin(BuiltinType::U8 { lang_c_char: true }) => self.emit(&I32FromChar8), 688 Type::Builtin(BuiltinType::U8 { lang_c_char: false }) => self.emit(&I32FromU8), 689 Type::Builtin(BuiltinType::S16) => self.emit(&I32FromS16), 690 Type::Builtin(BuiltinType::U16) => self.emit(&I32FromU16), 691 Type::Builtin(BuiltinType::S32) => self.emit(&I32FromS32), 692 Type::Builtin(BuiltinType::U32 { 693 lang_ptr_size: true, 694 }) => self.emit(&I32FromUsize), 695 Type::Builtin(BuiltinType::U32 { 696 lang_ptr_size: false, 697 }) => self.emit(&I32FromU32), 698 Type::Builtin(BuiltinType::S64) => self.emit(&I64FromS64), 699 Type::Builtin(BuiltinType::U64) => self.emit(&I64FromU64), 700 Type::Builtin(BuiltinType::Char) => self.emit(&I32FromChar), 701 Type::Pointer(_) => self.emit(&I32FromPointer), 702 Type::ConstPointer(_) => self.emit(&I32FromConstPointer), 703 Type::Handle(_) => self.emit(&I32FromHandle { 704 ty: match ty { 705 TypeRef::Name(ty) => ty, 706 _ => unreachable!(), 707 }, 708 }), 709 Type::Record(r) => { 710 let ty = match ty { 711 TypeRef::Name(ty) => ty, 712 _ => unreachable!(), 713 }; 714 match r.bitflags_repr() { 715 Some(IntRepr::U64) => self.emit(&I64FromBitflags { ty }), 716 Some(_) => self.emit(&I32FromBitflags { ty }), 717 None => self.emit(&AddrOf), 718 } 719 } 720 Type::Variant(v) => { 721 // Enum-like variants are simply lowered to their discriminant. 722 if v.is_enum() { 723 return self.emit(&EnumLower { 724 ty: match ty { 725 TypeRef::Name(n) => n, 726 _ => unreachable!(), 727 }, 728 }); 729 } 730 731 // If this variant is in the return position then it's special, 732 // otherwise it's an argument and we just pass the address. 733 let retptr = match retptr { 734 Some(ptr) => ptr, 735 None => return self.emit(&AddrOf), 736 }; 737 738 // For the return position we emit some blocks to lower the 739 // ok/err payloads which means that in the ok branch we're 740 // storing to out-params and in the err branch we're simply 741 // lowering the error enum. 742 // 743 // Note that this is all very specific to the current WASI ABI. 744 let (ok, err) = v.as_expected().unwrap(); 745 self.bindgen.push_block(); 746 if let Some(ok) = ok { 747 self.emit(&VariantPayload); 748 let store = |me: &mut Self, ty: &TypeRef, n| { 749 me.emit(&GetArg { nth: *retptr + n }); 750 match ty { 751 TypeRef::Name(ty) => me.emit(&Store { ty }), 752 _ => unreachable!(), 753 } 754 }; 755 match &**ok.type_() { 756 Type::Record(r) if r.is_tuple() => { 757 self.emit(&TupleLower { 758 amt: r.members.len(), 759 }); 760 // Note that `rev()` is used here due to the order 761 // that tuples are pushed onto the stack and how we 762 // consume the last item first from the stack. 763 for (i, member) in r.members.iter().enumerate().rev() { 764 store(self, &member.tref, i); 765 } 766 } 767 _ => store(self, ok, 0), 768 } 769 }; 770 self.bindgen.finish_block(None); 771 772 self.bindgen.push_block(); 773 let err_expr = if let Some(ty) = err { 774 self.emit(&VariantPayload); 775 self.lower(ty, None); 776 Some(self.stack.pop().unwrap()) 777 } else { 778 None 779 }; 780 self.bindgen.finish_block(err_expr); 781 782 self.emit(&ResultLower { ok, err }); 783 } 784 Type::Builtin(BuiltinType::F32) => self.emit(&F32FromIf32), 785 Type::Builtin(BuiltinType::F64) => self.emit(&F64FromIf64), 786 Type::List(_) => self.emit(&ListPointerLength), 787 } 788 } 789 prep_return_pointer(&mut self, ty: &Type)790 fn prep_return_pointer(&mut self, ty: &Type) { 791 // Return pointers are only needed for `Result<T, _>`... 792 let variant = match ty { 793 Type::Variant(v) => v, 794 _ => return, 795 }; 796 // ... and only if `T` is actually present in `Result<T, _>` 797 let ok = match &variant.cases[0].tref { 798 Some(t) => t, 799 None => return, 800 }; 801 802 // Tuples have each individual item in a separate return pointer while 803 // all other types go through a singular return pointer. 804 let mut n = 0; 805 let mut prep = |ty: &TypeRef| { 806 match ty { 807 TypeRef::Name(ty) => self.bindgen.allocate_space(n, ty), 808 _ => unreachable!(), 809 } 810 self.emit(&Instruction::ReturnPointerGet { n }); 811 n += 1; 812 }; 813 match &**ok.type_() { 814 Type::Record(r) if r.is_tuple() => { 815 for member in r.members.iter() { 816 prep(&member.tref); 817 } 818 } 819 _ => prep(ok), 820 } 821 } 822 823 // Note that in general everything in this function is the opposite of the 824 // `lower` function above. This is intentional and should be kept this way! lift(&mut self, ty: &TypeRef, is_return: bool)825 fn lift(&mut self, ty: &TypeRef, is_return: bool) { 826 use Instruction::*; 827 match &**ty.type_() { 828 Type::Builtin(BuiltinType::S8) => self.emit(&S8FromI32), 829 Type::Builtin(BuiltinType::U8 { lang_c_char: true }) => self.emit(&Char8FromI32), 830 Type::Builtin(BuiltinType::U8 { lang_c_char: false }) => self.emit(&U8FromI32), 831 Type::Builtin(BuiltinType::S16) => self.emit(&S16FromI32), 832 Type::Builtin(BuiltinType::U16) => self.emit(&U16FromI32), 833 Type::Builtin(BuiltinType::S32) => self.emit(&S32FromI32), 834 Type::Builtin(BuiltinType::U32 { 835 lang_ptr_size: true, 836 }) => self.emit(&UsizeFromI32), 837 Type::Builtin(BuiltinType::U32 { 838 lang_ptr_size: false, 839 }) => self.emit(&U32FromI32), 840 Type::Builtin(BuiltinType::S64) => self.emit(&S64FromI64), 841 Type::Builtin(BuiltinType::U64) => self.emit(&U64FromI64), 842 Type::Builtin(BuiltinType::Char) => self.emit(&CharFromI32), 843 Type::Builtin(BuiltinType::F32) => self.emit(&If32FromF32), 844 Type::Builtin(BuiltinType::F64) => self.emit(&If64FromF64), 845 Type::Pointer(ty) => self.emit(&PointerFromI32 { ty }), 846 Type::ConstPointer(ty) => self.emit(&ConstPointerFromI32 { ty }), 847 Type::Handle(_) => self.emit(&HandleFromI32 { 848 ty: match ty { 849 TypeRef::Name(ty) => ty, 850 _ => unreachable!(), 851 }, 852 }), 853 Type::Variant(v) => { 854 if v.is_enum() { 855 return self.emit(&EnumLift { 856 ty: match ty { 857 TypeRef::Name(n) => n, 858 _ => unreachable!(), 859 }, 860 }); 861 } else if !is_return { 862 return self.emit(&Load { 863 ty: match ty { 864 TypeRef::Name(n) => n, 865 _ => unreachable!(), 866 }, 867 }); 868 } 869 870 let (ok, err) = v.as_expected().unwrap(); 871 self.bindgen.push_block(); 872 let ok_expr = if let Some(ok) = ok { 873 let mut n = 0; 874 let mut load = |ty: &TypeRef| { 875 self.emit(&ReturnPointerGet { n }); 876 n += 1; 877 match ty { 878 TypeRef::Name(ty) => self.emit(&Load { ty }), 879 _ => unreachable!(), 880 } 881 }; 882 match &**ok.type_() { 883 Type::Record(r) if r.is_tuple() => { 884 for member in r.members.iter() { 885 load(&member.tref); 886 } 887 self.emit(&TupleLift { 888 amt: r.members.len(), 889 }); 890 } 891 _ => load(ok), 892 } 893 Some(self.stack.pop().unwrap()) 894 } else { 895 None 896 }; 897 self.bindgen.finish_block(ok_expr); 898 899 self.bindgen.push_block(); 900 let err_expr = if let Some(ty) = err { 901 self.emit(&ReuseReturn); 902 self.lift(ty, false); 903 Some(self.stack.pop().unwrap()) 904 } else { 905 None 906 }; 907 self.bindgen.finish_block(err_expr); 908 909 self.emit(&ResultLift); 910 } 911 Type::Record(r) => { 912 let ty = match ty { 913 TypeRef::Name(ty) => ty, 914 _ => unreachable!(), 915 }; 916 match r.bitflags_repr() { 917 Some(IntRepr::U64) => self.emit(&BitflagsFromI64 { ty }), 918 Some(_) => self.emit(&BitflagsFromI32 { ty }), 919 None => self.emit(&Load { ty }), 920 } 921 } 922 Type::List(ty) => self.emit(&ListFromPointerLength { ty }), 923 } 924 } 925 } 926