Deduplicate some size/align calculations (#4658)
This commit is an effort to reduce the amount of complexity around managing the size/alignment calculations of types in the canonical ABI. Previously the logic for the size/alignment of a type was spread out across a number of locations. While each individual calculation is not really the most complicated thing in the world having the duplication in so many places was constantly worrying me. I've opted in this commit to centralize all of this within the runtime at least, and now there's only one "duplicate" of this information in the fuzzing infrastructure which is to some degree less important to deduplicate. This commit introduces a new `CanonicalAbiInfo` type to house all abi size/align information for both memory32 and memory64. This new type is then used pervasively throughout fused adapter compilation, dynamic `Val` management, and typed functions. This type was also able to reduce the complexity of the macro-generated code meaning that even `wasmtime-component-macro` is performing less math than it was before. One other major feature of this commit is that this ABI information is now saved within a `ComponentTypes` structure. This avoids recursive querying of size/align information frequently and instead effectively caching it. This was a worry I had for the fused adapter compiler which frequently sought out size/align information and would recursively descend each type tree each time. The `fact-valid-module` fuzzer is now nearly 10x faster in terms of iterations/s which I suspect is due to this caching.
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -3559,6 +3559,7 @@ dependencies = [
|
|||||||
"wasmparser",
|
"wasmparser",
|
||||||
"wasmprinter",
|
"wasmprinter",
|
||||||
"wasmtime-environ",
|
"wasmtime-environ",
|
||||||
|
"wat",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -298,7 +298,7 @@ fn expand_record_for_component_type(
|
|||||||
let mut lower_generic_params = TokenStream::new();
|
let mut lower_generic_params = TokenStream::new();
|
||||||
let mut lower_generic_args = TokenStream::new();
|
let mut lower_generic_args = TokenStream::new();
|
||||||
let mut lower_field_declarations = TokenStream::new();
|
let mut lower_field_declarations = TokenStream::new();
|
||||||
let mut sizes = TokenStream::new();
|
let mut abi_list = TokenStream::new();
|
||||||
let mut unique_types = HashSet::new();
|
let mut unique_types = HashSet::new();
|
||||||
|
|
||||||
for (index, syn::Field { ident, ty, .. }) in fields.iter().enumerate() {
|
for (index, syn::Field { ident, ty, .. }) in fields.iter().enumerate() {
|
||||||
@@ -309,24 +309,13 @@ fn expand_record_for_component_type(
|
|||||||
|
|
||||||
lower_field_declarations.extend(quote!(#ident: #generic,));
|
lower_field_declarations.extend(quote!(#ident: #generic,));
|
||||||
|
|
||||||
sizes.extend(quote!(
|
abi_list.extend(quote!(
|
||||||
size = #internal::align_to(size, <#ty as wasmtime::component::ComponentType>::ALIGN32);
|
<#ty as wasmtime::component::ComponentType>::ABI,
|
||||||
size += <#ty as wasmtime::component::ComponentType>::SIZE32;
|
|
||||||
));
|
));
|
||||||
|
|
||||||
unique_types.insert(ty);
|
unique_types.insert(ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
let alignments = unique_types
|
|
||||||
.into_iter()
|
|
||||||
.map(|ty| {
|
|
||||||
let align = quote!(<#ty as wasmtime::component::ComponentType>::ALIGN32);
|
|
||||||
quote!(if #align > align {
|
|
||||||
align = #align;
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<TokenStream>();
|
|
||||||
|
|
||||||
let generics = add_trait_bounds(generics, parse_quote!(wasmtime::component::ComponentType));
|
let generics = add_trait_bounds(generics, parse_quote!(wasmtime::component::ComponentType));
|
||||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||||
let lower = format_ident!("Lower{}", name);
|
let lower = format_ident!("Lower{}", name);
|
||||||
@@ -358,17 +347,8 @@ fn expand_record_for_component_type(
|
|||||||
unsafe impl #impl_generics wasmtime::component::ComponentType for #name #ty_generics #where_clause {
|
unsafe impl #impl_generics wasmtime::component::ComponentType for #name #ty_generics #where_clause {
|
||||||
type Lower = #lower <#lower_generic_args>;
|
type Lower = #lower <#lower_generic_args>;
|
||||||
|
|
||||||
const SIZE32: usize = {
|
const ABI: #internal::CanonicalAbiInfo =
|
||||||
let mut size = 0;
|
#internal::CanonicalAbiInfo::record_static(&[#abi_list]);
|
||||||
#sizes
|
|
||||||
#internal::align_to(size, Self::ALIGN32)
|
|
||||||
};
|
|
||||||
|
|
||||||
const ALIGN32: u32 = {
|
|
||||||
let mut align = 1;
|
|
||||||
#alignments
|
|
||||||
align
|
|
||||||
};
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn typecheck(
|
fn typecheck(
|
||||||
@@ -429,7 +409,7 @@ impl Expander for LiftExpander {
|
|||||||
loads.extend(quote!(#ident: <#ty as wasmtime::component::Lift>::load(
|
loads.extend(quote!(#ident: <#ty as wasmtime::component::Lift>::load(
|
||||||
memory,
|
memory,
|
||||||
&bytes
|
&bytes
|
||||||
[#internal::next_field::<#ty>(&mut offset)..]
|
[<#ty as wasmtime::component::ComponentType>::ABI.next_field32_size(&mut offset)..]
|
||||||
[..<#ty as wasmtime::component::ComponentType>::SIZE32]
|
[..<#ty as wasmtime::component::ComponentType>::SIZE32]
|
||||||
)?,));
|
)?,));
|
||||||
}
|
}
|
||||||
@@ -514,8 +494,6 @@ impl Expander for LiftExpander {
|
|||||||
DiscriminantSize::Size4 => quote!(u32::from_le_bytes(bytes[0..4].try_into()?)),
|
DiscriminantSize::Size4 => quote!(u32::from_le_bytes(bytes[0..4].try_into()?)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let payload_offset = usize::from(discriminant_size);
|
|
||||||
|
|
||||||
let expanded = quote! {
|
let expanded = quote! {
|
||||||
unsafe impl #impl_generics wasmtime::component::Lift for #name #ty_generics #where_clause {
|
unsafe impl #impl_generics wasmtime::component::Lift for #name #ty_generics #where_clause {
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -535,7 +513,8 @@ impl Expander for LiftExpander {
|
|||||||
let align = <Self as wasmtime::component::ComponentType>::ALIGN32;
|
let align = <Self as wasmtime::component::ComponentType>::ALIGN32;
|
||||||
debug_assert!((bytes.as_ptr() as usize) % (align as usize) == 0);
|
debug_assert!((bytes.as_ptr() as usize) % (align as usize) == 0);
|
||||||
let discrim = #from_bytes;
|
let discrim = #from_bytes;
|
||||||
let payload = &bytes[#internal::align_to(#payload_offset, align)..];
|
let payload_offset = <Self as #internal::ComponentVariant>::PAYLOAD_OFFSET32;
|
||||||
|
let payload = &bytes[payload_offset..];
|
||||||
Ok(match discrim {
|
Ok(match discrim {
|
||||||
#loads
|
#loads
|
||||||
discrim => #internal::anyhow::bail!("unexpected discriminant: {}", discrim),
|
discrim => #internal::anyhow::bail!("unexpected discriminant: {}", discrim),
|
||||||
@@ -575,7 +554,9 @@ impl Expander for LowerExpander {
|
|||||||
)?;));
|
)?;));
|
||||||
|
|
||||||
stores.extend(quote!(wasmtime::component::Lower::store(
|
stores.extend(quote!(wasmtime::component::Lower::store(
|
||||||
&self.#ident, memory, #internal::next_field::<#ty>(&mut offset)
|
&self.#ident,
|
||||||
|
memory,
|
||||||
|
<#ty as wasmtime::component::ComponentType>::ABI.next_field32_size(&mut offset),
|
||||||
)?;));
|
)?;));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -640,10 +621,7 @@ impl Expander for LowerExpander {
|
|||||||
lower = quote!(value.lower(store, options, #internal::map_maybe_uninit!(dst.payload.#ident)));
|
lower = quote!(value.lower(store, options, #internal::map_maybe_uninit!(dst.payload.#ident)));
|
||||||
store = quote!(value.store(
|
store = quote!(value.store(
|
||||||
memory,
|
memory,
|
||||||
offset + #internal::align_to(
|
offset + <Self as #internal::ComponentVariant>::PAYLOAD_OFFSET32,
|
||||||
#discriminant_size,
|
|
||||||
<Self as wasmtime::component::ComponentType>::ALIGN32
|
|
||||||
)
|
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
pattern = quote!(Self::#ident);
|
pattern = quote!(Self::#ident);
|
||||||
@@ -749,7 +727,7 @@ impl Expander for ComponentTypeExpander {
|
|||||||
&self,
|
&self,
|
||||||
name: &syn::Ident,
|
name: &syn::Ident,
|
||||||
generics: &syn::Generics,
|
generics: &syn::Generics,
|
||||||
discriminant_size: DiscriminantSize,
|
_discriminant_size: DiscriminantSize,
|
||||||
cases: &[VariantCase],
|
cases: &[VariantCase],
|
||||||
style: VariantStyle,
|
style: VariantStyle,
|
||||||
) -> Result<TokenStream> {
|
) -> Result<TokenStream> {
|
||||||
@@ -760,7 +738,7 @@ impl Expander for ComponentTypeExpander {
|
|||||||
let mut lower_payload_generic_args = TokenStream::new();
|
let mut lower_payload_generic_args = TokenStream::new();
|
||||||
let mut lower_payload_case_declarations = TokenStream::new();
|
let mut lower_payload_case_declarations = TokenStream::new();
|
||||||
let mut lower_generic_args = TokenStream::new();
|
let mut lower_generic_args = TokenStream::new();
|
||||||
let mut sizes = TokenStream::new();
|
let mut abi_list = TokenStream::new();
|
||||||
let mut unique_types = HashSet::new();
|
let mut unique_types = HashSet::new();
|
||||||
|
|
||||||
for (index, VariantCase { attrs, ident, ty }) in cases.iter().enumerate() {
|
for (index, VariantCase { attrs, ident, ty }) in cases.iter().enumerate() {
|
||||||
@@ -776,12 +754,7 @@ impl Expander for ComponentTypeExpander {
|
|||||||
let name = rename.unwrap_or_else(|| Literal::string(&ident.to_string()));
|
let name = rename.unwrap_or_else(|| Literal::string(&ident.to_string()));
|
||||||
|
|
||||||
if let Some(ty) = ty {
|
if let Some(ty) = ty {
|
||||||
sizes.extend({
|
abi_list.extend(quote!(<#ty as wasmtime::component::ComponentType>::ABI,));
|
||||||
let size = quote!(<#ty as wasmtime::component::ComponentType>::SIZE32);
|
|
||||||
quote!(if #size > size {
|
|
||||||
size = #size;
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
case_names_and_checks.extend(match style {
|
case_names_and_checks.extend(match style {
|
||||||
VariantStyle::Variant => {
|
VariantStyle::Variant => {
|
||||||
@@ -808,6 +781,7 @@ impl Expander for ComponentTypeExpander {
|
|||||||
|
|
||||||
unique_types.insert(ty);
|
unique_types.insert(ty);
|
||||||
} else {
|
} else {
|
||||||
|
abi_list.extend(quote!(<() as wasmtime::component::ComponentType>::ABI,));
|
||||||
case_names_and_checks.extend(match style {
|
case_names_and_checks.extend(match style {
|
||||||
VariantStyle::Variant => {
|
VariantStyle::Variant => {
|
||||||
quote!((#name, <() as wasmtime::component::ComponentType>::typecheck),)
|
quote!((#name, <() as wasmtime::component::ComponentType>::typecheck),)
|
||||||
@@ -824,16 +798,6 @@ impl Expander for ComponentTypeExpander {
|
|||||||
lower_payload_case_declarations.extend(quote!(_dummy: ()));
|
lower_payload_case_declarations.extend(quote!(_dummy: ()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let alignments = unique_types
|
|
||||||
.into_iter()
|
|
||||||
.map(|ty| {
|
|
||||||
let align = quote!(<#ty as wasmtime::component::ComponentType>::ALIGN32);
|
|
||||||
quote!(if #align > align {
|
|
||||||
align = #align;
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<TokenStream>();
|
|
||||||
|
|
||||||
let typecheck = match style {
|
let typecheck = match style {
|
||||||
VariantStyle::Variant => quote!(typecheck_variant),
|
VariantStyle::Variant => quote!(typecheck_variant),
|
||||||
VariantStyle::Union => quote!(typecheck_union),
|
VariantStyle::Union => quote!(typecheck_union),
|
||||||
@@ -844,7 +808,6 @@ impl Expander for ComponentTypeExpander {
|
|||||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||||
let lower = format_ident!("Lower{}", name);
|
let lower = format_ident!("Lower{}", name);
|
||||||
let lower_payload = format_ident!("LowerPayload{}", name);
|
let lower_payload = format_ident!("LowerPayload{}", name);
|
||||||
let discriminant_size = u32::from(discriminant_size);
|
|
||||||
|
|
||||||
// You may wonder why we make the types of all the fields of the #lower struct and #lower_payload union
|
// You may wonder why we make the types of all the fields of the #lower struct and #lower_payload union
|
||||||
// generic. This is to work around a [normalization bug in
|
// generic. This is to work around a [normalization bug in
|
||||||
@@ -882,20 +845,12 @@ impl Expander for ComponentTypeExpander {
|
|||||||
#internal::#typecheck(ty, types, &[#case_names_and_checks])
|
#internal::#typecheck(ty, types, &[#case_names_and_checks])
|
||||||
}
|
}
|
||||||
|
|
||||||
const SIZE32: usize = {
|
const ABI: #internal::CanonicalAbiInfo =
|
||||||
let mut size = 0;
|
#internal::CanonicalAbiInfo::variant_static(&[#abi_list]);
|
||||||
#sizes
|
}
|
||||||
#internal::align_to(
|
|
||||||
#internal::align_to(#discriminant_size as usize, Self::ALIGN32) + size,
|
|
||||||
Self::ALIGN32
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
const ALIGN32: u32 = {
|
unsafe impl #impl_generics #internal::ComponentVariant for #name #ty_generics #where_clause {
|
||||||
let mut align = #discriminant_size;
|
const CASES: &'static [#internal::CanonicalAbiInfo] = &[#abi_list];
|
||||||
#alignments
|
|
||||||
align
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/// Represents the possible sizes in bytes of the discriminant of a variant type in the component model
|
/// Represents the possible sizes in bytes of the discriminant of a variant type in the component model
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum DiscriminantSize {
|
pub enum DiscriminantSize {
|
||||||
/// 8-bit discriminant
|
/// 8-bit discriminant
|
||||||
Size1,
|
Size1,
|
||||||
@@ -11,7 +11,7 @@ pub enum DiscriminantSize {
|
|||||||
|
|
||||||
impl DiscriminantSize {
|
impl DiscriminantSize {
|
||||||
/// Calculate the size of discriminant needed to represent a variant with the specified number of cases.
|
/// Calculate the size of discriminant needed to represent a variant with the specified number of cases.
|
||||||
pub fn from_count(count: usize) -> Option<Self> {
|
pub const fn from_count(count: usize) -> Option<Self> {
|
||||||
if count <= 0xFF {
|
if count <= 0xFF {
|
||||||
Some(Self::Size1)
|
Some(Self::Size1)
|
||||||
} else if count <= 0xFFFF {
|
} else if count <= 0xFFFF {
|
||||||
@@ -22,16 +22,21 @@ impl DiscriminantSize {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the size, in bytes, of this discriminant
|
||||||
|
pub const fn byte_size(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
DiscriminantSize::Size1 => 1,
|
||||||
|
DiscriminantSize::Size2 => 2,
|
||||||
|
DiscriminantSize::Size4 => 4,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<DiscriminantSize> for u32 {
|
impl From<DiscriminantSize> for u32 {
|
||||||
/// Size of the discriminant as a `u32`
|
/// Size of the discriminant as a `u32`
|
||||||
fn from(size: DiscriminantSize) -> u32 {
|
fn from(size: DiscriminantSize) -> u32 {
|
||||||
match size {
|
size.byte_size()
|
||||||
DiscriminantSize::Size1 => 1,
|
|
||||||
DiscriminantSize::Size2 => 2,
|
|
||||||
DiscriminantSize::Size4 => 4,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +65,7 @@ pub enum FlagsSize {
|
|||||||
|
|
||||||
impl FlagsSize {
|
impl FlagsSize {
|
||||||
/// Calculate the size needed to represent a value with the specified number of flags.
|
/// Calculate the size needed to represent a value with the specified number of flags.
|
||||||
pub fn from_count(count: usize) -> FlagsSize {
|
pub const fn from_count(count: usize) -> FlagsSize {
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
FlagsSize::Size0
|
FlagsSize::Size0
|
||||||
} else if count <= 8 {
|
} else if count <= 8 {
|
||||||
@@ -74,7 +79,7 @@ impl FlagsSize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Divide `n` by `d`, rounding up in the case of a non-zero remainder.
|
/// Divide `n` by `d`, rounding up in the case of a non-zero remainder.
|
||||||
fn ceiling_divide(n: usize, d: usize) -> usize {
|
const fn ceiling_divide(n: usize, d: usize) -> usize {
|
||||||
(n + d - 1) / d
|
(n + d - 1) / d
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ env_logger = "0.9.0"
|
|||||||
libfuzzer-sys = "0.4"
|
libfuzzer-sys = "0.4"
|
||||||
wasmparser = "0.88.0"
|
wasmparser = "0.88.0"
|
||||||
wasmprinter = "0.2.37"
|
wasmprinter = "0.2.37"
|
||||||
|
wat = "1.0"
|
||||||
wasmtime-environ = { path = ".." }
|
wasmtime-environ = { path = ".." }
|
||||||
component-fuzz-util = { path = "../../misc/component-fuzz-util", optional = true }
|
component-fuzz-util = { path = "../../misc/component-fuzz-util", optional = true }
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,9 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
use arbitrary::Arbitrary;
|
use arbitrary::Arbitrary;
|
||||||
use component_fuzz_util::Type as ValType;
|
use component_fuzz_util::TestCase;
|
||||||
use libfuzzer_sys::fuzz_target;
|
use libfuzzer_sys::fuzz_target;
|
||||||
use wasmparser::{Validator, WasmFeatures};
|
use wasmparser::{Parser, Payload, Validator, WasmFeatures};
|
||||||
use wasmtime_environ::component::*;
|
use wasmtime_environ::component::*;
|
||||||
use wasmtime_environ::fact::Module;
|
use wasmtime_environ::fact::Module;
|
||||||
|
|
||||||
@@ -24,25 +24,10 @@ struct GenAdapterModule {
|
|||||||
|
|
||||||
#[derive(Arbitrary, Debug)]
|
#[derive(Arbitrary, Debug)]
|
||||||
struct GenAdapter {
|
struct GenAdapter {
|
||||||
ty: FuncType,
|
|
||||||
post_return: bool,
|
post_return: bool,
|
||||||
lift_memory64: bool,
|
lift_memory64: bool,
|
||||||
lower_memory64: bool,
|
lower_memory64: bool,
|
||||||
lift_encoding: GenStringEncoding,
|
test: TestCase,
|
||||||
lower_encoding: GenStringEncoding,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Arbitrary, Debug)]
|
|
||||||
struct FuncType {
|
|
||||||
params: Vec<ValType>,
|
|
||||||
result: ValType,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Arbitrary, Debug)]
|
|
||||||
enum GenStringEncoding {
|
|
||||||
Utf8,
|
|
||||||
Utf16,
|
|
||||||
CompactUtf16,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fuzz_target!(|module: GenAdapterModule| {
|
fuzz_target!(|module: GenAdapterModule| {
|
||||||
@@ -90,21 +75,44 @@ fn target(module: GenAdapterModule) {
|
|||||||
|
|
||||||
let mut adapters = Vec::new();
|
let mut adapters = Vec::new();
|
||||||
for adapter in module.adapters.iter() {
|
for adapter in module.adapters.iter() {
|
||||||
let mut params = Vec::new();
|
let wat_decls = adapter.test.declarations();
|
||||||
for param in adapter.ty.params.iter() {
|
let wat = format!(
|
||||||
params.push((None, intern(&mut types, param)));
|
"(component
|
||||||
}
|
{types}
|
||||||
let result = intern(&mut types, &adapter.ty.result);
|
(type (func {params} {result}))
|
||||||
let signature = types.add_func_type(TypeFunc {
|
)",
|
||||||
params: params.into(),
|
types = wat_decls.types,
|
||||||
result,
|
params = wat_decls.params,
|
||||||
|
result = wat_decls.result,
|
||||||
|
);
|
||||||
|
let wasm = wat::parse_str(&wat).unwrap();
|
||||||
|
|
||||||
|
let mut validator = Validator::new_with_features(WasmFeatures {
|
||||||
|
component_model: true,
|
||||||
|
..Default::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
types.push_type_scope();
|
||||||
|
for payload in Parser::new(0).parse_all(&wasm) {
|
||||||
|
let payload = payload.unwrap();
|
||||||
|
validator.payload(&payload).unwrap();
|
||||||
|
let section = match payload {
|
||||||
|
Payload::ComponentTypeSection(s) => s,
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
for ty in section {
|
||||||
|
let ty = types.intern_component_type(&ty.unwrap()).unwrap();
|
||||||
|
types.push_component_typedef(ty);
|
||||||
|
let ty = match ty {
|
||||||
|
TypeDef::ComponentFunc(ty) => ty,
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
adapters.push(Adapter {
|
adapters.push(Adapter {
|
||||||
lift_ty: signature,
|
lift_ty: ty,
|
||||||
lower_ty: signature,
|
lower_ty: ty,
|
||||||
lower_options: AdapterOptions {
|
lower_options: AdapterOptions {
|
||||||
instance: RuntimeComponentInstanceIndex::from_u32(0),
|
instance: RuntimeComponentInstanceIndex::from_u32(0),
|
||||||
string_encoding: adapter.lower_encoding.into(),
|
string_encoding: convert_encoding(adapter.test.encoding1),
|
||||||
memory64: adapter.lower_memory64,
|
memory64: adapter.lower_memory64,
|
||||||
// Pessimistically assume that memory/realloc are going to be
|
// Pessimistically assume that memory/realloc are going to be
|
||||||
// required for this trampoline and provide it. Avoids doing
|
// required for this trampoline and provide it. Avoids doing
|
||||||
@@ -118,7 +126,7 @@ fn target(module: GenAdapterModule) {
|
|||||||
},
|
},
|
||||||
lift_options: AdapterOptions {
|
lift_options: AdapterOptions {
|
||||||
instance: RuntimeComponentInstanceIndex::from_u32(1),
|
instance: RuntimeComponentInstanceIndex::from_u32(1),
|
||||||
string_encoding: adapter.lift_encoding.into(),
|
string_encoding: convert_encoding(adapter.test.encoding2),
|
||||||
memory64: adapter.lift_memory64,
|
memory64: adapter.lift_memory64,
|
||||||
memory: Some(dummy_memory(adapter.lift_memory64)),
|
memory: Some(dummy_memory(adapter.lift_memory64)),
|
||||||
realloc: Some(dummy_def()),
|
realloc: Some(dummy_def()),
|
||||||
@@ -131,6 +139,10 @@ fn target(module: GenAdapterModule) {
|
|||||||
func: dummy_def(),
|
func: dummy_def(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
types.pop_type_scope();
|
||||||
|
}
|
||||||
|
|
||||||
let types = types.finish();
|
let types = types.finish();
|
||||||
let mut fact_module = Module::new(&types, module.debug);
|
let mut fact_module = Module::new(&types, module.debug);
|
||||||
for (i, adapter) in adapters.iter().enumerate() {
|
for (i, adapter) in adapters.iter().enumerate() {
|
||||||
@@ -161,94 +173,10 @@ fn target(module: GenAdapterModule) {
|
|||||||
panic!()
|
panic!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn intern(types: &mut ComponentTypesBuilder, ty: &ValType) -> InterfaceType {
|
fn convert_encoding(encoding: component_fuzz_util::StringEncoding) -> StringEncoding {
|
||||||
match ty {
|
match encoding {
|
||||||
ValType::Unit => InterfaceType::Unit,
|
component_fuzz_util::StringEncoding::Utf8 => StringEncoding::Utf8,
|
||||||
ValType::Bool => InterfaceType::Bool,
|
component_fuzz_util::StringEncoding::Utf16 => StringEncoding::Utf16,
|
||||||
ValType::U8 => InterfaceType::U8,
|
component_fuzz_util::StringEncoding::Latin1OrUtf16 => StringEncoding::CompactUtf16,
|
||||||
ValType::S8 => InterfaceType::S8,
|
|
||||||
ValType::U16 => InterfaceType::U16,
|
|
||||||
ValType::S16 => InterfaceType::S16,
|
|
||||||
ValType::U32 => InterfaceType::U32,
|
|
||||||
ValType::S32 => InterfaceType::S32,
|
|
||||||
ValType::U64 => InterfaceType::U64,
|
|
||||||
ValType::S64 => InterfaceType::S64,
|
|
||||||
ValType::Float32 => InterfaceType::Float32,
|
|
||||||
ValType::Float64 => InterfaceType::Float64,
|
|
||||||
ValType::Char => InterfaceType::Char,
|
|
||||||
ValType::String => InterfaceType::String,
|
|
||||||
ValType::List(ty) => {
|
|
||||||
let ty = intern(types, ty);
|
|
||||||
InterfaceType::List(types.add_interface_type(ty))
|
|
||||||
}
|
|
||||||
ValType::Record(tys) => {
|
|
||||||
let ty = TypeRecord {
|
|
||||||
fields: tys
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, ty)| RecordField {
|
|
||||||
name: format!("f{i}"),
|
|
||||||
ty: intern(types, ty),
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
};
|
|
||||||
InterfaceType::Record(types.add_record_type(ty))
|
|
||||||
}
|
|
||||||
ValType::Flags(size) => {
|
|
||||||
let ty = TypeFlags {
|
|
||||||
names: (0..size.as_usize()).map(|i| format!("f{i}")).collect(),
|
|
||||||
};
|
|
||||||
InterfaceType::Flags(types.add_flags_type(ty))
|
|
||||||
}
|
|
||||||
ValType::Tuple(tys) => {
|
|
||||||
let ty = TypeTuple {
|
|
||||||
types: tys.iter().map(|ty| intern(types, ty)).collect(),
|
|
||||||
};
|
|
||||||
InterfaceType::Tuple(types.add_tuple_type(ty))
|
|
||||||
}
|
|
||||||
ValType::Variant(cases) => {
|
|
||||||
let ty = TypeVariant {
|
|
||||||
cases: cases
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, ty)| VariantCase {
|
|
||||||
name: format!("c{i}"),
|
|
||||||
ty: intern(types, ty),
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
};
|
|
||||||
InterfaceType::Variant(types.add_variant_type(ty))
|
|
||||||
}
|
|
||||||
ValType::Union(tys) => {
|
|
||||||
let ty = TypeUnion {
|
|
||||||
types: tys.iter().map(|ty| intern(types, ty)).collect(),
|
|
||||||
};
|
|
||||||
InterfaceType::Union(types.add_union_type(ty))
|
|
||||||
}
|
|
||||||
ValType::Enum(size) => {
|
|
||||||
let ty = TypeEnum {
|
|
||||||
names: (0..size.as_usize()).map(|i| format!("c{i}")).collect(),
|
|
||||||
};
|
|
||||||
InterfaceType::Enum(types.add_enum_type(ty))
|
|
||||||
}
|
|
||||||
ValType::Option(ty) => {
|
|
||||||
let ty = intern(types, ty);
|
|
||||||
InterfaceType::Option(types.add_interface_type(ty))
|
|
||||||
}
|
|
||||||
ValType::Expected { ok, err } => {
|
|
||||||
let ok = intern(types, ok);
|
|
||||||
let err = intern(types, err);
|
|
||||||
InterfaceType::Expected(types.add_expected_type(TypeExpected { ok, err }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<GenStringEncoding> for StringEncoding {
|
|
||||||
fn from(gen: GenStringEncoding) -> StringEncoding {
|
|
||||||
match gen {
|
|
||||||
GenStringEncoding::Utf8 => StringEncoding::Utf8,
|
|
||||||
GenStringEncoding::Utf16 => StringEncoding::Utf16,
|
|
||||||
GenStringEncoding::CompactUtf16 => StringEncoding::CompactUtf16,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use std::ops::Index;
|
|||||||
use wasmparser::{
|
use wasmparser::{
|
||||||
ComponentAlias, ComponentOuterAliasKind, ComponentTypeDeclaration, InstanceTypeDeclaration,
|
ComponentAlias, ComponentOuterAliasKind, ComponentTypeDeclaration, InstanceTypeDeclaration,
|
||||||
};
|
};
|
||||||
|
use wasmtime_component_util::{DiscriminantSize, FlagsSize};
|
||||||
|
|
||||||
macro_rules! indices {
|
macro_rules! indices {
|
||||||
($(
|
($(
|
||||||
@@ -89,6 +90,9 @@ indices! {
|
|||||||
pub struct TypeEnumIndex(u32);
|
pub struct TypeEnumIndex(u32);
|
||||||
/// Index pointing to a union type in the component model.
|
/// Index pointing to a union type in the component model.
|
||||||
pub struct TypeUnionIndex(u32);
|
pub struct TypeUnionIndex(u32);
|
||||||
|
/// Index pointing to an option type in the component model (aka a
|
||||||
|
/// `Option<T, E>`)
|
||||||
|
pub struct TypeOptionIndex(u32);
|
||||||
/// Index pointing to an expected type in the component model (aka a
|
/// Index pointing to an expected type in the component model (aka a
|
||||||
/// `Result<T, E>`)
|
/// `Result<T, E>`)
|
||||||
pub struct TypeExpectedIndex(u32);
|
pub struct TypeExpectedIndex(u32);
|
||||||
@@ -209,6 +213,7 @@ pub struct ComponentTypes {
|
|||||||
enums: PrimaryMap<TypeEnumIndex, TypeEnum>,
|
enums: PrimaryMap<TypeEnumIndex, TypeEnum>,
|
||||||
flags: PrimaryMap<TypeFlagsIndex, TypeFlags>,
|
flags: PrimaryMap<TypeFlagsIndex, TypeFlags>,
|
||||||
unions: PrimaryMap<TypeUnionIndex, TypeUnion>,
|
unions: PrimaryMap<TypeUnionIndex, TypeUnion>,
|
||||||
|
options: PrimaryMap<TypeOptionIndex, TypeOption>,
|
||||||
expecteds: PrimaryMap<TypeExpectedIndex, TypeExpected>,
|
expecteds: PrimaryMap<TypeExpectedIndex, TypeExpected>,
|
||||||
|
|
||||||
module_types: ModuleTypes,
|
module_types: ModuleTypes,
|
||||||
@@ -219,6 +224,39 @@ impl ComponentTypes {
|
|||||||
pub fn module_types(&self) -> &ModuleTypes {
|
pub fn module_types(&self) -> &ModuleTypes {
|
||||||
&self.module_types
|
&self.module_types
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the canonical ABI information about the specified type.
|
||||||
|
pub fn canonical_abi(&self, ty: &InterfaceType) -> &CanonicalAbiInfo {
|
||||||
|
match ty {
|
||||||
|
InterfaceType::Unit => &CanonicalAbiInfo::ZERO,
|
||||||
|
|
||||||
|
InterfaceType::U8 | InterfaceType::S8 | InterfaceType::Bool => {
|
||||||
|
&CanonicalAbiInfo::SCALAR1
|
||||||
|
}
|
||||||
|
|
||||||
|
InterfaceType::U16 | InterfaceType::S16 => &CanonicalAbiInfo::SCALAR2,
|
||||||
|
|
||||||
|
InterfaceType::U32
|
||||||
|
| InterfaceType::S32
|
||||||
|
| InterfaceType::Float32
|
||||||
|
| InterfaceType::Char => &CanonicalAbiInfo::SCALAR4,
|
||||||
|
|
||||||
|
InterfaceType::U64 | InterfaceType::S64 | InterfaceType::Float64 => {
|
||||||
|
&CanonicalAbiInfo::SCALAR8
|
||||||
|
}
|
||||||
|
|
||||||
|
InterfaceType::String | InterfaceType::List(_) => &CanonicalAbiInfo::POINTER_PAIR,
|
||||||
|
|
||||||
|
InterfaceType::Record(i) => &self[*i].abi,
|
||||||
|
InterfaceType::Variant(i) => &self[*i].abi,
|
||||||
|
InterfaceType::Tuple(i) => &self[*i].abi,
|
||||||
|
InterfaceType::Flags(i) => &self[*i].abi,
|
||||||
|
InterfaceType::Enum(i) => &self[*i].abi,
|
||||||
|
InterfaceType::Union(i) => &self[*i].abi,
|
||||||
|
InterfaceType::Option(i) => &self[*i].abi,
|
||||||
|
InterfaceType::Expected(i) => &self[*i].abi,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_index {
|
macro_rules! impl_index {
|
||||||
@@ -244,6 +282,7 @@ impl_index! {
|
|||||||
impl Index<TypeEnumIndex> for ComponentTypes { TypeEnum => enums }
|
impl Index<TypeEnumIndex> for ComponentTypes { TypeEnum => enums }
|
||||||
impl Index<TypeFlagsIndex> for ComponentTypes { TypeFlags => flags }
|
impl Index<TypeFlagsIndex> for ComponentTypes { TypeFlags => flags }
|
||||||
impl Index<TypeUnionIndex> for ComponentTypes { TypeUnion => unions }
|
impl Index<TypeUnionIndex> for ComponentTypes { TypeUnion => unions }
|
||||||
|
impl Index<TypeOptionIndex> for ComponentTypes { TypeOption => options }
|
||||||
impl Index<TypeExpectedIndex> for ComponentTypes { TypeExpected => expecteds }
|
impl Index<TypeExpectedIndex> for ComponentTypes { TypeExpected => expecteds }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,6 +313,7 @@ pub struct ComponentTypesBuilder {
|
|||||||
enums: HashMap<TypeEnum, TypeEnumIndex>,
|
enums: HashMap<TypeEnum, TypeEnumIndex>,
|
||||||
flags: HashMap<TypeFlags, TypeFlagsIndex>,
|
flags: HashMap<TypeFlags, TypeFlagsIndex>,
|
||||||
unions: HashMap<TypeUnion, TypeUnionIndex>,
|
unions: HashMap<TypeUnion, TypeUnionIndex>,
|
||||||
|
options: HashMap<TypeOption, TypeOptionIndex>,
|
||||||
expecteds: HashMap<TypeExpected, TypeExpectedIndex>,
|
expecteds: HashMap<TypeExpected, TypeExpectedIndex>,
|
||||||
|
|
||||||
component_types: ComponentTypes,
|
component_types: ComponentTypes,
|
||||||
@@ -599,8 +639,7 @@ impl ComponentTypesBuilder {
|
|||||||
wasmparser::ComponentDefinedType::Enum(e) => InterfaceType::Enum(self.enum_type(e)),
|
wasmparser::ComponentDefinedType::Enum(e) => InterfaceType::Enum(self.enum_type(e)),
|
||||||
wasmparser::ComponentDefinedType::Union(e) => InterfaceType::Union(self.union_type(e)),
|
wasmparser::ComponentDefinedType::Union(e) => InterfaceType::Union(self.union_type(e)),
|
||||||
wasmparser::ComponentDefinedType::Option(e) => {
|
wasmparser::ComponentDefinedType::Option(e) => {
|
||||||
let ty = self.valtype(e);
|
InterfaceType::Option(self.option_type(e))
|
||||||
InterfaceType::Option(self.add_interface_type(ty))
|
|
||||||
}
|
}
|
||||||
wasmparser::ComponentDefinedType::Expected { ok, error } => {
|
wasmparser::ComponentDefinedType::Expected { ok, error } => {
|
||||||
InterfaceType::Expected(self.expected_type(ok, error))
|
InterfaceType::Expected(self.expected_type(ok, error))
|
||||||
@@ -623,21 +662,23 @@ impl ComponentTypesBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn record_type(&mut self, record: &[(&str, wasmparser::ComponentValType)]) -> TypeRecordIndex {
|
fn record_type(&mut self, record: &[(&str, wasmparser::ComponentValType)]) -> TypeRecordIndex {
|
||||||
let record = TypeRecord {
|
let fields = record
|
||||||
fields: record
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(name, ty)| RecordField {
|
.map(|(name, ty)| RecordField {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
ty: self.valtype(ty),
|
ty: self.valtype(ty),
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect::<Box<[_]>>();
|
||||||
};
|
let abi = CanonicalAbiInfo::record(
|
||||||
self.add_record_type(record)
|
fields
|
||||||
|
.iter()
|
||||||
|
.map(|field| self.component_types.canonical_abi(&field.ty)),
|
||||||
|
);
|
||||||
|
self.add_record_type(TypeRecord { fields, abi })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn variant_type(&mut self, cases: &[wasmparser::VariantCase<'_>]) -> TypeVariantIndex {
|
fn variant_type(&mut self, cases: &[wasmparser::VariantCase<'_>]) -> TypeVariantIndex {
|
||||||
let variant = TypeVariant {
|
let cases = cases
|
||||||
cases: cases
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|case| {
|
.map(|case| {
|
||||||
// FIXME: need to implement `refines`, not sure what that
|
// FIXME: need to implement `refines`, not sure what that
|
||||||
@@ -648,37 +689,63 @@ impl ComponentTypesBuilder {
|
|||||||
ty: self.valtype(&case.ty),
|
ty: self.valtype(&case.ty),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect::<Box<[_]>>();
|
||||||
};
|
let (info, abi) = VariantInfo::new(
|
||||||
self.add_variant_type(variant)
|
cases
|
||||||
|
.iter()
|
||||||
|
.map(|c| self.component_types.canonical_abi(&c.ty)),
|
||||||
|
);
|
||||||
|
self.add_variant_type(TypeVariant { cases, abi, info })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tuple_type(&mut self, types: &[wasmparser::ComponentValType]) -> TypeTupleIndex {
|
fn tuple_type(&mut self, types: &[wasmparser::ComponentValType]) -> TypeTupleIndex {
|
||||||
let tuple = TypeTuple {
|
let types = types
|
||||||
types: types.iter().map(|ty| self.valtype(ty)).collect(),
|
.iter()
|
||||||
};
|
.map(|ty| self.valtype(ty))
|
||||||
self.add_tuple_type(tuple)
|
.collect::<Box<[_]>>();
|
||||||
|
let abi = CanonicalAbiInfo::record(
|
||||||
|
types
|
||||||
|
.iter()
|
||||||
|
.map(|ty| self.component_types.canonical_abi(ty)),
|
||||||
|
);
|
||||||
|
self.add_tuple_type(TypeTuple { types, abi })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flags_type(&mut self, flags: &[&str]) -> TypeFlagsIndex {
|
fn flags_type(&mut self, flags: &[&str]) -> TypeFlagsIndex {
|
||||||
let flags = TypeFlags {
|
let flags = TypeFlags {
|
||||||
names: flags.iter().map(|s| s.to_string()).collect(),
|
names: flags.iter().map(|s| s.to_string()).collect(),
|
||||||
|
abi: CanonicalAbiInfo::flags(flags.len()),
|
||||||
};
|
};
|
||||||
self.add_flags_type(flags)
|
self.add_flags_type(flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enum_type(&mut self, variants: &[&str]) -> TypeEnumIndex {
|
fn enum_type(&mut self, variants: &[&str]) -> TypeEnumIndex {
|
||||||
let e = TypeEnum {
|
let names = variants.iter().map(|s| s.to_string()).collect::<Box<[_]>>();
|
||||||
names: variants.iter().map(|s| s.to_string()).collect(),
|
let (info, abi) = VariantInfo::new(
|
||||||
};
|
names
|
||||||
self.add_enum_type(e)
|
.iter()
|
||||||
|
.map(|_| self.component_types.canonical_abi(&InterfaceType::Unit)),
|
||||||
|
);
|
||||||
|
self.add_enum_type(TypeEnum { names, abi, info })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn union_type(&mut self, types: &[wasmparser::ComponentValType]) -> TypeUnionIndex {
|
fn union_type(&mut self, types: &[wasmparser::ComponentValType]) -> TypeUnionIndex {
|
||||||
let union = TypeUnion {
|
let types = types
|
||||||
types: types.iter().map(|ty| self.valtype(ty)).collect(),
|
.iter()
|
||||||
};
|
.map(|ty| self.valtype(ty))
|
||||||
self.add_union_type(union)
|
.collect::<Box<[_]>>();
|
||||||
|
let (info, abi) =
|
||||||
|
VariantInfo::new(types.iter().map(|t| self.component_types.canonical_abi(t)));
|
||||||
|
self.add_union_type(TypeUnion { types, abi, info })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn option_type(&mut self, ty: &wasmparser::ComponentValType) -> TypeOptionIndex {
|
||||||
|
let ty = self.valtype(ty);
|
||||||
|
let (info, abi) = VariantInfo::new([
|
||||||
|
self.component_types.canonical_abi(&InterfaceType::Unit),
|
||||||
|
self.component_types.canonical_abi(&ty),
|
||||||
|
]);
|
||||||
|
self.add_option_type(TypeOption { ty, abi, info })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expected_type(
|
fn expected_type(
|
||||||
@@ -686,11 +753,13 @@ impl ComponentTypesBuilder {
|
|||||||
ok: &wasmparser::ComponentValType,
|
ok: &wasmparser::ComponentValType,
|
||||||
err: &wasmparser::ComponentValType,
|
err: &wasmparser::ComponentValType,
|
||||||
) -> TypeExpectedIndex {
|
) -> TypeExpectedIndex {
|
||||||
let expected = TypeExpected {
|
let ok = self.valtype(ok);
|
||||||
ok: self.valtype(ok),
|
let err = self.valtype(err);
|
||||||
err: self.valtype(err),
|
let (info, abi) = VariantInfo::new([
|
||||||
};
|
self.component_types.canonical_abi(&ok),
|
||||||
self.add_expected_type(expected)
|
self.component_types.canonical_abi(&err),
|
||||||
|
]);
|
||||||
|
self.add_expected_type(TypeExpected { ok, err, abi, info })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Interns a new function type within this type information.
|
/// Interns a new function type within this type information.
|
||||||
@@ -728,6 +797,11 @@ impl ComponentTypesBuilder {
|
|||||||
intern(&mut self.enums, &mut self.component_types.enums, ty)
|
intern(&mut self.enums, &mut self.component_types.enums, ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Interns a new option type within this type information.
|
||||||
|
pub fn add_option_type(&mut self, ty: TypeOption) -> TypeOptionIndex {
|
||||||
|
intern(&mut self.options, &mut self.component_types.options, ty)
|
||||||
|
}
|
||||||
|
|
||||||
/// Interns a new expected type within this type information.
|
/// Interns a new expected type within this type information.
|
||||||
pub fn add_expected_type(&mut self, ty: TypeExpected) -> TypeExpectedIndex {
|
pub fn add_expected_type(&mut self, ty: TypeExpected) -> TypeExpectedIndex {
|
||||||
intern(&mut self.expecteds, &mut self.component_types.expecteds, ty)
|
intern(&mut self.expecteds, &mut self.component_types.expecteds, ty)
|
||||||
@@ -875,7 +949,7 @@ pub enum InterfaceType {
|
|||||||
Flags(TypeFlagsIndex),
|
Flags(TypeFlagsIndex),
|
||||||
Enum(TypeEnumIndex),
|
Enum(TypeEnumIndex),
|
||||||
Union(TypeUnionIndex),
|
Union(TypeUnionIndex),
|
||||||
Option(TypeInterfaceIndex),
|
Option(TypeOptionIndex),
|
||||||
Expected(TypeExpectedIndex),
|
Expected(TypeExpectedIndex),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -900,6 +974,306 @@ impl From<&wasmparser::PrimitiveValType> for InterfaceType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Bye information about a type in the canonical ABI, with metadata for both
|
||||||
|
/// memory32 and memory64-based types.
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
|
||||||
|
pub struct CanonicalAbiInfo {
|
||||||
|
/// The byte-size of this type in a 32-bit memory.
|
||||||
|
pub size32: u32,
|
||||||
|
/// The byte-alignment of this type in a 32-bit memory.
|
||||||
|
pub align32: u32,
|
||||||
|
/// The byte-size of this type in a 64-bit memory.
|
||||||
|
pub size64: u32,
|
||||||
|
/// The byte-alignment of this type in a 64-bit memory.
|
||||||
|
pub align64: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CanonicalAbiInfo {
|
||||||
|
fn default() -> CanonicalAbiInfo {
|
||||||
|
CanonicalAbiInfo {
|
||||||
|
size32: 0,
|
||||||
|
align32: 1,
|
||||||
|
size64: 0,
|
||||||
|
align64: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn align_to(a: u32, b: u32) -> u32 {
|
||||||
|
assert!(b.is_power_of_two());
|
||||||
|
(a + (b - 1)) & !(b - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn max(a: u32, b: u32) -> u32 {
|
||||||
|
if a > b {
|
||||||
|
a
|
||||||
|
} else {
|
||||||
|
b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CanonicalAbiInfo {
|
||||||
|
/// ABI information for zero-sized types.
|
||||||
|
pub const ZERO: CanonicalAbiInfo = CanonicalAbiInfo {
|
||||||
|
size32: 0,
|
||||||
|
align32: 1,
|
||||||
|
size64: 0,
|
||||||
|
align64: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// ABI information for one-byte scalars.
|
||||||
|
pub const SCALAR1: CanonicalAbiInfo = CanonicalAbiInfo::scalar(1);
|
||||||
|
/// ABI information for two-byte scalars.
|
||||||
|
pub const SCALAR2: CanonicalAbiInfo = CanonicalAbiInfo::scalar(2);
|
||||||
|
/// ABI information for four-byte scalars.
|
||||||
|
pub const SCALAR4: CanonicalAbiInfo = CanonicalAbiInfo::scalar(4);
|
||||||
|
/// ABI information for eight-byte scalars.
|
||||||
|
pub const SCALAR8: CanonicalAbiInfo = CanonicalAbiInfo::scalar(8);
|
||||||
|
|
||||||
|
const fn scalar(size: u32) -> CanonicalAbiInfo {
|
||||||
|
CanonicalAbiInfo {
|
||||||
|
size32: size,
|
||||||
|
align32: size,
|
||||||
|
size64: size,
|
||||||
|
align64: size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ABI information for lists/strings which are "pointer pairs"
|
||||||
|
pub const POINTER_PAIR: CanonicalAbiInfo = CanonicalAbiInfo {
|
||||||
|
size32: 8,
|
||||||
|
align32: 4,
|
||||||
|
size64: 16,
|
||||||
|
align64: 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Returns the abi for a record represented by the specified fields.
|
||||||
|
pub fn record<'a>(fields: impl Iterator<Item = &'a CanonicalAbiInfo>) -> CanonicalAbiInfo {
|
||||||
|
// NB: this is basically a duplicate copy of
|
||||||
|
// `CanonicalAbiInfo::record_static` and the two should be kept in sync.
|
||||||
|
|
||||||
|
let mut ret = CanonicalAbiInfo::default();
|
||||||
|
for field in fields {
|
||||||
|
ret.size32 = align_to(ret.size32, field.align32) + field.size32;
|
||||||
|
ret.align32 = ret.align32.max(field.align32);
|
||||||
|
ret.size64 = align_to(ret.size64, field.align64) + field.size64;
|
||||||
|
ret.align64 = ret.align64.max(field.align64);
|
||||||
|
}
|
||||||
|
ret.size32 = align_to(ret.size32, ret.align32);
|
||||||
|
ret.size64 = align_to(ret.size64, ret.align64);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same as `CanonicalAbiInfo::record` but in a `const`-friendly context.
|
||||||
|
pub const fn record_static(fields: &[CanonicalAbiInfo]) -> CanonicalAbiInfo {
|
||||||
|
// NB: this is basically a duplicate copy of `CanonicalAbiInfo::record`
|
||||||
|
// and the two should be kept in sync.
|
||||||
|
|
||||||
|
let mut ret = CanonicalAbiInfo::ZERO;
|
||||||
|
let mut i = 0;
|
||||||
|
while i < fields.len() {
|
||||||
|
let field = &fields[i];
|
||||||
|
ret.size32 = align_to(ret.size32, field.align32) + field.size32;
|
||||||
|
ret.align32 = max(ret.align32, field.align32);
|
||||||
|
ret.size64 = align_to(ret.size64, field.align64) + field.size64;
|
||||||
|
ret.align64 = max(ret.align64, field.align64);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
ret.size32 = align_to(ret.size32, ret.align32);
|
||||||
|
ret.size64 = align_to(ret.size64, ret.align64);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the delta from the current value of `offset` to align properly
|
||||||
|
/// and read the next record field of type `abi` for 32-bit memories.
|
||||||
|
pub fn next_field32(&self, offset: &mut u32) -> u32 {
|
||||||
|
*offset = align_to(*offset, self.align32) + self.size32;
|
||||||
|
*offset - self.size32
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same as `next_field32`, but bumps a usize pointer
|
||||||
|
pub fn next_field32_size(&self, offset: &mut usize) -> usize {
|
||||||
|
let cur = u32::try_from(*offset).unwrap();
|
||||||
|
let cur = align_to(cur, self.align32) + self.size32;
|
||||||
|
*offset = usize::try_from(cur).unwrap();
|
||||||
|
usize::try_from(cur - self.size32).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the delta from the current value of `offset` to align properly
|
||||||
|
/// and read the next record field of type `abi` for 64-bit memories.
|
||||||
|
pub fn next_field64(&self, offset: &mut u32) -> u32 {
|
||||||
|
*offset = align_to(*offset, self.align64) + self.size64;
|
||||||
|
*offset - self.size64
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same as `next_field64`, but bumps a usize pointer
|
||||||
|
pub fn next_field64_size(&self, offset: &mut usize) -> usize {
|
||||||
|
let cur = u32::try_from(*offset).unwrap();
|
||||||
|
let cur = align_to(cur, self.align64) + self.size64;
|
||||||
|
*offset = usize::try_from(cur).unwrap();
|
||||||
|
usize::try_from(cur - self.size64).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns ABI information for a structure which contains `count` flags.
|
||||||
|
pub const fn flags(count: usize) -> CanonicalAbiInfo {
|
||||||
|
let (size, align) = match FlagsSize::from_count(count) {
|
||||||
|
FlagsSize::Size0 => (0, 1),
|
||||||
|
FlagsSize::Size1 => (1, 1),
|
||||||
|
FlagsSize::Size2 => (2, 2),
|
||||||
|
FlagsSize::Size4Plus(n) => ((n as u32) * 4, 4),
|
||||||
|
};
|
||||||
|
CanonicalAbiInfo {
|
||||||
|
size32: size,
|
||||||
|
align32: align,
|
||||||
|
size64: size,
|
||||||
|
align64: align,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn variant<'a, I>(cases: I) -> CanonicalAbiInfo
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = &'a CanonicalAbiInfo>,
|
||||||
|
I::IntoIter: ExactSizeIterator,
|
||||||
|
{
|
||||||
|
// NB: this is basically a duplicate definition of
|
||||||
|
// `CanonicalAbiInfo::variant_static`, these should be kept in sync.
|
||||||
|
|
||||||
|
let cases = cases.into_iter();
|
||||||
|
let discrim_size = u32::from(DiscriminantSize::from_count(cases.len()).unwrap());
|
||||||
|
let mut max_size32 = 0;
|
||||||
|
let mut max_align32 = discrim_size;
|
||||||
|
let mut max_size64 = 0;
|
||||||
|
let mut max_align64 = discrim_size;
|
||||||
|
for case in cases {
|
||||||
|
max_size32 = max_size32.max(case.size32);
|
||||||
|
max_align32 = max_align32.max(case.align32);
|
||||||
|
max_size64 = max_size64.max(case.size64);
|
||||||
|
max_align64 = max_align64.max(case.align64);
|
||||||
|
}
|
||||||
|
CanonicalAbiInfo {
|
||||||
|
size32: align_to(
|
||||||
|
align_to(discrim_size, max_align32) + max_size32,
|
||||||
|
max_align32,
|
||||||
|
),
|
||||||
|
align32: max_align32,
|
||||||
|
size64: align_to(
|
||||||
|
align_to(discrim_size, max_align64) + max_size64,
|
||||||
|
max_align64,
|
||||||
|
),
|
||||||
|
align64: max_align64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same as `CanonicalAbiInfo::variant` but `const`-safe
|
||||||
|
pub const fn variant_static(cases: &[CanonicalAbiInfo]) -> CanonicalAbiInfo {
|
||||||
|
// NB: this is basically a duplicate definition of
|
||||||
|
// `CanonicalAbiInfo::variant`, these should be kept in sync.
|
||||||
|
|
||||||
|
let discrim_size = match DiscriminantSize::from_count(cases.len()) {
|
||||||
|
Some(size) => size.byte_size(),
|
||||||
|
None => unreachable!(),
|
||||||
|
};
|
||||||
|
let mut max_size32 = 0;
|
||||||
|
let mut max_align32 = discrim_size;
|
||||||
|
let mut max_size64 = 0;
|
||||||
|
let mut max_align64 = discrim_size;
|
||||||
|
let mut i = 0;
|
||||||
|
while i < cases.len() {
|
||||||
|
let case = &cases[i];
|
||||||
|
max_size32 = max(max_size32, case.size32);
|
||||||
|
max_align32 = max(max_align32, case.align32);
|
||||||
|
max_size64 = max(max_size64, case.size64);
|
||||||
|
max_align64 = max(max_align64, case.align64);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
CanonicalAbiInfo {
|
||||||
|
size32: align_to(
|
||||||
|
align_to(discrim_size, max_align32) + max_size32,
|
||||||
|
max_align32,
|
||||||
|
),
|
||||||
|
align32: max_align32,
|
||||||
|
size64: align_to(
|
||||||
|
align_to(discrim_size, max_align64) + max_size64,
|
||||||
|
max_align64,
|
||||||
|
),
|
||||||
|
align64: max_align64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ABI information about the representation of a variant.
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
|
||||||
|
pub struct VariantInfo {
|
||||||
|
/// The size of the discriminant used.
|
||||||
|
#[serde(with = "serde_discrim_size")]
|
||||||
|
pub size: DiscriminantSize,
|
||||||
|
/// The offset of the payload from the start of the variant in 32-bit
|
||||||
|
/// memories.
|
||||||
|
pub payload_offset32: u32,
|
||||||
|
/// The offset of the payload from the start of the variant in 64-bit
|
||||||
|
/// memories.
|
||||||
|
pub payload_offset64: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VariantInfo {
|
||||||
|
/// Returns the abi information for a variant represented by the specified
|
||||||
|
/// cases.
|
||||||
|
pub fn new<'a, I>(cases: I) -> (VariantInfo, CanonicalAbiInfo)
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = &'a CanonicalAbiInfo>,
|
||||||
|
I::IntoIter: ExactSizeIterator,
|
||||||
|
{
|
||||||
|
let cases = cases.into_iter();
|
||||||
|
let size = DiscriminantSize::from_count(cases.len()).unwrap();
|
||||||
|
let abi = CanonicalAbiInfo::variant(cases);
|
||||||
|
(
|
||||||
|
VariantInfo {
|
||||||
|
size,
|
||||||
|
payload_offset32: align_to(u32::from(size), abi.align32),
|
||||||
|
payload_offset64: align_to(u32::from(size), abi.align64),
|
||||||
|
},
|
||||||
|
abi,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/// TODO
|
||||||
|
pub const fn new_static(cases: &[CanonicalAbiInfo]) -> VariantInfo {
|
||||||
|
let size = match DiscriminantSize::from_count(cases.len()) {
|
||||||
|
Some(size) => size,
|
||||||
|
None => unreachable!(),
|
||||||
|
};
|
||||||
|
let abi = CanonicalAbiInfo::variant_static(cases);
|
||||||
|
VariantInfo {
|
||||||
|
size,
|
||||||
|
payload_offset32: align_to(size.byte_size(), abi.align32),
|
||||||
|
payload_offset64: align_to(size.byte_size(), abi.align64),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod serde_discrim_size {
|
||||||
|
use super::DiscriminantSize;
|
||||||
|
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
|
pub fn serialize<S>(disc: &DiscriminantSize, ser: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
u32::from(*disc).serialize(ser)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize<'de, D>(deser: D) -> Result<DiscriminantSize, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
match u32::deserialize(deser)? {
|
||||||
|
1 => Ok(DiscriminantSize::Size1),
|
||||||
|
2 => Ok(DiscriminantSize::Size2),
|
||||||
|
4 => Ok(DiscriminantSize::Size4),
|
||||||
|
_ => Err(D::Error::custom("invalid discriminant size")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Shape of a "record" type in interface types.
|
/// Shape of a "record" type in interface types.
|
||||||
///
|
///
|
||||||
/// This is equivalent to a `struct` in Rust.
|
/// This is equivalent to a `struct` in Rust.
|
||||||
@@ -907,6 +1281,8 @@ impl From<&wasmparser::PrimitiveValType> for InterfaceType {
|
|||||||
pub struct TypeRecord {
|
pub struct TypeRecord {
|
||||||
/// The fields that are contained within this struct type.
|
/// The fields that are contained within this struct type.
|
||||||
pub fields: Box<[RecordField]>,
|
pub fields: Box<[RecordField]>,
|
||||||
|
/// Byte information about this type in the canonical ABI.
|
||||||
|
pub abi: CanonicalAbiInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// One field within a record.
|
/// One field within a record.
|
||||||
@@ -927,6 +1303,10 @@ pub struct RecordField {
|
|||||||
pub struct TypeVariant {
|
pub struct TypeVariant {
|
||||||
/// The list of cases that this variant can take.
|
/// The list of cases that this variant can take.
|
||||||
pub cases: Box<[VariantCase]>,
|
pub cases: Box<[VariantCase]>,
|
||||||
|
/// Byte information about this type in the canonical ABI.
|
||||||
|
pub abi: CanonicalAbiInfo,
|
||||||
|
/// Byte information about this variant type.
|
||||||
|
pub info: VariantInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// One case of a `variant` type which contains the name of the variant as well
|
/// One case of a `variant` type which contains the name of the variant as well
|
||||||
@@ -947,6 +1327,8 @@ pub struct VariantCase {
|
|||||||
pub struct TypeTuple {
|
pub struct TypeTuple {
|
||||||
/// The types that are contained within this tuple.
|
/// The types that are contained within this tuple.
|
||||||
pub types: Box<[InterfaceType]>,
|
pub types: Box<[InterfaceType]>,
|
||||||
|
/// Byte information about this type in the canonical ABI.
|
||||||
|
pub abi: CanonicalAbiInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shape of a "flags" type in interface types.
|
/// Shape of a "flags" type in interface types.
|
||||||
@@ -957,6 +1339,8 @@ pub struct TypeTuple {
|
|||||||
pub struct TypeFlags {
|
pub struct TypeFlags {
|
||||||
/// The names of all flags, all of which are unique.
|
/// The names of all flags, all of which are unique.
|
||||||
pub names: Box<[String]>,
|
pub names: Box<[String]>,
|
||||||
|
/// Byte information about this type in the canonical ABI.
|
||||||
|
pub abi: CanonicalAbiInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shape of an "enum" type in interface types, not to be confused with a Rust
|
/// Shape of an "enum" type in interface types, not to be confused with a Rust
|
||||||
@@ -968,6 +1352,10 @@ pub struct TypeFlags {
|
|||||||
pub struct TypeEnum {
|
pub struct TypeEnum {
|
||||||
/// The names of this enum, all of which are unique.
|
/// The names of this enum, all of which are unique.
|
||||||
pub names: Box<[String]>,
|
pub names: Box<[String]>,
|
||||||
|
/// Byte information about this type in the canonical ABI.
|
||||||
|
pub abi: CanonicalAbiInfo,
|
||||||
|
/// Byte information about this variant type.
|
||||||
|
pub info: VariantInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shape of a "union" type in interface types.
|
/// Shape of a "union" type in interface types.
|
||||||
@@ -979,6 +1367,21 @@ pub struct TypeEnum {
|
|||||||
pub struct TypeUnion {
|
pub struct TypeUnion {
|
||||||
/// The list of types this is a union over.
|
/// The list of types this is a union over.
|
||||||
pub types: Box<[InterfaceType]>,
|
pub types: Box<[InterfaceType]>,
|
||||||
|
/// Byte information about this type in the canonical ABI.
|
||||||
|
pub abi: CanonicalAbiInfo,
|
||||||
|
/// Byte information about this variant type.
|
||||||
|
pub info: VariantInfo,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shape of an "option" interface type.
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
|
||||||
|
pub struct TypeOption {
|
||||||
|
/// The `T` in `Result<T, E>`
|
||||||
|
pub ty: InterfaceType,
|
||||||
|
/// Byte information about this type in the canonical ABI.
|
||||||
|
pub abi: CanonicalAbiInfo,
|
||||||
|
/// Byte information about this variant type.
|
||||||
|
pub info: VariantInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shape of an "expected" interface type.
|
/// Shape of an "expected" interface type.
|
||||||
@@ -988,4 +1391,8 @@ pub struct TypeExpected {
|
|||||||
pub ok: InterfaceType,
|
pub ok: InterfaceType,
|
||||||
/// The `E` in `Result<T, E>`
|
/// The `E` in `Result<T, E>`
|
||||||
pub err: InterfaceType,
|
pub err: InterfaceType,
|
||||||
|
/// Byte information about this type in the canonical ABI.
|
||||||
|
pub abi: CanonicalAbiInfo,
|
||||||
|
/// Byte information about this variant type.
|
||||||
|
pub info: VariantInfo,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
use crate::component::{ComponentTypes, InterfaceType, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS};
|
use crate::component::{ComponentTypes, InterfaceType, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS};
|
||||||
use crate::fact::{AdapterOptions, Context, Options};
|
use crate::fact::{AdapterOptions, Context, Options};
|
||||||
use wasm_encoder::ValType;
|
use wasm_encoder::ValType;
|
||||||
use wasmtime_component_util::{DiscriminantSize, FlagsSize};
|
use wasmtime_component_util::FlagsSize;
|
||||||
|
|
||||||
/// Metadata about a core wasm signature which is created for a component model
|
/// Metadata about a core wasm signature which is created for a component model
|
||||||
/// signature.
|
/// signature.
|
||||||
@@ -23,11 +23,6 @@ pub struct Signature {
|
|||||||
pub results_indirect: bool,
|
pub results_indirect: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn align_to(n: usize, align: usize) -> usize {
|
|
||||||
assert!(align.is_power_of_two());
|
|
||||||
(n + (align - 1)) & !(align - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ComponentTypes {
|
impl ComponentTypes {
|
||||||
/// Calculates the core wasm function signature for the component function
|
/// Calculates the core wasm function signature for the component function
|
||||||
/// type specified within `Context`.
|
/// type specified within `Context`.
|
||||||
@@ -120,15 +115,18 @@ impl ComponentTypes {
|
|||||||
}
|
}
|
||||||
InterfaceType::Flags(f) => {
|
InterfaceType::Flags(f) => {
|
||||||
let flags = &self[*f];
|
let flags = &self[*f];
|
||||||
let nflags = align_to(flags.names.len(), 32) / 32;
|
match FlagsSize::from_count(flags.names.len()) {
|
||||||
for _ in 0..nflags {
|
FlagsSize::Size0 => {}
|
||||||
dst.push(ValType::I32);
|
FlagsSize::Size1 | FlagsSize::Size2 => dst.push(ValType::I32),
|
||||||
|
FlagsSize::Size4Plus(n) => {
|
||||||
|
dst.extend((0..n).map(|_| ValType::I32));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InterfaceType::Enum(_) => dst.push(ValType::I32),
|
InterfaceType::Enum(_) => dst.push(ValType::I32),
|
||||||
InterfaceType::Option(t) => {
|
InterfaceType::Option(t) => {
|
||||||
dst.push(ValType::I32);
|
dst.push(ValType::I32);
|
||||||
self.push_flat(opts, &self[*t], dst);
|
self.push_flat(opts, &self[*t].ty, dst);
|
||||||
}
|
}
|
||||||
InterfaceType::Variant(t) => {
|
InterfaceType::Variant(t) => {
|
||||||
dst.push(ValType::I32);
|
dst.push(ValType::I32);
|
||||||
@@ -185,7 +183,7 @@ impl ComponentTypes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn align(&self, opts: &Options, ty: &InterfaceType) -> usize {
|
pub(super) fn align(&self, opts: &Options, ty: &InterfaceType) -> u32 {
|
||||||
self.size_align(opts, ty).1
|
self.size_align(opts, ty).1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,85 +192,12 @@ impl ComponentTypes {
|
|||||||
//
|
//
|
||||||
// TODO: this is probably inefficient to entire recalculate at all phases,
|
// TODO: this is probably inefficient to entire recalculate at all phases,
|
||||||
// seems like it would be best to intern this in some sort of map somewhere.
|
// seems like it would be best to intern this in some sort of map somewhere.
|
||||||
pub(super) fn size_align(&self, opts: &Options, ty: &InterfaceType) -> (usize, usize) {
|
pub(super) fn size_align(&self, opts: &Options, ty: &InterfaceType) -> (u32, u32) {
|
||||||
match ty {
|
let abi = self.canonical_abi(ty);
|
||||||
InterfaceType::Unit => (0, 1),
|
if opts.memory64 {
|
||||||
InterfaceType::Bool | InterfaceType::S8 | InterfaceType::U8 => (1, 1),
|
(abi.size64, abi.align64)
|
||||||
InterfaceType::S16 | InterfaceType::U16 => (2, 2),
|
} else {
|
||||||
InterfaceType::S32
|
(abi.size32, abi.align32)
|
||||||
| InterfaceType::U32
|
|
||||||
| InterfaceType::Char
|
|
||||||
| InterfaceType::Float32 => (4, 4),
|
|
||||||
InterfaceType::S64 | InterfaceType::U64 | InterfaceType::Float64 => (8, 8),
|
|
||||||
InterfaceType::String | InterfaceType::List(_) => {
|
|
||||||
((2 * opts.ptr_size()).into(), opts.ptr_size().into())
|
|
||||||
}
|
|
||||||
|
|
||||||
InterfaceType::Record(r) => {
|
|
||||||
self.record_size_align(opts, self[*r].fields.iter().map(|f| &f.ty))
|
|
||||||
}
|
|
||||||
InterfaceType::Tuple(t) => self.record_size_align(opts, self[*t].types.iter()),
|
|
||||||
InterfaceType::Flags(f) => match FlagsSize::from_count(self[*f].names.len()) {
|
|
||||||
FlagsSize::Size0 => (0, 1),
|
|
||||||
FlagsSize::Size1 => (1, 1),
|
|
||||||
FlagsSize::Size2 => (2, 2),
|
|
||||||
FlagsSize::Size4Plus(n) => (n * 4, 4),
|
|
||||||
},
|
|
||||||
InterfaceType::Enum(t) => self.discrim_size_align(self[*t].names.len()),
|
|
||||||
InterfaceType::Option(t) => {
|
|
||||||
let ty = &self[*t];
|
|
||||||
self.variant_size_align(opts, [&InterfaceType::Unit, ty].into_iter())
|
|
||||||
}
|
|
||||||
InterfaceType::Variant(t) => {
|
|
||||||
self.variant_size_align(opts, self[*t].cases.iter().map(|c| &c.ty))
|
|
||||||
}
|
|
||||||
InterfaceType::Union(t) => self.variant_size_align(opts, self[*t].types.iter()),
|
|
||||||
InterfaceType::Expected(t) => {
|
|
||||||
let e = &self[*t];
|
|
||||||
self.variant_size_align(opts, [&e.ok, &e.err].into_iter())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn record_size_align<'a>(
|
|
||||||
&self,
|
|
||||||
opts: &Options,
|
|
||||||
fields: impl Iterator<Item = &'a InterfaceType>,
|
|
||||||
) -> (usize, usize) {
|
|
||||||
let mut size = 0;
|
|
||||||
let mut align = 1;
|
|
||||||
for ty in fields {
|
|
||||||
let (fsize, falign) = self.size_align(opts, ty);
|
|
||||||
size = align_to(size, falign) + fsize;
|
|
||||||
align = align.max(falign);
|
|
||||||
}
|
|
||||||
(align_to(size, align), align)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn variant_size_align<'a>(
|
|
||||||
&self,
|
|
||||||
opts: &Options,
|
|
||||||
cases: impl ExactSizeIterator<Item = &'a InterfaceType>,
|
|
||||||
) -> (usize, usize) {
|
|
||||||
let (discrim_size, mut align) = self.discrim_size_align(cases.len());
|
|
||||||
let mut payload_size = 0;
|
|
||||||
for ty in cases {
|
|
||||||
let (csize, calign) = self.size_align(opts, ty);
|
|
||||||
payload_size = payload_size.max(csize);
|
|
||||||
align = align.max(calign);
|
|
||||||
}
|
|
||||||
(
|
|
||||||
align_to(align_to(discrim_size, align) + payload_size, align),
|
|
||||||
align,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn discrim_size_align<'a>(&self, cases: usize) -> (usize, usize) {
|
|
||||||
match DiscriminantSize::from_count(cases) {
|
|
||||||
Some(DiscriminantSize::Size1) => (1, 1),
|
|
||||||
Some(DiscriminantSize::Size2) => (2, 2),
|
|
||||||
Some(DiscriminantSize::Size4) => (4, 4),
|
|
||||||
None => unreachable!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,11 +16,12 @@
|
|||||||
//! can be somewhat arbitrary, an intentional decision.
|
//! can be somewhat arbitrary, an intentional decision.
|
||||||
|
|
||||||
use crate::component::{
|
use crate::component::{
|
||||||
ComponentTypes, InterfaceType, StringEncoding, TypeEnumIndex, TypeExpectedIndex,
|
CanonicalAbiInfo, ComponentTypes, InterfaceType, StringEncoding, TypeEnumIndex,
|
||||||
TypeFlagsIndex, TypeInterfaceIndex, TypeRecordIndex, TypeTupleIndex, TypeUnionIndex,
|
TypeExpectedIndex, TypeFlagsIndex, TypeInterfaceIndex, TypeOptionIndex, TypeRecordIndex,
|
||||||
TypeVariantIndex, FLAG_MAY_ENTER, FLAG_MAY_LEAVE, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS,
|
TypeTupleIndex, TypeUnionIndex, TypeVariantIndex, VariantInfo, FLAG_MAY_ENTER, FLAG_MAY_LEAVE,
|
||||||
|
MAX_FLAT_PARAMS, MAX_FLAT_RESULTS,
|
||||||
};
|
};
|
||||||
use crate::fact::signature::{align_to, Signature};
|
use crate::fact::signature::Signature;
|
||||||
use crate::fact::transcode::{FixedEncoding as FE, Transcode, Transcoder};
|
use crate::fact::transcode::{FixedEncoding as FE, Transcode, Transcoder};
|
||||||
use crate::fact::traps::Trap;
|
use crate::fact::traps::Trap;
|
||||||
use crate::fact::{AdapterData, Body, Context, Function, FunctionId, Module, Options};
|
use crate::fact::{AdapterData, Body, Context, Function, FunctionId, Module, Options};
|
||||||
@@ -307,7 +308,12 @@ impl Compiler<'_, '_> {
|
|||||||
} else {
|
} else {
|
||||||
// If there are too many parameters then space is allocated in the
|
// If there are too many parameters then space is allocated in the
|
||||||
// destination module for the parameters via its `realloc` function.
|
// destination module for the parameters via its `realloc` function.
|
||||||
let (size, align) = self.types.record_size_align(lift_opts, dst_tys.iter());
|
let abi = CanonicalAbiInfo::record(dst_tys.iter().map(|t| self.types.canonical_abi(t)));
|
||||||
|
let (size, align) = if lift_opts.memory64 {
|
||||||
|
(abi.size64, abi.align64)
|
||||||
|
} else {
|
||||||
|
(abi.size32, abi.align32)
|
||||||
|
};
|
||||||
let size = MallocSize::Const(size);
|
let size = MallocSize::Const(size);
|
||||||
Destination::Memory(self.malloc(lift_opts, size, align))
|
Destination::Memory(self.malloc(lift_opts, size, align))
|
||||||
};
|
};
|
||||||
@@ -1692,13 +1698,13 @@ impl Compiler<'_, '_> {
|
|||||||
// Update the two loop pointers
|
// Update the two loop pointers
|
||||||
if src_size > 0 {
|
if src_size > 0 {
|
||||||
self.instruction(LocalGet(cur_src_ptr.idx));
|
self.instruction(LocalGet(cur_src_ptr.idx));
|
||||||
self.ptr_uconst(src_opts, u32::try_from(src_size).unwrap());
|
self.ptr_uconst(src_opts, src_size);
|
||||||
self.ptr_add(src_opts);
|
self.ptr_add(src_opts);
|
||||||
self.instruction(LocalSet(cur_src_ptr.idx));
|
self.instruction(LocalSet(cur_src_ptr.idx));
|
||||||
}
|
}
|
||||||
if dst_size > 0 {
|
if dst_size > 0 {
|
||||||
self.instruction(LocalGet(cur_dst_ptr.idx));
|
self.instruction(LocalGet(cur_dst_ptr.idx));
|
||||||
self.ptr_uconst(dst_opts, u32::try_from(dst_size).unwrap());
|
self.ptr_uconst(dst_opts, dst_size);
|
||||||
self.ptr_add(dst_opts);
|
self.ptr_add(dst_opts);
|
||||||
self.instruction(LocalSet(cur_dst_ptr.idx));
|
self.instruction(LocalSet(cur_dst_ptr.idx));
|
||||||
}
|
}
|
||||||
@@ -1745,7 +1751,7 @@ impl Compiler<'_, '_> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
opts: &Options,
|
opts: &Options,
|
||||||
len_local: u32,
|
len_local: u32,
|
||||||
elt_size: usize,
|
elt_size: u32,
|
||||||
) -> TempLocal {
|
) -> TempLocal {
|
||||||
// Zero-size types are easy to handle here because the byte size of the
|
// Zero-size types are easy to handle here because the byte size of the
|
||||||
// destination is always zero.
|
// destination is always zero.
|
||||||
@@ -1810,7 +1816,7 @@ impl Compiler<'_, '_> {
|
|||||||
//
|
//
|
||||||
// The result of the multiplication is saved into a local as well to
|
// The result of the multiplication is saved into a local as well to
|
||||||
// get the result afterwards.
|
// get the result afterwards.
|
||||||
self.instruction(I64Const(u32::try_from(elt_size).unwrap().into()));
|
self.instruction(I64Const(elt_size.into()));
|
||||||
self.instruction(I64Mul);
|
self.instruction(I64Mul);
|
||||||
let tmp = self.local_tee_new_tmp(ValType::I64);
|
let tmp = self.local_tee_new_tmp(ValType::I64);
|
||||||
// Branch to success if the upper 32-bits are zero, otherwise
|
// Branch to success if the upper 32-bits are zero, otherwise
|
||||||
@@ -1983,8 +1989,8 @@ impl Compiler<'_, '_> {
|
|||||||
_ => panic!("expected a variant"),
|
_ => panic!("expected a variant"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let src_info = VariantInfo::new(self.types, src.opts(), src_ty.cases.iter().map(|c| c.ty));
|
let src_info = variant_info(self.types, src_ty.cases.iter().map(|c| c.ty));
|
||||||
let dst_info = VariantInfo::new(self.types, dst.opts(), dst_ty.cases.iter().map(|c| c.ty));
|
let dst_info = variant_info(self.types, dst_ty.cases.iter().map(|c| c.ty));
|
||||||
|
|
||||||
let iter = src_ty.cases.iter().enumerate().map(|(src_i, src_case)| {
|
let iter = src_ty.cases.iter().enumerate().map(|(src_i, src_case)| {
|
||||||
let dst_i = dst_ty
|
let dst_i = dst_ty
|
||||||
@@ -2018,8 +2024,8 @@ impl Compiler<'_, '_> {
|
|||||||
_ => panic!("expected an option"),
|
_ => panic!("expected an option"),
|
||||||
};
|
};
|
||||||
assert_eq!(src_ty.types.len(), dst_ty.types.len());
|
assert_eq!(src_ty.types.len(), dst_ty.types.len());
|
||||||
let src_info = VariantInfo::new(self.types, src.opts(), src_ty.types.iter().copied());
|
let src_info = variant_info(self.types, src_ty.types.iter().copied());
|
||||||
let dst_info = VariantInfo::new(self.types, dst.opts(), dst_ty.types.iter().copied());
|
let dst_info = variant_info(self.types, dst_ty.types.iter().copied());
|
||||||
|
|
||||||
self.convert_variant(
|
self.convert_variant(
|
||||||
src,
|
src,
|
||||||
@@ -2055,16 +2061,8 @@ impl Compiler<'_, '_> {
|
|||||||
InterfaceType::Enum(t) => &self.types[*t],
|
InterfaceType::Enum(t) => &self.types[*t],
|
||||||
_ => panic!("expected an option"),
|
_ => panic!("expected an option"),
|
||||||
};
|
};
|
||||||
let src_info = VariantInfo::new(
|
let src_info = variant_info(self.types, src_ty.names.iter().map(|_| InterfaceType::Unit));
|
||||||
self.types,
|
let dst_info = variant_info(self.types, dst_ty.names.iter().map(|_| InterfaceType::Unit));
|
||||||
src.opts(),
|
|
||||||
src_ty.names.iter().map(|_| InterfaceType::Unit),
|
|
||||||
);
|
|
||||||
let dst_info = VariantInfo::new(
|
|
||||||
self.types,
|
|
||||||
dst.opts(),
|
|
||||||
dst_ty.names.iter().map(|_| InterfaceType::Unit),
|
|
||||||
);
|
|
||||||
|
|
||||||
let unit = &InterfaceType::Unit;
|
let unit = &InterfaceType::Unit;
|
||||||
self.convert_variant(
|
self.convert_variant(
|
||||||
@@ -2088,19 +2086,19 @@ impl Compiler<'_, '_> {
|
|||||||
|
|
||||||
fn translate_option(
|
fn translate_option(
|
||||||
&mut self,
|
&mut self,
|
||||||
src_ty: TypeInterfaceIndex,
|
src_ty: TypeOptionIndex,
|
||||||
src: &Source<'_>,
|
src: &Source<'_>,
|
||||||
dst_ty: &InterfaceType,
|
dst_ty: &InterfaceType,
|
||||||
dst: &Destination,
|
dst: &Destination,
|
||||||
) {
|
) {
|
||||||
let src_ty = &self.types[src_ty];
|
let src_ty = &self.types[src_ty].ty;
|
||||||
let dst_ty = match dst_ty {
|
let dst_ty = match dst_ty {
|
||||||
InterfaceType::Option(t) => &self.types[*t],
|
InterfaceType::Option(t) => &self.types[*t].ty,
|
||||||
_ => panic!("expected an option"),
|
_ => panic!("expected an option"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let src_info = VariantInfo::new(self.types, src.opts(), [InterfaceType::Unit, *src_ty]);
|
let src_info = variant_info(self.types, [InterfaceType::Unit, *src_ty]);
|
||||||
let dst_info = VariantInfo::new(self.types, dst.opts(), [InterfaceType::Unit, *dst_ty]);
|
let dst_info = variant_info(self.types, [InterfaceType::Unit, *dst_ty]);
|
||||||
|
|
||||||
self.convert_variant(
|
self.convert_variant(
|
||||||
src,
|
src,
|
||||||
@@ -2138,8 +2136,8 @@ impl Compiler<'_, '_> {
|
|||||||
_ => panic!("expected an expected"),
|
_ => panic!("expected an expected"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let src_info = VariantInfo::new(self.types, src.opts(), [src_ty.ok, src_ty.err]);
|
let src_info = variant_info(self.types, [src_ty.ok, src_ty.err]);
|
||||||
let dst_info = VariantInfo::new(self.types, dst.opts(), [dst_ty.ok, dst_ty.err]);
|
let dst_info = variant_info(self.types, [dst_ty.ok, dst_ty.err]);
|
||||||
|
|
||||||
self.convert_variant(
|
self.convert_variant(
|
||||||
src,
|
src,
|
||||||
@@ -2316,7 +2314,7 @@ impl Compiler<'_, '_> {
|
|||||||
self.instruction(GlobalSet(flags_global.as_u32()));
|
self.instruction(GlobalSet(flags_global.as_u32()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_aligned(&mut self, opts: &Options, addr_local: u32, align: usize) {
|
fn verify_aligned(&mut self, opts: &Options, addr_local: u32, align: u32) {
|
||||||
// If the alignment is 1 then everything is trivially aligned and the
|
// If the alignment is 1 then everything is trivially aligned and the
|
||||||
// check can be omitted.
|
// check can be omitted.
|
||||||
if align == 1 {
|
if align == 1 {
|
||||||
@@ -2324,7 +2322,7 @@ impl Compiler<'_, '_> {
|
|||||||
}
|
}
|
||||||
self.instruction(LocalGet(addr_local));
|
self.instruction(LocalGet(addr_local));
|
||||||
assert!(align.is_power_of_two());
|
assert!(align.is_power_of_two());
|
||||||
self.ptr_uconst(opts, u32::try_from(align - 1).unwrap());
|
self.ptr_uconst(opts, align - 1);
|
||||||
self.ptr_and(opts);
|
self.ptr_and(opts);
|
||||||
self.ptr_if(opts, BlockType::Empty);
|
self.ptr_if(opts, BlockType::Empty);
|
||||||
self.trap(Trap::UnalignedPointer);
|
self.trap(Trap::UnalignedPointer);
|
||||||
@@ -2343,20 +2341,20 @@ impl Compiler<'_, '_> {
|
|||||||
self.instruction(LocalGet(mem.addr.idx));
|
self.instruction(LocalGet(mem.addr.idx));
|
||||||
self.ptr_uconst(mem.opts, mem.offset);
|
self.ptr_uconst(mem.opts, mem.offset);
|
||||||
self.ptr_add(mem.opts);
|
self.ptr_add(mem.opts);
|
||||||
self.ptr_uconst(mem.opts, u32::try_from(align - 1).unwrap());
|
self.ptr_uconst(mem.opts, align - 1);
|
||||||
self.ptr_and(mem.opts);
|
self.ptr_and(mem.opts);
|
||||||
self.ptr_if(mem.opts, BlockType::Empty);
|
self.ptr_if(mem.opts, BlockType::Empty);
|
||||||
self.trap(Trap::AssertFailed("pointer not aligned"));
|
self.trap(Trap::AssertFailed("pointer not aligned"));
|
||||||
self.instruction(End);
|
self.instruction(End);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn malloc<'a>(&mut self, opts: &'a Options, size: MallocSize, align: usize) -> Memory<'a> {
|
fn malloc<'a>(&mut self, opts: &'a Options, size: MallocSize, align: u32) -> Memory<'a> {
|
||||||
let realloc = opts.realloc.unwrap();
|
let realloc = opts.realloc.unwrap();
|
||||||
self.ptr_uconst(opts, 0);
|
self.ptr_uconst(opts, 0);
|
||||||
self.ptr_uconst(opts, 0);
|
self.ptr_uconst(opts, 0);
|
||||||
self.ptr_uconst(opts, u32::try_from(align).unwrap());
|
self.ptr_uconst(opts, align);
|
||||||
match size {
|
match size {
|
||||||
MallocSize::Const(size) => self.ptr_uconst(opts, u32::try_from(size).unwrap()),
|
MallocSize::Const(size) => self.ptr_uconst(opts, size),
|
||||||
MallocSize::Local(idx) => self.instruction(LocalGet(idx)),
|
MallocSize::Local(idx) => self.instruction(LocalGet(idx)),
|
||||||
}
|
}
|
||||||
self.instruction(Call(realloc.as_u32()));
|
self.instruction(Call(realloc.as_u32()));
|
||||||
@@ -2364,12 +2362,7 @@ impl Compiler<'_, '_> {
|
|||||||
self.memory_operand(opts, addr, align)
|
self.memory_operand(opts, addr, align)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn memory_operand<'a>(
|
fn memory_operand<'a>(&mut self, opts: &'a Options, addr: TempLocal, align: u32) -> Memory<'a> {
|
||||||
&mut self,
|
|
||||||
opts: &'a Options,
|
|
||||||
addr: TempLocal,
|
|
||||||
align: usize,
|
|
||||||
) -> Memory<'a> {
|
|
||||||
let ret = Memory {
|
let ret = Memory {
|
||||||
addr,
|
addr,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
@@ -2795,9 +2788,9 @@ impl<'a> Source<'a> {
|
|||||||
Source::Memory(mem)
|
Source::Memory(mem)
|
||||||
}
|
}
|
||||||
Source::Stack(stack) => {
|
Source::Stack(stack) => {
|
||||||
let cnt = types.flatten_types(stack.opts, [ty]).len();
|
let cnt = types.flatten_types(stack.opts, [ty]).len() as u32;
|
||||||
offset += cnt;
|
offset += cnt;
|
||||||
Source::Stack(stack.slice(offset - cnt..offset))
|
Source::Stack(stack.slice((offset - cnt) as usize..offset as usize))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -2815,7 +2808,11 @@ impl<'a> Source<'a> {
|
|||||||
Source::Stack(s.slice(1..s.locals.len()).slice(0..flat_len))
|
Source::Stack(s.slice(1..s.locals.len()).slice(0..flat_len))
|
||||||
}
|
}
|
||||||
Source::Memory(mem) => {
|
Source::Memory(mem) => {
|
||||||
let mem = info.payload_offset(case, mem);
|
let mem = if mem.opts.memory64 {
|
||||||
|
mem.bump(info.payload_offset64)
|
||||||
|
} else {
|
||||||
|
mem.bump(info.payload_offset32)
|
||||||
|
};
|
||||||
Source::Memory(mem)
|
Source::Memory(mem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2846,9 +2843,9 @@ impl<'a> Destination<'a> {
|
|||||||
Destination::Memory(mem)
|
Destination::Memory(mem)
|
||||||
}
|
}
|
||||||
Destination::Stack(s, opts) => {
|
Destination::Stack(s, opts) => {
|
||||||
let cnt = types.flatten_types(opts, [ty]).len();
|
let cnt = types.flatten_types(opts, [ty]).len() as u32;
|
||||||
offset += cnt;
|
offset += cnt;
|
||||||
Destination::Stack(&s[offset - cnt..offset], opts)
|
Destination::Stack(&s[(offset - cnt) as usize..offset as usize], opts)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -2866,7 +2863,11 @@ impl<'a> Destination<'a> {
|
|||||||
Destination::Stack(&s[1..][..flat_len], opts)
|
Destination::Stack(&s[1..][..flat_len], opts)
|
||||||
}
|
}
|
||||||
Destination::Memory(mem) => {
|
Destination::Memory(mem) => {
|
||||||
let mem = info.payload_offset(case, mem);
|
let mem = if mem.opts.memory64 {
|
||||||
|
mem.bump(info.payload_offset64)
|
||||||
|
} else {
|
||||||
|
mem.bump(info.payload_offset32)
|
||||||
|
};
|
||||||
Destination::Memory(mem)
|
Destination::Memory(mem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2881,38 +2882,18 @@ impl<'a> Destination<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn next_field_offset<'a>(
|
fn next_field_offset<'a>(
|
||||||
offset: &mut usize,
|
offset: &mut u32,
|
||||||
types: &ComponentTypes,
|
types: &ComponentTypes,
|
||||||
field: &InterfaceType,
|
field: &InterfaceType,
|
||||||
mem: &Memory<'a>,
|
mem: &Memory<'a>,
|
||||||
) -> Memory<'a> {
|
) -> Memory<'a> {
|
||||||
let (size, align) = types.size_align(mem.opts, field);
|
let abi = types.canonical_abi(field);
|
||||||
*offset = align_to(*offset, align) + size;
|
let offset = if mem.opts.memory64 {
|
||||||
mem.bump(*offset - size)
|
abi.next_field64(offset)
|
||||||
}
|
} else {
|
||||||
|
abi.next_field32(offset)
|
||||||
struct VariantInfo {
|
};
|
||||||
size: DiscriminantSize,
|
mem.bump(offset)
|
||||||
align: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VariantInfo {
|
|
||||||
fn new<I>(types: &ComponentTypes, options: &Options, iter: I) -> VariantInfo
|
|
||||||
where
|
|
||||||
I: IntoIterator<Item = InterfaceType>,
|
|
||||||
I::IntoIter: ExactSizeIterator,
|
|
||||||
{
|
|
||||||
let iter = iter.into_iter();
|
|
||||||
let size = DiscriminantSize::from_count(iter.len()).unwrap();
|
|
||||||
VariantInfo {
|
|
||||||
size,
|
|
||||||
align: usize::from(size).max(iter.map(|i| types.align(options, &i)).max().unwrap_or(1)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn payload_offset<'a>(&self, _case: &InterfaceType, mem: &Memory<'a>) -> Memory<'a> {
|
|
||||||
mem.bump(align_to(self.size.into(), self.align))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Memory<'a> {
|
impl<'a> Memory<'a> {
|
||||||
@@ -2924,11 +2905,11 @@ impl<'a> Memory<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bump(&self, offset: usize) -> Memory<'a> {
|
fn bump(&self, offset: u32) -> Memory<'a> {
|
||||||
Memory {
|
Memory {
|
||||||
opts: self.opts,
|
opts: self.opts,
|
||||||
addr: TempLocal::new(self.addr.idx, self.addr.ty),
|
addr: TempLocal::new(self.addr.idx, self.addr.ty),
|
||||||
offset: self.offset + u32::try_from(offset).unwrap(),
|
offset: self.offset + offset,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2949,8 +2930,16 @@ struct VariantCase<'a> {
|
|||||||
dst_ty: &'a InterfaceType,
|
dst_ty: &'a InterfaceType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn variant_info<I>(types: &ComponentTypes, cases: I) -> VariantInfo
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = InterfaceType>,
|
||||||
|
I::IntoIter: ExactSizeIterator,
|
||||||
|
{
|
||||||
|
VariantInfo::new(cases.into_iter().map(|i| types.canonical_abi(&i))).0
|
||||||
|
}
|
||||||
|
|
||||||
enum MallocSize {
|
enum MallocSize {
|
||||||
Const(usize),
|
Const(u32),
|
||||||
Local(u32),
|
Local(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use anyhow::Result;
|
|||||||
use arbitrary::Arbitrary;
|
use arbitrary::Arbitrary;
|
||||||
use std::mem::MaybeUninit;
|
use std::mem::MaybeUninit;
|
||||||
use wasmtime::component::__internal::{
|
use wasmtime::component::__internal::{
|
||||||
ComponentTypes, InterfaceType, Memory, MemoryMut, Options, StoreOpaque,
|
CanonicalAbiInfo, ComponentTypes, InterfaceType, Memory, MemoryMut, Options, StoreOpaque,
|
||||||
};
|
};
|
||||||
use wasmtime::component::{ComponentParams, ComponentType, Func, Lift, Lower, TypedFunc, Val};
|
use wasmtime::component::{ComponentParams, ComponentType, Func, Lift, Lower, TypedFunc, Val};
|
||||||
use wasmtime::{AsContextMut, Config, Engine, StoreContextMut};
|
use wasmtime::{AsContextMut, Config, Engine, StoreContextMut};
|
||||||
@@ -68,8 +68,7 @@ macro_rules! forward_impls {
|
|||||||
unsafe impl ComponentType for $a {
|
unsafe impl ComponentType for $a {
|
||||||
type Lower = <$b as ComponentType>::Lower;
|
type Lower = <$b as ComponentType>::Lower;
|
||||||
|
|
||||||
const SIZE32: usize = <$b as ComponentType>::SIZE32;
|
const ABI: CanonicalAbiInfo = <$b as ComponentType>::ABI;
|
||||||
const ALIGN32: u32 = <$b as ComponentType>::ALIGN32;
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn typecheck(ty: &InterfaceType, types: &ComponentTypes) -> Result<()> {
|
fn typecheck(ty: &InterfaceType, types: &ComponentTypes) -> Result<()> {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::component::instance::{Instance, InstanceData};
|
use crate::component::instance::{Instance, InstanceData};
|
||||||
use crate::component::types::{SizeAndAlignment, Type};
|
use crate::component::types::Type;
|
||||||
use crate::component::values::Val;
|
use crate::component::values::Val;
|
||||||
use crate::store::{StoreOpaque, Stored};
|
use crate::store::{StoreOpaque, Stored};
|
||||||
use crate::{AsContext, AsContextMut, StoreContextMut, ValRaw};
|
use crate::{AsContext, AsContextMut, StoreContextMut, ValRaw};
|
||||||
@@ -8,8 +8,8 @@ use std::mem::{self, MaybeUninit};
|
|||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use wasmtime_environ::component::{
|
use wasmtime_environ::component::{
|
||||||
CanonicalOptions, ComponentTypes, CoreDef, RuntimeComponentInstanceIndex, TypeFuncIndex,
|
CanonicalAbiInfo, CanonicalOptions, ComponentTypes, CoreDef, RuntimeComponentInstanceIndex,
|
||||||
MAX_FLAT_PARAMS, MAX_FLAT_RESULTS,
|
TypeFuncIndex, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS,
|
||||||
};
|
};
|
||||||
use wasmtime_runtime::{Export, ExportFunction, VMTrampoline};
|
use wasmtime_runtime::{Export, ExportFunction, VMTrampoline};
|
||||||
|
|
||||||
@@ -558,18 +558,15 @@ impl Func {
|
|||||||
args: &[Val],
|
args: &[Val],
|
||||||
dst: &mut MaybeUninit<[ValRaw; MAX_FLAT_PARAMS]>,
|
dst: &mut MaybeUninit<[ValRaw; MAX_FLAT_PARAMS]>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut size = 0;
|
let abi = CanonicalAbiInfo::record(params.iter().map(|t| t.canonical_abi()));
|
||||||
let mut alignment = 1;
|
|
||||||
for ty in params {
|
|
||||||
alignment = alignment.max(ty.size_and_alignment().alignment);
|
|
||||||
ty.next_field(&mut size);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut memory = MemoryMut::new(store.as_context_mut(), options);
|
let mut memory = MemoryMut::new(store.as_context_mut(), options);
|
||||||
let ptr = memory.realloc(0, 0, alignment, size)?;
|
let size = usize::try_from(abi.size32).unwrap();
|
||||||
|
let ptr = memory.realloc(0, 0, abi.align32, size)?;
|
||||||
let mut offset = ptr;
|
let mut offset = ptr;
|
||||||
for (ty, arg) in params.iter().zip(args) {
|
for (ty, arg) in params.iter().zip(args) {
|
||||||
arg.store(&mut memory, ty.next_field(&mut offset))?;
|
let abi = ty.canonical_abi();
|
||||||
|
arg.store(&mut memory, abi.next_field32_size(&mut offset))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
map_maybe_uninit!(dst[0]).write(ValRaw::i64(ptr as i64));
|
map_maybe_uninit!(dst[0]).write(ValRaw::i64(ptr as i64));
|
||||||
@@ -582,17 +579,17 @@ impl Func {
|
|||||||
ty: &Type,
|
ty: &Type,
|
||||||
src: &mut std::slice::Iter<'_, ValRaw>,
|
src: &mut std::slice::Iter<'_, ValRaw>,
|
||||||
) -> Result<Val> {
|
) -> Result<Val> {
|
||||||
let SizeAndAlignment { size, alignment } = ty.size_and_alignment();
|
let abi = ty.canonical_abi();
|
||||||
// FIXME: needs to read an i64 for memory64
|
// FIXME: needs to read an i64 for memory64
|
||||||
let ptr = usize::try_from(src.next().unwrap().get_u32())?;
|
let ptr = usize::try_from(src.next().unwrap().get_u32())?;
|
||||||
if ptr % usize::try_from(alignment)? != 0 {
|
if ptr % usize::try_from(abi.align32)? != 0 {
|
||||||
bail!("return pointer not aligned");
|
bail!("return pointer not aligned");
|
||||||
}
|
}
|
||||||
|
|
||||||
let bytes = mem
|
let bytes = mem
|
||||||
.as_slice()
|
.as_slice()
|
||||||
.get(ptr..)
|
.get(ptr..)
|
||||||
.and_then(|b| b.get(..size))
|
.and_then(|b| b.get(..usize::try_from(abi.size32).unwrap()))
|
||||||
.ok_or_else(|| anyhow::anyhow!("pointer out of bounds of memory"))?;
|
.ok_or_else(|| anyhow::anyhow!("pointer out of bounds of memory"))?;
|
||||||
|
|
||||||
Val::load(ty, mem, bytes)
|
Val::load(ty, mem, bytes)
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use crate::component::func::{Memory, MemoryMut, Options};
|
use crate::component::func::{Memory, MemoryMut, Options};
|
||||||
use crate::component::types::SizeAndAlignment;
|
|
||||||
use crate::component::{ComponentParams, ComponentType, Lift, Lower, Type, Val};
|
use crate::component::{ComponentParams, ComponentType, Lift, Lower, Type, Val};
|
||||||
use crate::{AsContextMut, StoreContextMut, ValRaw};
|
use crate::{AsContextMut, StoreContextMut, ValRaw};
|
||||||
use anyhow::{anyhow, bail, Context, Result};
|
use anyhow::{anyhow, bail, Context, Result};
|
||||||
@@ -9,7 +8,8 @@ use std::panic::{self, AssertUnwindSafe};
|
|||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use wasmtime_environ::component::{
|
use wasmtime_environ::component::{
|
||||||
ComponentTypes, StringEncoding, TypeFuncIndex, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS,
|
CanonicalAbiInfo, ComponentTypes, StringEncoding, TypeFuncIndex, MAX_FLAT_PARAMS,
|
||||||
|
MAX_FLAT_RESULTS,
|
||||||
};
|
};
|
||||||
use wasmtime_runtime::component::{
|
use wasmtime_runtime::component::{
|
||||||
InstanceFlags, VMComponentContext, VMLowering, VMLoweringCallee,
|
InstanceFlags, VMComponentContext, VMLowering, VMLoweringCallee,
|
||||||
@@ -413,26 +413,19 @@ where
|
|||||||
.collect::<Result<Box<[_]>>>()?;
|
.collect::<Result<Box<[_]>>>()?;
|
||||||
ret_index = param_count;
|
ret_index = param_count;
|
||||||
} else {
|
} else {
|
||||||
let param_layout = {
|
let param_abi = CanonicalAbiInfo::record(params.iter().map(|t| t.canonical_abi()));
|
||||||
let mut size = 0;
|
|
||||||
let mut alignment = 1;
|
|
||||||
for ty in params.iter() {
|
|
||||||
alignment = alignment.max(ty.size_and_alignment().alignment);
|
|
||||||
ty.next_field(&mut size);
|
|
||||||
}
|
|
||||||
SizeAndAlignment { size, alignment }
|
|
||||||
};
|
|
||||||
|
|
||||||
let memory = Memory::new(cx.0, &options);
|
let memory = Memory::new(cx.0, &options);
|
||||||
let mut offset = validate_inbounds_dynamic(param_layout, memory.as_slice(), &storage[0])?;
|
let mut offset = validate_inbounds_dynamic(¶m_abi, memory.as_slice(), &storage[0])?;
|
||||||
args = params
|
args = params
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ty| {
|
.map(|ty| {
|
||||||
|
let abi = ty.canonical_abi();
|
||||||
|
let size = usize::try_from(abi.size32).unwrap();
|
||||||
Val::load(
|
Val::load(
|
||||||
ty,
|
ty,
|
||||||
&memory,
|
&memory,
|
||||||
&memory.as_slice()[ty.next_field(&mut offset)..]
|
&memory.as_slice()[abi.next_field32_size(&mut offset)..][..size],
|
||||||
[..ty.size_and_alignment().size],
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect::<Result<Box<[_]>>>()?;
|
.collect::<Result<Box<[_]>>>()?;
|
||||||
@@ -451,7 +444,7 @@ where
|
|||||||
let ret_ptr = &storage[ret_index];
|
let ret_ptr = &storage[ret_index];
|
||||||
let mut memory = MemoryMut::new(cx.as_context_mut(), &options);
|
let mut memory = MemoryMut::new(cx.as_context_mut(), &options);
|
||||||
let ptr =
|
let ptr =
|
||||||
validate_inbounds_dynamic(result.size_and_alignment(), memory.as_slice_mut(), ret_ptr)?;
|
validate_inbounds_dynamic(result.canonical_abi(), memory.as_slice_mut(), ret_ptr)?;
|
||||||
ret.store(&mut memory, ptr)?;
|
ret.store(&mut memory, ptr)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -460,17 +453,13 @@ where
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_inbounds_dynamic(
|
fn validate_inbounds_dynamic(abi: &CanonicalAbiInfo, memory: &[u8], ptr: &ValRaw) -> Result<usize> {
|
||||||
SizeAndAlignment { size, alignment }: SizeAndAlignment,
|
|
||||||
memory: &[u8],
|
|
||||||
ptr: &ValRaw,
|
|
||||||
) -> Result<usize> {
|
|
||||||
// FIXME: needs memory64 support
|
// FIXME: needs memory64 support
|
||||||
let ptr = usize::try_from(ptr.get_u32())?;
|
let ptr = usize::try_from(ptr.get_u32())?;
|
||||||
if ptr % usize::try_from(alignment)? != 0 {
|
if ptr % usize::try_from(abi.align32)? != 0 {
|
||||||
bail!("pointer not aligned");
|
bail!("pointer not aligned");
|
||||||
}
|
}
|
||||||
let end = match ptr.checked_add(size) {
|
let end = match ptr.checked_add(usize::try_from(abi.size32).unwrap()) {
|
||||||
Some(n) => n,
|
Some(n) => n,
|
||||||
None => bail!("pointer size overflow"),
|
None => bail!("pointer size overflow"),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ use std::marker;
|
|||||||
use std::mem::{self, MaybeUninit};
|
use std::mem::{self, MaybeUninit};
|
||||||
use std::str;
|
use std::str;
|
||||||
use wasmtime_environ::component::{
|
use wasmtime_environ::component::{
|
||||||
ComponentTypes, InterfaceType, StringEncoding, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS,
|
CanonicalAbiInfo, ComponentTypes, InterfaceType, StringEncoding, VariantInfo, MAX_FLAT_PARAMS,
|
||||||
|
MAX_FLAT_RESULTS,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A statically-typed version of [`Func`] which takes `Params` as input and
|
/// A statically-typed version of [`Func`] which takes `Params` as input and
|
||||||
@@ -363,13 +364,14 @@ pub unsafe trait ComponentType {
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
type Lower: Copy;
|
type Lower: Copy;
|
||||||
|
|
||||||
/// The size, in bytes, that this type has in the canonical ABI.
|
/// The information about this type's canonical ABI (size/align/etc).
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
const SIZE32: usize;
|
const ABI: CanonicalAbiInfo;
|
||||||
|
|
||||||
/// The alignment, in bytes, that this type has in the canonical ABI.
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
const ALIGN32: u32;
|
const SIZE32: usize = Self::ABI.size32 as usize;
|
||||||
|
#[doc(hidden)]
|
||||||
|
const ALIGN32: u32 = Self::ABI.align32;
|
||||||
|
|
||||||
/// Returns the number of core wasm abi values will be used to represent
|
/// Returns the number of core wasm abi values will be used to represent
|
||||||
/// this type in its lowered form.
|
/// this type in its lowered form.
|
||||||
@@ -382,14 +384,19 @@ pub unsafe trait ComponentType {
|
|||||||
mem::size_of::<Self::Lower>() / mem::size_of::<ValRaw>()
|
mem::size_of::<Self::Lower>() / mem::size_of::<ValRaw>()
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: need SIZE64 and ALIGN64 probably
|
|
||||||
|
|
||||||
/// Performs a type-check to see whether this component value type matches
|
/// Performs a type-check to see whether this component value type matches
|
||||||
/// the interface type `ty` provided.
|
/// the interface type `ty` provided.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
fn typecheck(ty: &InterfaceType, types: &ComponentTypes) -> Result<()>;
|
fn typecheck(ty: &InterfaceType, types: &ComponentTypes) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub unsafe trait ComponentVariant: ComponentType {
|
||||||
|
const CASES: &'static [CanonicalAbiInfo];
|
||||||
|
const INFO: VariantInfo = VariantInfo::new_static(Self::CASES);
|
||||||
|
const PAYLOAD_OFFSET32: usize = Self::INFO.payload_offset32 as usize;
|
||||||
|
}
|
||||||
|
|
||||||
/// Host types which can be passed to WebAssembly components.
|
/// Host types which can be passed to WebAssembly components.
|
||||||
///
|
///
|
||||||
/// This trait is implemented for all types that can be passed to components
|
/// This trait is implemented for all types that can be passed to components
|
||||||
@@ -475,8 +482,7 @@ macro_rules! forward_type_impls {
|
|||||||
unsafe impl <$($generics)*> ComponentType for $a {
|
unsafe impl <$($generics)*> ComponentType for $a {
|
||||||
type Lower = <$b as ComponentType>::Lower;
|
type Lower = <$b as ComponentType>::Lower;
|
||||||
|
|
||||||
const SIZE32: usize = <$b as ComponentType>::SIZE32;
|
const ABI: CanonicalAbiInfo = <$b as ComponentType>::ABI;
|
||||||
const ALIGN32: u32 = <$b as ComponentType>::ALIGN32;
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn typecheck(ty: &InterfaceType, types: &ComponentTypes) -> Result<()> {
|
fn typecheck(ty: &InterfaceType, types: &ComponentTypes) -> Result<()> {
|
||||||
@@ -570,17 +576,11 @@ forward_list_lifts! {
|
|||||||
// Macro to help generate `ComponentType` implementations for primitive types
|
// Macro to help generate `ComponentType` implementations for primitive types
|
||||||
// such as integers, char, bool, etc.
|
// such as integers, char, bool, etc.
|
||||||
macro_rules! integers {
|
macro_rules! integers {
|
||||||
($($primitive:ident = $ty:ident in $field:ident/$get:ident,)*) => ($(
|
($($primitive:ident = $ty:ident in $field:ident/$get:ident with abi:$abi:ident,)*) => ($(
|
||||||
unsafe impl ComponentType for $primitive {
|
unsafe impl ComponentType for $primitive {
|
||||||
type Lower = ValRaw;
|
type Lower = ValRaw;
|
||||||
|
|
||||||
const SIZE32: usize = mem::size_of::<$primitive>();
|
const ABI: CanonicalAbiInfo = CanonicalAbiInfo::$abi;
|
||||||
|
|
||||||
// Note that this specifically doesn't use `align_of` as some
|
|
||||||
// host platforms have a 4-byte alignment for primitive types but
|
|
||||||
// the canonical abi always has the same size/alignment for these
|
|
||||||
// types.
|
|
||||||
const ALIGN32: u32 = mem::size_of::<$primitive>() as u32;
|
|
||||||
|
|
||||||
fn typecheck(ty: &InterfaceType, _types: &ComponentTypes) -> Result<()> {
|
fn typecheck(ty: &InterfaceType, _types: &ComponentTypes) -> Result<()> {
|
||||||
match ty {
|
match ty {
|
||||||
@@ -624,18 +624,18 @@ macro_rules! integers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
integers! {
|
integers! {
|
||||||
i8 = S8 in i32/get_i32,
|
i8 = S8 in i32/get_i32 with abi:SCALAR1,
|
||||||
u8 = U8 in u32/get_u32,
|
u8 = U8 in u32/get_u32 with abi:SCALAR1,
|
||||||
i16 = S16 in i32/get_i32,
|
i16 = S16 in i32/get_i32 with abi:SCALAR2,
|
||||||
u16 = U16 in u32/get_u32,
|
u16 = U16 in u32/get_u32 with abi:SCALAR2,
|
||||||
i32 = S32 in i32/get_i32,
|
i32 = S32 in i32/get_i32 with abi:SCALAR4,
|
||||||
u32 = U32 in u32/get_u32,
|
u32 = U32 in u32/get_u32 with abi:SCALAR4,
|
||||||
i64 = S64 in i64/get_i64,
|
i64 = S64 in i64/get_i64 with abi:SCALAR8,
|
||||||
u64 = U64 in u64/get_u64,
|
u64 = U64 in u64/get_u64 with abi:SCALAR8,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! floats {
|
macro_rules! floats {
|
||||||
($($float:ident/$get_float:ident = $ty:ident)*) => ($(const _: () = {
|
($($float:ident/$get_float:ident = $ty:ident with abi:$abi:ident)*) => ($(const _: () = {
|
||||||
/// All floats in-and-out of the canonical abi always have their nan
|
/// All floats in-and-out of the canonical abi always have their nan
|
||||||
/// payloads canonicalized. conveniently the `NAN` constant in rust has
|
/// payloads canonicalized. conveniently the `NAN` constant in rust has
|
||||||
/// the same representation as canonical nan, so we can use that for the
|
/// the same representation as canonical nan, so we can use that for the
|
||||||
@@ -652,11 +652,7 @@ macro_rules! floats {
|
|||||||
unsafe impl ComponentType for $float {
|
unsafe impl ComponentType for $float {
|
||||||
type Lower = ValRaw;
|
type Lower = ValRaw;
|
||||||
|
|
||||||
const SIZE32: usize = mem::size_of::<$float>();
|
const ABI: CanonicalAbiInfo = CanonicalAbiInfo::$abi;
|
||||||
|
|
||||||
// note that like integers size is used here instead of alignment to
|
|
||||||
// respect the canonical abi, not host platforms.
|
|
||||||
const ALIGN32: u32 = mem::size_of::<$float>() as u32;
|
|
||||||
|
|
||||||
fn typecheck(ty: &InterfaceType, _types: &ComponentTypes) -> Result<()> {
|
fn typecheck(ty: &InterfaceType, _types: &ComponentTypes) -> Result<()> {
|
||||||
match ty {
|
match ty {
|
||||||
@@ -701,15 +697,14 @@ macro_rules! floats {
|
|||||||
}
|
}
|
||||||
|
|
||||||
floats! {
|
floats! {
|
||||||
f32/get_f32 = Float32
|
f32/get_f32 = Float32 with abi:SCALAR4
|
||||||
f64/get_f64 = Float64
|
f64/get_f64 = Float64 with abi:SCALAR8
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl ComponentType for bool {
|
unsafe impl ComponentType for bool {
|
||||||
type Lower = ValRaw;
|
type Lower = ValRaw;
|
||||||
|
|
||||||
const SIZE32: usize = 1;
|
const ABI: CanonicalAbiInfo = CanonicalAbiInfo::SCALAR1;
|
||||||
const ALIGN32: u32 = 1;
|
|
||||||
|
|
||||||
fn typecheck(ty: &InterfaceType, _types: &ComponentTypes) -> Result<()> {
|
fn typecheck(ty: &InterfaceType, _types: &ComponentTypes) -> Result<()> {
|
||||||
match ty {
|
match ty {
|
||||||
@@ -758,8 +753,7 @@ unsafe impl Lift for bool {
|
|||||||
unsafe impl ComponentType for char {
|
unsafe impl ComponentType for char {
|
||||||
type Lower = ValRaw;
|
type Lower = ValRaw;
|
||||||
|
|
||||||
const SIZE32: usize = 4;
|
const ABI: CanonicalAbiInfo = CanonicalAbiInfo::SCALAR4;
|
||||||
const ALIGN32: u32 = 4;
|
|
||||||
|
|
||||||
fn typecheck(ty: &InterfaceType, _types: &ComponentTypes) -> Result<()> {
|
fn typecheck(ty: &InterfaceType, _types: &ComponentTypes) -> Result<()> {
|
||||||
match ty {
|
match ty {
|
||||||
@@ -810,8 +804,7 @@ const MAX_STRING_BYTE_LENGTH: usize = (1 << 31) - 1;
|
|||||||
unsafe impl ComponentType for str {
|
unsafe impl ComponentType for str {
|
||||||
type Lower = [ValRaw; 2];
|
type Lower = [ValRaw; 2];
|
||||||
|
|
||||||
const SIZE32: usize = 8;
|
const ABI: CanonicalAbiInfo = CanonicalAbiInfo::POINTER_PAIR;
|
||||||
const ALIGN32: u32 = 4;
|
|
||||||
|
|
||||||
fn typecheck(ty: &InterfaceType, _types: &ComponentTypes) -> Result<()> {
|
fn typecheck(ty: &InterfaceType, _types: &ComponentTypes) -> Result<()> {
|
||||||
match ty {
|
match ty {
|
||||||
@@ -1078,8 +1071,7 @@ impl WasmStr {
|
|||||||
unsafe impl ComponentType for WasmStr {
|
unsafe impl ComponentType for WasmStr {
|
||||||
type Lower = <str as ComponentType>::Lower;
|
type Lower = <str as ComponentType>::Lower;
|
||||||
|
|
||||||
const SIZE32: usize = <str as ComponentType>::SIZE32;
|
const ABI: CanonicalAbiInfo = CanonicalAbiInfo::POINTER_PAIR;
|
||||||
const ALIGN32: u32 = <str as ComponentType>::ALIGN32;
|
|
||||||
|
|
||||||
fn typecheck(ty: &InterfaceType, _types: &ComponentTypes) -> Result<()> {
|
fn typecheck(ty: &InterfaceType, _types: &ComponentTypes) -> Result<()> {
|
||||||
match ty {
|
match ty {
|
||||||
@@ -1114,8 +1106,7 @@ where
|
|||||||
{
|
{
|
||||||
type Lower = [ValRaw; 2];
|
type Lower = [ValRaw; 2];
|
||||||
|
|
||||||
const SIZE32: usize = 8;
|
const ABI: CanonicalAbiInfo = CanonicalAbiInfo::POINTER_PAIR;
|
||||||
const ALIGN32: u32 = 4;
|
|
||||||
|
|
||||||
fn typecheck(ty: &InterfaceType, types: &ComponentTypes) -> Result<()> {
|
fn typecheck(ty: &InterfaceType, types: &ComponentTypes) -> Result<()> {
|
||||||
match ty {
|
match ty {
|
||||||
@@ -1324,8 +1315,7 @@ raw_wasm_list_accessors! {
|
|||||||
unsafe impl<T: ComponentType> ComponentType for WasmList<T> {
|
unsafe impl<T: ComponentType> ComponentType for WasmList<T> {
|
||||||
type Lower = <[T] as ComponentType>::Lower;
|
type Lower = <[T] as ComponentType>::Lower;
|
||||||
|
|
||||||
const SIZE32: usize = <[T] as ComponentType>::SIZE32;
|
const ABI: CanonicalAbiInfo = CanonicalAbiInfo::POINTER_PAIR;
|
||||||
const ALIGN32: u32 = <[T] as ComponentType>::ALIGN32;
|
|
||||||
|
|
||||||
fn typecheck(ty: &InterfaceType, types: &ComponentTypes) -> Result<()> {
|
fn typecheck(ty: &InterfaceType, types: &ComponentTypes) -> Result<()> {
|
||||||
match ty {
|
match ty {
|
||||||
@@ -1354,24 +1344,6 @@ unsafe impl<T: Lift> Lift for WasmList<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Round `a` up to the next multiple of `align`, assuming that `align` is a power of 2.
|
|
||||||
#[inline]
|
|
||||||
pub const fn align_to(a: usize, align: u32) -> usize {
|
|
||||||
debug_assert!(align.is_power_of_two());
|
|
||||||
let align = align as usize;
|
|
||||||
(a + (align - 1)) & !(align - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// For a field of type T starting after `offset` bytes, updates the offset to reflect the correct
|
|
||||||
/// alignment and size of T. Returns the correctly aligned offset for the start of the field.
|
|
||||||
#[inline]
|
|
||||||
pub fn next_field<T: ComponentType>(offset: &mut usize) -> usize {
|
|
||||||
*offset = align_to(*offset, T::ALIGN32);
|
|
||||||
let result = *offset;
|
|
||||||
*offset += T::SIZE32;
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verify that the given wasm type is a tuple with the expected fields in the right order.
|
/// Verify that the given wasm type is a tuple with the expected fields in the right order.
|
||||||
fn typecheck_tuple(
|
fn typecheck_tuple(
|
||||||
ty: &InterfaceType,
|
ty: &InterfaceType,
|
||||||
@@ -1585,17 +1557,24 @@ where
|
|||||||
{
|
{
|
||||||
type Lower = TupleLower2<<u32 as ComponentType>::Lower, T::Lower>;
|
type Lower = TupleLower2<<u32 as ComponentType>::Lower, T::Lower>;
|
||||||
|
|
||||||
const SIZE32: usize = align_to(1, T::ALIGN32) + T::SIZE32;
|
const ABI: CanonicalAbiInfo =
|
||||||
const ALIGN32: u32 = T::ALIGN32;
|
CanonicalAbiInfo::variant_static(&[<() as ComponentType>::ABI, T::ABI]);
|
||||||
|
|
||||||
fn typecheck(ty: &InterfaceType, types: &ComponentTypes) -> Result<()> {
|
fn typecheck(ty: &InterfaceType, types: &ComponentTypes) -> Result<()> {
|
||||||
match ty {
|
match ty {
|
||||||
InterfaceType::Option(t) => T::typecheck(&types[*t], types),
|
InterfaceType::Option(t) => T::typecheck(&types[*t].ty, types),
|
||||||
other => bail!("expected `option` found `{}`", desc(other)),
|
other => bail!("expected `option` found `{}`", desc(other)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe impl<T> ComponentVariant for Option<T>
|
||||||
|
where
|
||||||
|
T: ComponentType,
|
||||||
|
{
|
||||||
|
const CASES: &'static [CanonicalAbiInfo] = &[<() as ComponentType>::ABI, T::ABI];
|
||||||
|
}
|
||||||
|
|
||||||
unsafe impl<T> Lower for Option<T>
|
unsafe impl<T> Lower for Option<T>
|
||||||
where
|
where
|
||||||
T: Lower,
|
T: Lower,
|
||||||
@@ -1635,7 +1614,7 @@ where
|
|||||||
}
|
}
|
||||||
Some(val) => {
|
Some(val) => {
|
||||||
mem.get::<1>(offset)[0] = 1;
|
mem.get::<1>(offset)[0] = 1;
|
||||||
val.store(mem, offset + align_to(1, T::ALIGN32))?;
|
val.store(mem, offset + (Self::INFO.payload_offset32 as usize))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1657,7 +1636,7 @@ where
|
|||||||
fn load(memory: &Memory<'_>, bytes: &[u8]) -> Result<Self> {
|
fn load(memory: &Memory<'_>, bytes: &[u8]) -> Result<Self> {
|
||||||
debug_assert!((bytes.as_ptr() as usize) % (Self::ALIGN32 as usize) == 0);
|
debug_assert!((bytes.as_ptr() as usize) % (Self::ALIGN32 as usize) == 0);
|
||||||
let discrim = bytes[0];
|
let discrim = bytes[0];
|
||||||
let payload = &bytes[align_to(1, T::ALIGN32)..];
|
let payload = &bytes[Self::INFO.payload_offset32 as usize..];
|
||||||
match discrim {
|
match discrim {
|
||||||
0 => Ok(None),
|
0 => Ok(None),
|
||||||
1 => Ok(Some(T::load(memory, payload)?)),
|
1 => Ok(Some(T::load(memory, payload)?)),
|
||||||
@@ -1687,17 +1666,7 @@ where
|
|||||||
{
|
{
|
||||||
type Lower = ResultLower<T::Lower, E::Lower>;
|
type Lower = ResultLower<T::Lower, E::Lower>;
|
||||||
|
|
||||||
const SIZE32: usize = align_to(1, Self::ALIGN32)
|
const ABI: CanonicalAbiInfo = CanonicalAbiInfo::variant_static(&[T::ABI, E::ABI]);
|
||||||
+ if T::SIZE32 > E::SIZE32 {
|
|
||||||
T::SIZE32
|
|
||||||
} else {
|
|
||||||
E::SIZE32
|
|
||||||
};
|
|
||||||
const ALIGN32: u32 = if T::ALIGN32 > E::ALIGN32 {
|
|
||||||
T::ALIGN32
|
|
||||||
} else {
|
|
||||||
E::ALIGN32
|
|
||||||
};
|
|
||||||
|
|
||||||
fn typecheck(ty: &InterfaceType, types: &ComponentTypes) -> Result<()> {
|
fn typecheck(ty: &InterfaceType, types: &ComponentTypes) -> Result<()> {
|
||||||
match ty {
|
match ty {
|
||||||
@@ -1712,6 +1681,14 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe impl<T, E> ComponentVariant for Result<T, E>
|
||||||
|
where
|
||||||
|
T: ComponentType,
|
||||||
|
E: ComponentType,
|
||||||
|
{
|
||||||
|
const CASES: &'static [CanonicalAbiInfo] = &[T::ABI, E::ABI];
|
||||||
|
}
|
||||||
|
|
||||||
unsafe impl<T, E> Lower for Result<T, E>
|
unsafe impl<T, E> Lower for Result<T, E>
|
||||||
where
|
where
|
||||||
T: Lower,
|
T: Lower,
|
||||||
@@ -1756,14 +1733,15 @@ where
|
|||||||
|
|
||||||
fn store<U>(&self, mem: &mut MemoryMut<'_, U>, offset: usize) -> Result<()> {
|
fn store<U>(&self, mem: &mut MemoryMut<'_, U>, offset: usize) -> Result<()> {
|
||||||
debug_assert!(offset % (Self::ALIGN32 as usize) == 0);
|
debug_assert!(offset % (Self::ALIGN32 as usize) == 0);
|
||||||
|
let payload_offset = Self::INFO.payload_offset32 as usize;
|
||||||
match self {
|
match self {
|
||||||
Ok(e) => {
|
Ok(e) => {
|
||||||
mem.get::<1>(offset)[0] = 0;
|
mem.get::<1>(offset)[0] = 0;
|
||||||
e.store(mem, offset + align_to(1, Self::ALIGN32))?;
|
e.store(mem, offset + payload_offset)?;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
mem.get::<1>(offset)[0] = 1;
|
mem.get::<1>(offset)[0] = 1;
|
||||||
e.store(mem, offset + align_to(1, Self::ALIGN32))?;
|
e.store(mem, offset + payload_offset)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1804,9 +1782,8 @@ where
|
|||||||
|
|
||||||
fn load(memory: &Memory<'_>, bytes: &[u8]) -> Result<Self> {
|
fn load(memory: &Memory<'_>, bytes: &[u8]) -> Result<Self> {
|
||||||
debug_assert!((bytes.as_ptr() as usize) % (Self::ALIGN32 as usize) == 0);
|
debug_assert!((bytes.as_ptr() as usize) % (Self::ALIGN32 as usize) == 0);
|
||||||
let align = Self::ALIGN32;
|
|
||||||
let discrim = bytes[0];
|
let discrim = bytes[0];
|
||||||
let payload = &bytes[align_to(1, align)..];
|
let payload = &bytes[Self::INFO.payload_offset32 as usize..];
|
||||||
match discrim {
|
match discrim {
|
||||||
0 => Ok(Ok(T::load(memory, &payload[..T::SIZE32])?)),
|
0 => Ok(Ok(T::load(memory, &payload[..T::SIZE32])?)),
|
||||||
1 => Ok(Err(E::load(memory, &payload[..E::SIZE32])?)),
|
1 => Ok(Err(E::load(memory, &payload[..E::SIZE32])?)),
|
||||||
@@ -1832,22 +1809,9 @@ macro_rules! impl_component_ty_for_tuples {
|
|||||||
{
|
{
|
||||||
type Lower = [<TupleLower$n>]<$($t::Lower),*>;
|
type Lower = [<TupleLower$n>]<$($t::Lower),*>;
|
||||||
|
|
||||||
const SIZE32: usize = {
|
const ABI: CanonicalAbiInfo = CanonicalAbiInfo::record_static(&[
|
||||||
let mut _size = 0;
|
$($t::ABI),*
|
||||||
$(
|
]);
|
||||||
_size = align_to(_size, $t::ALIGN32);
|
|
||||||
_size += $t::SIZE32;
|
|
||||||
)*
|
|
||||||
align_to(_size, Self::ALIGN32)
|
|
||||||
};
|
|
||||||
|
|
||||||
const ALIGN32: u32 = {
|
|
||||||
let mut _align = 1;
|
|
||||||
$(if $t::ALIGN32 > _align {
|
|
||||||
_align = $t::ALIGN32;
|
|
||||||
})*
|
|
||||||
_align
|
|
||||||
};
|
|
||||||
|
|
||||||
fn typecheck(
|
fn typecheck(
|
||||||
ty: &InterfaceType,
|
ty: &InterfaceType,
|
||||||
@@ -1875,7 +1839,7 @@ macro_rules! impl_component_ty_for_tuples {
|
|||||||
fn store<U>(&self, _memory: &mut MemoryMut<'_, U>, mut _offset: usize) -> Result<()> {
|
fn store<U>(&self, _memory: &mut MemoryMut<'_, U>, mut _offset: usize) -> Result<()> {
|
||||||
debug_assert!(_offset % (Self::ALIGN32 as usize) == 0);
|
debug_assert!(_offset % (Self::ALIGN32 as usize) == 0);
|
||||||
let ($($t,)*) = self;
|
let ($($t,)*) = self;
|
||||||
$($t.store(_memory, next_field::<$t>(&mut _offset))?;)*
|
$($t.store(_memory, $t::ABI.next_field32_size(&mut _offset))?;)*
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1891,7 +1855,7 @@ macro_rules! impl_component_ty_for_tuples {
|
|||||||
fn load(_memory: &Memory<'_>, bytes: &[u8]) -> Result<Self> {
|
fn load(_memory: &Memory<'_>, bytes: &[u8]) -> Result<Self> {
|
||||||
debug_assert!((bytes.as_ptr() as usize) % (Self::ALIGN32 as usize) == 0);
|
debug_assert!((bytes.as_ptr() as usize) % (Self::ALIGN32 as usize) == 0);
|
||||||
let mut _offset = 0;
|
let mut _offset = 0;
|
||||||
$(let $t = $t::load(_memory, &bytes[next_field::<$t>(&mut _offset)..][..$t::SIZE32])?;)*
|
$(let $t = $t::load(_memory, &bytes[$t::ABI.next_field32_size(&mut _offset)..][..$t::SIZE32])?;)*
|
||||||
Ok(($($t,)*))
|
Ok(($($t,)*))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,14 +28,14 @@ pub use wasmtime_component_macro::{flags, ComponentType, Lift, Lower};
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub mod __internal {
|
pub mod __internal {
|
||||||
pub use super::func::{
|
pub use super::func::{
|
||||||
align_to, format_flags, next_field, typecheck_enum, typecheck_flags, typecheck_record,
|
format_flags, typecheck_enum, typecheck_flags, typecheck_record, typecheck_union,
|
||||||
typecheck_union, typecheck_variant, MaybeUninitExt, Memory, MemoryMut, Options,
|
typecheck_variant, ComponentVariant, MaybeUninitExt, Memory, MemoryMut, Options,
|
||||||
};
|
};
|
||||||
pub use crate::map_maybe_uninit;
|
pub use crate::map_maybe_uninit;
|
||||||
pub use crate::store::StoreOpaque;
|
pub use crate::store::StoreOpaque;
|
||||||
pub use anyhow;
|
pub use anyhow;
|
||||||
pub use wasmtime_environ;
|
pub use wasmtime_environ;
|
||||||
pub use wasmtime_environ::component::{ComponentTypes, InterfaceType};
|
pub use wasmtime_environ::component::{CanonicalAbiInfo, ComponentTypes, InterfaceType};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) use self::store::ComponentStoreData;
|
pub(crate) use self::store::ComponentStoreData;
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
//! This module defines the `Type` type, representing the dynamic form of a component interface type.
|
//! This module defines the `Type` type, representing the dynamic form of a component interface type.
|
||||||
|
|
||||||
use crate::component::func;
|
|
||||||
use crate::component::values::{self, Val};
|
use crate::component::values::{self, Val};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use wasmtime_component_util::{DiscriminantSize, FlagsSize};
|
|
||||||
use wasmtime_environ::component::{
|
use wasmtime_environ::component::{
|
||||||
ComponentTypes, InterfaceType, TypeEnumIndex, TypeExpectedIndex, TypeFlagsIndex,
|
CanonicalAbiInfo, ComponentTypes, InterfaceType, TypeEnumIndex, TypeExpectedIndex,
|
||||||
TypeInterfaceIndex, TypeRecordIndex, TypeTupleIndex, TypeUnionIndex, TypeVariantIndex,
|
TypeFlagsIndex, TypeInterfaceIndex, TypeOptionIndex, TypeRecordIndex, TypeTupleIndex,
|
||||||
|
TypeUnionIndex, TypeVariantIndex, VariantInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -125,6 +124,10 @@ impl Variant {
|
|||||||
ty: Type::from(&case.ty, &self.0.types),
|
ty: Type::from(&case.ty, &self.0.types),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn variant_info(&self) -> &VariantInfo {
|
||||||
|
&self.0.types[self.0.index].info
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An `enum` interface type
|
/// An `enum` interface type
|
||||||
@@ -144,6 +147,10 @@ impl Enum {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|name| name.deref())
|
.map(|name| name.deref())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn variant_info(&self) -> &VariantInfo {
|
||||||
|
&self.0.types[self.0.index].info
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A `union` interface type
|
/// A `union` interface type
|
||||||
@@ -163,11 +170,15 @@ impl Union {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|ty| Type::from(ty, &self.0.types))
|
.map(|ty| Type::from(ty, &self.0.types))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn variant_info(&self) -> &VariantInfo {
|
||||||
|
&self.0.types[self.0.index].info
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An `option` interface type
|
/// An `option` interface type
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct Option(Handle<TypeInterfaceIndex>);
|
pub struct Option(Handle<TypeOptionIndex>);
|
||||||
|
|
||||||
impl Option {
|
impl Option {
|
||||||
/// Instantiate this type with the specified `value`.
|
/// Instantiate this type with the specified `value`.
|
||||||
@@ -177,7 +188,11 @@ impl Option {
|
|||||||
|
|
||||||
/// Retrieve the type parameter for this `option`.
|
/// Retrieve the type parameter for this `option`.
|
||||||
pub fn ty(&self) -> Type {
|
pub fn ty(&self) -> Type {
|
||||||
Type::from(&self.0.types[self.0.index], &self.0.types)
|
Type::from(&self.0.types[self.0.index].ty, &self.0.types)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn variant_info(&self) -> &VariantInfo {
|
||||||
|
&self.0.types[self.0.index].info
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,6 +215,10 @@ impl Expected {
|
|||||||
pub fn err(&self) -> Type {
|
pub fn err(&self) -> Type {
|
||||||
Type::from(&self.0.types[self.0.index].err, &self.0.types)
|
Type::from(&self.0.types[self.0.index].err, &self.0.types)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn variant_info(&self) -> &VariantInfo {
|
||||||
|
&self.0.types[self.0.index].info
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A `flags` interface type
|
/// A `flags` interface type
|
||||||
@@ -221,13 +240,6 @@ impl Flags {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents the size and alignment requirements of the heap-serialized form of a type
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) struct SizeAndAlignment {
|
|
||||||
pub(crate) size: usize,
|
|
||||||
pub(crate) alignment: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents a component model interface type
|
/// Represents a component model interface type
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
@@ -554,119 +566,22 @@ impl Type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the size and alignment requirements for the specified type.
|
/// Calculate the size and alignment requirements for the specified type.
|
||||||
pub(crate) fn size_and_alignment(&self) -> SizeAndAlignment {
|
pub(crate) fn canonical_abi(&self) -> &CanonicalAbiInfo {
|
||||||
match self {
|
match self {
|
||||||
Type::Unit => SizeAndAlignment {
|
Type::Unit => &CanonicalAbiInfo::ZERO,
|
||||||
size: 0,
|
Type::Bool | Type::S8 | Type::U8 => &CanonicalAbiInfo::SCALAR1,
|
||||||
alignment: 1,
|
Type::S16 | Type::U16 => &CanonicalAbiInfo::SCALAR2,
|
||||||
},
|
Type::S32 | Type::U32 | Type::Char | Type::Float32 => &CanonicalAbiInfo::SCALAR4,
|
||||||
|
Type::S64 | Type::U64 | Type::Float64 => &CanonicalAbiInfo::SCALAR8,
|
||||||
Type::Bool | Type::S8 | Type::U8 => SizeAndAlignment {
|
Type::String | Type::List(_) => &CanonicalAbiInfo::POINTER_PAIR,
|
||||||
size: 1,
|
Type::Record(handle) => &handle.0.types[handle.0.index].abi,
|
||||||
alignment: 1,
|
Type::Tuple(handle) => &handle.0.types[handle.0.index].abi,
|
||||||
},
|
Type::Variant(handle) => &handle.0.types[handle.0.index].abi,
|
||||||
|
Type::Enum(handle) => &handle.0.types[handle.0.index].abi,
|
||||||
Type::S16 | Type::U16 => SizeAndAlignment {
|
Type::Union(handle) => &handle.0.types[handle.0.index].abi,
|
||||||
size: 2,
|
Type::Option(handle) => &handle.0.types[handle.0.index].abi,
|
||||||
alignment: 2,
|
Type::Expected(handle) => &handle.0.types[handle.0.index].abi,
|
||||||
},
|
Type::Flags(handle) => &handle.0.types[handle.0.index].abi,
|
||||||
|
|
||||||
Type::S32 | Type::U32 | Type::Char | Type::Float32 => SizeAndAlignment {
|
|
||||||
size: 4,
|
|
||||||
alignment: 4,
|
|
||||||
},
|
|
||||||
|
|
||||||
Type::S64 | Type::U64 | Type::Float64 => SizeAndAlignment {
|
|
||||||
size: 8,
|
|
||||||
alignment: 8,
|
|
||||||
},
|
|
||||||
|
|
||||||
Type::String | Type::List(_) => SizeAndAlignment {
|
|
||||||
size: 8,
|
|
||||||
alignment: 4,
|
|
||||||
},
|
|
||||||
|
|
||||||
Type::Record(handle) => {
|
|
||||||
record_size_and_alignment(handle.fields().map(|field| field.ty))
|
|
||||||
}
|
|
||||||
|
|
||||||
Type::Tuple(handle) => record_size_and_alignment(handle.types()),
|
|
||||||
|
|
||||||
Type::Variant(handle) => variant_size_and_alignment(handle.cases().map(|case| case.ty)),
|
|
||||||
|
|
||||||
Type::Enum(handle) => variant_size_and_alignment(handle.names().map(|_| Type::Unit)),
|
|
||||||
|
|
||||||
Type::Union(handle) => variant_size_and_alignment(handle.types()),
|
|
||||||
|
|
||||||
Type::Option(handle) => {
|
|
||||||
variant_size_and_alignment([Type::Unit, handle.ty()].into_iter())
|
|
||||||
}
|
|
||||||
|
|
||||||
Type::Expected(handle) => {
|
|
||||||
variant_size_and_alignment([handle.ok(), handle.err()].into_iter())
|
|
||||||
}
|
|
||||||
|
|
||||||
Type::Flags(handle) => match FlagsSize::from_count(handle.names().len()) {
|
|
||||||
FlagsSize::Size0 => SizeAndAlignment {
|
|
||||||
size: 0,
|
|
||||||
alignment: 1,
|
|
||||||
},
|
|
||||||
FlagsSize::Size1 => SizeAndAlignment {
|
|
||||||
size: 1,
|
|
||||||
alignment: 1,
|
|
||||||
},
|
|
||||||
FlagsSize::Size2 => SizeAndAlignment {
|
|
||||||
size: 2,
|
|
||||||
alignment: 2,
|
|
||||||
},
|
|
||||||
FlagsSize::Size4Plus(n) => SizeAndAlignment {
|
|
||||||
size: n * 4,
|
|
||||||
alignment: 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the aligned offset of a field of this type, updating `offset` to point to just after that field.
|
|
||||||
pub(crate) fn next_field(&self, offset: &mut usize) -> usize {
|
|
||||||
let SizeAndAlignment { size, alignment } = self.size_and_alignment();
|
|
||||||
*offset = func::align_to(*offset, alignment);
|
|
||||||
let result = *offset;
|
|
||||||
*offset += size;
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn record_size_and_alignment(types: impl Iterator<Item = Type>) -> SizeAndAlignment {
|
|
||||||
let mut offset = 0;
|
|
||||||
let mut align = 1;
|
|
||||||
for ty in types {
|
|
||||||
let SizeAndAlignment { size, alignment } = ty.size_and_alignment();
|
|
||||||
offset = func::align_to(offset, alignment) + size;
|
|
||||||
align = align.max(alignment);
|
|
||||||
}
|
|
||||||
|
|
||||||
SizeAndAlignment {
|
|
||||||
size: func::align_to(offset, align),
|
|
||||||
alignment: align,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn variant_size_and_alignment(types: impl ExactSizeIterator<Item = Type>) -> SizeAndAlignment {
|
|
||||||
let discriminant_size = DiscriminantSize::from_count(types.len()).unwrap();
|
|
||||||
let mut alignment = u32::from(discriminant_size);
|
|
||||||
let mut size = 0;
|
|
||||||
for ty in types {
|
|
||||||
let size_and_alignment = ty.size_and_alignment();
|
|
||||||
alignment = alignment.max(size_and_alignment.alignment);
|
|
||||||
size = size.max(size_and_alignment.size);
|
|
||||||
}
|
|
||||||
|
|
||||||
SizeAndAlignment {
|
|
||||||
size: func::align_to(
|
|
||||||
func::align_to(usize::from(discriminant_size), alignment) + size,
|
|
||||||
alignment,
|
|
||||||
),
|
|
||||||
alignment,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::component::func::{self, Lift, Lower, Memory, MemoryMut, Options};
|
use crate::component::func::{Lift, Lower, Memory, MemoryMut, Options};
|
||||||
use crate::component::types::{self, SizeAndAlignment, Type};
|
use crate::component::types::{self, Type};
|
||||||
use crate::store::StoreOpaque;
|
use crate::store::StoreOpaque;
|
||||||
use crate::{AsContextMut, StoreContextMut, ValRaw};
|
use crate::{AsContextMut, StoreContextMut, ValRaw};
|
||||||
use anyhow::{anyhow, bail, Context, Error, Result};
|
use anyhow::{anyhow, bail, Context, Error, Result};
|
||||||
@@ -9,6 +9,7 @@ use std::iter;
|
|||||||
use std::mem::MaybeUninit;
|
use std::mem::MaybeUninit;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use wasmtime_component_util::{DiscriminantSize, FlagsSize};
|
use wasmtime_component_util::{DiscriminantSize, FlagsSize};
|
||||||
|
use wasmtime_environ::component::VariantInfo;
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone)]
|
#[derive(PartialEq, Eq, Clone)]
|
||||||
pub struct List {
|
pub struct List {
|
||||||
@@ -700,8 +701,12 @@ impl Val {
|
|||||||
values: load_record(handle.types(), mem, bytes)?,
|
values: load_record(handle.types(), mem, bytes)?,
|
||||||
}),
|
}),
|
||||||
Type::Variant(handle) => {
|
Type::Variant(handle) => {
|
||||||
let (discriminant, value) =
|
let (discriminant, value) = load_variant(
|
||||||
load_variant(ty, handle.cases().map(|case| case.ty), mem, bytes)?;
|
handle.variant_info(),
|
||||||
|
handle.cases().map(|case| case.ty),
|
||||||
|
mem,
|
||||||
|
bytes,
|
||||||
|
)?;
|
||||||
|
|
||||||
Val::Variant(Variant {
|
Val::Variant(Variant {
|
||||||
ty: handle.clone(),
|
ty: handle.clone(),
|
||||||
@@ -710,8 +715,12 @@ impl Val {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
Type::Enum(handle) => {
|
Type::Enum(handle) => {
|
||||||
let (discriminant, _) =
|
let (discriminant, _) = load_variant(
|
||||||
load_variant(ty, handle.names().map(|_| Type::Unit), mem, bytes)?;
|
handle.variant_info(),
|
||||||
|
handle.names().map(|_| Type::Unit),
|
||||||
|
mem,
|
||||||
|
bytes,
|
||||||
|
)?;
|
||||||
|
|
||||||
Val::Enum(Enum {
|
Val::Enum(Enum {
|
||||||
ty: handle.clone(),
|
ty: handle.clone(),
|
||||||
@@ -719,7 +728,8 @@ impl Val {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
Type::Union(handle) => {
|
Type::Union(handle) => {
|
||||||
let (discriminant, value) = load_variant(ty, handle.types(), mem, bytes)?;
|
let (discriminant, value) =
|
||||||
|
load_variant(handle.variant_info(), handle.types(), mem, bytes)?;
|
||||||
|
|
||||||
Val::Union(Union {
|
Val::Union(Union {
|
||||||
ty: handle.clone(),
|
ty: handle.clone(),
|
||||||
@@ -728,8 +738,12 @@ impl Val {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
Type::Option(handle) => {
|
Type::Option(handle) => {
|
||||||
let (discriminant, value) =
|
let (discriminant, value) = load_variant(
|
||||||
load_variant(ty, [Type::Unit, handle.ty()].into_iter(), mem, bytes)?;
|
handle.variant_info(),
|
||||||
|
[Type::Unit, handle.ty()].into_iter(),
|
||||||
|
mem,
|
||||||
|
bytes,
|
||||||
|
)?;
|
||||||
|
|
||||||
Val::Option(Option {
|
Val::Option(Option {
|
||||||
ty: handle.clone(),
|
ty: handle.clone(),
|
||||||
@@ -738,8 +752,12 @@ impl Val {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
Type::Expected(handle) => {
|
Type::Expected(handle) => {
|
||||||
let (discriminant, value) =
|
let (discriminant, value) = load_variant(
|
||||||
load_variant(ty, [handle.ok(), handle.err()].into_iter(), mem, bytes)?;
|
handle.variant_info(),
|
||||||
|
[handle.ok(), handle.err()].into_iter(),
|
||||||
|
mem,
|
||||||
|
bytes,
|
||||||
|
)?;
|
||||||
|
|
||||||
Val::Expected(Expected {
|
Val::Expected(Expected {
|
||||||
ty: handle.clone(),
|
ty: handle.clone(),
|
||||||
@@ -845,7 +863,7 @@ impl Val {
|
|||||||
|
|
||||||
/// Serialize this value to the heap at the specified memory location.
|
/// Serialize this value to the heap at the specified memory location.
|
||||||
pub(crate) fn store<T>(&self, mem: &mut MemoryMut<'_, T>, offset: usize) -> Result<()> {
|
pub(crate) fn store<T>(&self, mem: &mut MemoryMut<'_, T>, offset: usize) -> Result<()> {
|
||||||
debug_assert!(offset % usize::try_from(self.ty().size_and_alignment().alignment)? == 0);
|
debug_assert!(offset % usize::try_from(self.ty().canonical_abi().align32)? == 0);
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Val::Unit => (),
|
Val::Unit => (),
|
||||||
@@ -871,35 +889,39 @@ impl Val {
|
|||||||
Val::Record(Record { values, .. }) | Val::Tuple(Tuple { values, .. }) => {
|
Val::Record(Record { values, .. }) | Val::Tuple(Tuple { values, .. }) => {
|
||||||
let mut offset = offset;
|
let mut offset = offset;
|
||||||
for value in values.deref() {
|
for value in values.deref() {
|
||||||
value.store(mem, value.ty().next_field(&mut offset))?;
|
value.store(
|
||||||
|
mem,
|
||||||
|
value.ty().canonical_abi().next_field32_size(&mut offset),
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Val::Variant(Variant {
|
Val::Variant(Variant {
|
||||||
discriminant,
|
discriminant,
|
||||||
value,
|
value,
|
||||||
ty,
|
ty,
|
||||||
}) => self.store_variant(*discriminant, value, ty.cases().len(), mem, offset)?,
|
}) => self.store_variant(*discriminant, value, ty.variant_info(), mem, offset)?,
|
||||||
|
|
||||||
Val::Enum(Enum { discriminant, ty }) => {
|
Val::Enum(Enum { discriminant, ty }) => {
|
||||||
self.store_variant(*discriminant, &Val::Unit, ty.names().len(), mem, offset)?
|
self.store_variant(*discriminant, &Val::Unit, ty.variant_info(), mem, offset)?
|
||||||
}
|
}
|
||||||
|
|
||||||
Val::Union(Union {
|
Val::Union(Union {
|
||||||
discriminant,
|
discriminant,
|
||||||
value,
|
value,
|
||||||
ty,
|
ty,
|
||||||
}) => self.store_variant(*discriminant, value, ty.types().len(), mem, offset)?,
|
}) => self.store_variant(*discriminant, value, ty.variant_info(), mem, offset)?,
|
||||||
|
|
||||||
Val::Option(Option {
|
Val::Option(Option {
|
||||||
discriminant,
|
discriminant,
|
||||||
value,
|
value,
|
||||||
..
|
ty,
|
||||||
})
|
}) => self.store_variant(*discriminant, value, ty.variant_info(), mem, offset)?,
|
||||||
| Val::Expected(Expected {
|
|
||||||
|
Val::Expected(Expected {
|
||||||
discriminant,
|
discriminant,
|
||||||
value,
|
value,
|
||||||
..
|
ty,
|
||||||
}) => self.store_variant(*discriminant, value, 2, mem, offset)?,
|
}) => self.store_variant(*discriminant, value, ty.variant_info(), mem, offset)?,
|
||||||
|
|
||||||
Val::Flags(Flags { count, value, .. }) => {
|
Val::Flags(Flags { count, value, .. }) => {
|
||||||
match FlagsSize::from_count(*count as usize) {
|
match FlagsSize::from_count(*count as usize) {
|
||||||
@@ -924,34 +946,26 @@ impl Val {
|
|||||||
&self,
|
&self,
|
||||||
discriminant: u32,
|
discriminant: u32,
|
||||||
value: &Val,
|
value: &Val,
|
||||||
case_count: usize,
|
info: &VariantInfo,
|
||||||
mem: &mut MemoryMut<'_, T>,
|
mem: &mut MemoryMut<'_, T>,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let discriminant_size = DiscriminantSize::from_count(case_count).unwrap();
|
match info.size {
|
||||||
match discriminant_size {
|
|
||||||
DiscriminantSize::Size1 => u8::try_from(discriminant).unwrap().store(mem, offset)?,
|
DiscriminantSize::Size1 => u8::try_from(discriminant).unwrap().store(mem, offset)?,
|
||||||
DiscriminantSize::Size2 => u16::try_from(discriminant).unwrap().store(mem, offset)?,
|
DiscriminantSize::Size2 => u16::try_from(discriminant).unwrap().store(mem, offset)?,
|
||||||
DiscriminantSize::Size4 => (discriminant).store(mem, offset)?,
|
DiscriminantSize::Size4 => discriminant.store(mem, offset)?,
|
||||||
}
|
}
|
||||||
|
|
||||||
value.store(
|
let offset = offset + usize::try_from(info.payload_offset32).unwrap();
|
||||||
mem,
|
value.store(mem, offset)
|
||||||
offset
|
|
||||||
+ func::align_to(
|
|
||||||
discriminant_size.into(),
|
|
||||||
self.ty().size_and_alignment().alignment,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_list(handle: &types::List, mem: &Memory, ptr: usize, len: usize) -> Result<Val> {
|
fn load_list(handle: &types::List, mem: &Memory, ptr: usize, len: usize) -> Result<Val> {
|
||||||
let element_type = handle.ty();
|
let element_type = handle.ty();
|
||||||
let SizeAndAlignment {
|
let abi = element_type.canonical_abi();
|
||||||
size: element_size,
|
let element_size = usize::try_from(abi.size32).unwrap();
|
||||||
alignment: element_alignment,
|
let element_alignment = abi.align32;
|
||||||
} = element_type.size_and_alignment();
|
|
||||||
|
|
||||||
match len
|
match len
|
||||||
.checked_mul(element_size)
|
.checked_mul(element_size)
|
||||||
@@ -986,25 +1000,24 @@ fn load_record(
|
|||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
types
|
types
|
||||||
.map(|ty| {
|
.map(|ty| {
|
||||||
Val::load(
|
let abi = ty.canonical_abi();
|
||||||
&ty,
|
let offset = abi.next_field32(&mut offset);
|
||||||
mem,
|
let offset = usize::try_from(offset).unwrap();
|
||||||
&bytes[ty.next_field(&mut offset)..][..ty.size_and_alignment().size],
|
let size = usize::try_from(abi.size32).unwrap();
|
||||||
)
|
Val::load(&ty, mem, &bytes[offset..][..size])
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_variant(
|
fn load_variant(
|
||||||
ty: &Type,
|
info: &VariantInfo,
|
||||||
mut types: impl ExactSizeIterator<Item = Type>,
|
mut types: impl ExactSizeIterator<Item = Type>,
|
||||||
mem: &Memory,
|
mem: &Memory,
|
||||||
bytes: &[u8],
|
bytes: &[u8],
|
||||||
) -> Result<(u32, Val)> {
|
) -> Result<(u32, Val)> {
|
||||||
let discriminant_size = DiscriminantSize::from_count(types.len()).unwrap();
|
let discriminant = match info.size {
|
||||||
let discriminant = match discriminant_size {
|
DiscriminantSize::Size1 => u32::from(u8::load(mem, &bytes[..1])?),
|
||||||
DiscriminantSize::Size1 => u8::load(mem, &bytes[..1])? as u32,
|
DiscriminantSize::Size2 => u32::from(u16::load(mem, &bytes[..2])?),
|
||||||
DiscriminantSize::Size2 => u16::load(mem, &bytes[..2])? as u32,
|
|
||||||
DiscriminantSize::Size4 => u32::load(mem, &bytes[..4])?,
|
DiscriminantSize::Size4 => u32::load(mem, &bytes[..4])?,
|
||||||
};
|
};
|
||||||
let case_ty = types.nth(discriminant as usize).ok_or_else(|| {
|
let case_ty = types.nth(discriminant as usize).ok_or_else(|| {
|
||||||
@@ -1014,14 +1027,9 @@ fn load_variant(
|
|||||||
types.len()
|
types.len()
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let value = Val::load(
|
let payload_offset = usize::try_from(info.payload_offset32).unwrap();
|
||||||
&case_ty,
|
let case_size = usize::try_from(case_ty.canonical_abi().size32).unwrap();
|
||||||
mem,
|
let value = Val::load(&case_ty, mem, &bytes[payload_offset..][..case_size])?;
|
||||||
&bytes[func::align_to(
|
|
||||||
usize::from(discriminant_size),
|
|
||||||
ty.size_and_alignment().alignment,
|
|
||||||
)..][..case_ty.size_and_alignment().size],
|
|
||||||
)?;
|
|
||||||
Ok((discriminant, value))
|
Ok((discriminant, value))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1050,19 +1058,18 @@ fn lower_list<T>(
|
|||||||
mem: &mut MemoryMut<'_, T>,
|
mem: &mut MemoryMut<'_, T>,
|
||||||
items: &[Val],
|
items: &[Val],
|
||||||
) -> Result<(usize, usize)> {
|
) -> Result<(usize, usize)> {
|
||||||
let SizeAndAlignment {
|
let abi = element_type.canonical_abi();
|
||||||
size: element_size,
|
let elt_size = usize::try_from(abi.size32)?;
|
||||||
alignment: element_alignment,
|
let elt_align = abi.align32;
|
||||||
} = element_type.size_and_alignment();
|
|
||||||
let size = items
|
let size = items
|
||||||
.len()
|
.len()
|
||||||
.checked_mul(element_size)
|
.checked_mul(elt_size)
|
||||||
.ok_or_else(|| anyhow::anyhow!("size overflow copying a list"))?;
|
.ok_or_else(|| anyhow::anyhow!("size overflow copying a list"))?;
|
||||||
let ptr = mem.realloc(0, 0, element_alignment, size)?;
|
let ptr = mem.realloc(0, 0, elt_align, size)?;
|
||||||
let mut element_ptr = ptr;
|
let mut element_ptr = ptr;
|
||||||
for item in items {
|
for item in items {
|
||||||
item.store(mem, element_ptr)?;
|
item.store(mem, element_ptr)?;
|
||||||
element_ptr += element_size;
|
element_ptr += elt_size;
|
||||||
}
|
}
|
||||||
Ok((ptr, items.len()))
|
Ok((ptr, items.len()))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user