Skip to content

Commit

Permalink
support callback-less (AKA stackful) async lifts
Browse files Browse the repository at this point in the history
Signed-off-by: Joel Dice <joel.dice@fermyon.com>
  • Loading branch information
dicej committed Dec 9, 2024
1 parent 81c83e5 commit 66f0bbf
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 43 deletions.
27 changes: 24 additions & 3 deletions crates/wasmparser/src/validator/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use super::{
check_max,
component_types::{
AliasableResourceId, ComponentAnyTypeId, ComponentCoreInstanceTypeId,
Abi, AliasableResourceId, ComponentAnyTypeId, ComponentCoreInstanceTypeId,
ComponentCoreModuleTypeId, ComponentCoreTypeId, ComponentDefinedType,
ComponentDefinedTypeId, ComponentEntityType, ComponentFuncType, ComponentFuncTypeId,
ComponentInstanceType, ComponentInstanceTypeId, ComponentType, ComponentTypeId,
Expand Down Expand Up @@ -963,7 +963,21 @@ impl ComponentState {

// Lifting a function is for an export, so match the expected canonical ABI
// export signature
let info = ty.lower(types, false, options.contains(&CanonicalOption::Async));
let info = ty.lower(
types,
if options.contains(&CanonicalOption::Async) {
if options
.iter()
.any(|v| matches!(v, CanonicalOption::Callback(_)))
{
Abi::LiftAsync
} else {
Abi::LiftAsyncStackful
}
} else {
Abi::LiftSync
},
);
self.check_options(
Some(core_ty),
&info,
Expand Down Expand Up @@ -1012,7 +1026,14 @@ impl ComponentState {

// Lowering a function is for an import, so use a function type that matches
// the expected canonical ABI import signature.
let info = ty.lower(types, true, options.contains(&CanonicalOption::Async));
let info = ty.lower(
types,
if options.contains(&CanonicalOption::Async) {
Abi::LowerAsync
} else {
Abi::LowerSync
},
);

self.check_options(None, &info, &options, types, offset, features, true)?;

Expand Down
80 changes: 49 additions & 31 deletions crates/wasmparser/src/validator/component_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -891,21 +891,34 @@ impl TypeData for ComponentFuncType {
}
}

#[derive(Copy, Clone, Debug)]
pub(crate) enum Abi {
LowerSync,
LowerAsync,
LiftSync,
LiftAsync,
LiftAsyncStackful,
}

impl ComponentFuncType {
/// Lowers the component function type to core parameter and result types for the
/// canonical ABI.
pub(crate) fn lower(&self, types: &TypeList, is_lower: bool, async_: bool) -> LoweringInfo {
pub(crate) fn lower(&self, types: &TypeList, abi: Abi) -> LoweringInfo {
let mut info = LoweringInfo::default();

if async_ && is_lower {
for _ in 0..2 {
info.params.push(ValType::I32);
let is_lower = match abi {
Abi::LowerAsync => {
for _ in 0..2 {
info.params.push(ValType::I32);
}
info.results.push(ValType::I32);
info.requires_memory = true;
info.requires_realloc = self.results.iter().any(|(_, ty)| ty.contains_ptr(types));
return info;
}
info.results.push(ValType::I32);
info.requires_memory = true;
info.requires_realloc = self.results.iter().any(|(_, ty)| ty.contains_ptr(types));
return info;
}
Abi::LowerSync => true,
Abi::LiftSync | Abi::LiftAsync | Abi::LiftAsyncStackful => false,
};

for (_, ty) in self.params.iter() {
// Check to see if `ty` has a pointer somewhere in it, needed for
Expand Down Expand Up @@ -940,32 +953,37 @@ impl ComponentFuncType {
}
}

if async_ {
info.results.push(ValType::I32);
} else {
for (_, ty) in self.results.iter() {
// Results of lowered functions that contains pointers must be
// allocated by the callee meaning that realloc is required.
// Results of lifted function are allocated by the guest which
// means that no realloc option is necessary.
if is_lower && !info.requires_realloc {
info.requires_realloc = ty.contains_ptr(types);
}
match abi {
Abi::LowerAsync => unreachable!(),
Abi::LowerSync | Abi::LiftSync => {
for (_, ty) in self.results.iter() {
// Results of lowered functions that contains pointers must be
// allocated by the callee meaning that realloc is required.
// Results of lifted function are allocated by the guest which
// means that no realloc option is necessary.
if is_lower && !info.requires_realloc {
info.requires_realloc = ty.contains_ptr(types);
}

if !ty.push_wasm_types(types, &mut info.results) {
// Too many results to return directly, either a retptr parameter will be used (import)
// or a single pointer will be returned (export)
info.results.clear();
if is_lower {
info.params.max = MAX_LOWERED_TYPES;
assert!(info.params.push(ValType::I32));
} else {
assert!(info.results.push(ValType::I32));
if !ty.push_wasm_types(types, &mut info.results) {
// Too many results to return directly, either a retptr parameter will be used (import)
// or a single pointer will be returned (export)
info.results.clear();
if is_lower {
info.params.max = MAX_LOWERED_TYPES;
assert!(info.params.push(ValType::I32));
} else {
assert!(info.results.push(ValType::I32));
}
info.requires_memory = true;
break;
}
info.requires_memory = true;
break;
}
}
Abi::LiftAsync => {
info.results.push(ValType::I32);
}
Abi::LiftAsyncStackful => {}
}

// Memory is always required when realloc is required
Expand Down
2 changes: 1 addition & 1 deletion crates/wit-component/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ impl RequiredOptions {
ret |= RequiredOptions::REALLOC;
}
}
if abi == AbiVariant::GuestExportAsync {
if let AbiVariant::GuestExportAsync | AbiVariant::GuestExportAsyncStackful = abi {
ret |= RequiredOptions::ASYNC;
}
ret
Expand Down
10 changes: 10 additions & 0 deletions crates/wit-component/src/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -816,6 +816,8 @@ impl ExportMap {
let full_name = name;
let (abi, name) = if let Some(name) = names.async_name(name) {
(AbiVariant::GuestExportAsync, name)
} else if let Some(name) = names.async_stackful_name(name) {
(AbiVariant::GuestExportAsyncStackful, name)
} else {
(AbiVariant::GuestExport, name)
};
Expand Down Expand Up @@ -1081,6 +1083,7 @@ trait NameMangling {
fn subtask_drop(&self) -> Option<&str>;
fn callback_name<'a>(&self, s: &'a str) -> Option<&'a str>;
fn async_name<'a>(&self, s: &'a str) -> Option<&'a str>;
fn async_stackful_name<'a>(&self, s: &'a str) -> Option<&'a str>;
fn error_context_new(&self, s: &str) -> Option<StringEncoding>;
fn error_context_debug_message<'a>(&self, s: &'a str) -> Option<(StringEncoding, &'a str)>;
fn error_context_drop(&self) -> Option<&str>;
Expand Down Expand Up @@ -1176,6 +1179,10 @@ impl NameMangling for Standard {
_ = s;
None
}
fn async_stackful_name<'a>(&self, s: &'a str) -> Option<&'a str> {
_ = s;
None
}
fn error_context_new(&self, s: &str) -> Option<StringEncoding> {
_ = s;
None
Expand Down Expand Up @@ -1357,6 +1364,9 @@ impl NameMangling for Legacy {
fn async_name<'a>(&self, s: &'a str) -> Option<&'a str> {
s.strip_prefix("[async]")
}
fn async_stackful_name<'a>(&self, s: &'a str) -> Option<&'a str> {
s.strip_prefix("[async-stackful]")
}
fn error_context_new(&self, s: &str) -> Option<StringEncoding> {
parse_encoding(
s.strip_prefix("[error-context-new;encoding=")?
Expand Down
30 changes: 22 additions & 8 deletions crates/wit-parser/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ pub enum AbiVariant {
GuestExport,
GuestImportAsync,
GuestExportAsync,
GuestExportAsyncStackful,
}

impl Resolve {
Expand Down Expand Up @@ -164,7 +165,9 @@ impl Resolve {
(&func.kind, variant),
(
crate::FunctionKind::Method(_),
AbiVariant::GuestExport | AbiVariant::GuestExportAsync
AbiVariant::GuestExport
| AbiVariant::GuestExportAsync
| AbiVariant::GuestExportAsyncStackful
)
) {
// Guest exported methods always receive resource rep as first argument
Expand All @@ -178,13 +181,24 @@ impl Resolve {
}
}

if let AbiVariant::GuestExportAsync = variant {
return WasmSignature {
params,
indirect_params,
results: vec![WasmType::Pointer],
retptr: false,
};
match variant {
AbiVariant::GuestExportAsync => {
return WasmSignature {
params,
indirect_params,
results: vec![WasmType::Pointer],
retptr: false,
};
}
AbiVariant::GuestExportAsyncStackful => {
return WasmSignature {
params,
indirect_params,
results: Vec::new(),
retptr: false,
};
}
_ => {}
}

let mut results = Vec::new();
Expand Down

0 comments on commit 66f0bbf

Please sign in to comment.