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:
@@ -11,6 +11,7 @@ use std::ops::Index;
|
||||
use wasmparser::{
|
||||
ComponentAlias, ComponentOuterAliasKind, ComponentTypeDeclaration, InstanceTypeDeclaration,
|
||||
};
|
||||
use wasmtime_component_util::{DiscriminantSize, FlagsSize};
|
||||
|
||||
macro_rules! indices {
|
||||
($(
|
||||
@@ -89,6 +90,9 @@ indices! {
|
||||
pub struct TypeEnumIndex(u32);
|
||||
/// Index pointing to a union type in the component model.
|
||||
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
|
||||
/// `Result<T, E>`)
|
||||
pub struct TypeExpectedIndex(u32);
|
||||
@@ -209,6 +213,7 @@ pub struct ComponentTypes {
|
||||
enums: PrimaryMap<TypeEnumIndex, TypeEnum>,
|
||||
flags: PrimaryMap<TypeFlagsIndex, TypeFlags>,
|
||||
unions: PrimaryMap<TypeUnionIndex, TypeUnion>,
|
||||
options: PrimaryMap<TypeOptionIndex, TypeOption>,
|
||||
expecteds: PrimaryMap<TypeExpectedIndex, TypeExpected>,
|
||||
|
||||
module_types: ModuleTypes,
|
||||
@@ -219,6 +224,39 @@ impl ComponentTypes {
|
||||
pub fn module_types(&self) -> &ModuleTypes {
|
||||
&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 {
|
||||
@@ -244,6 +282,7 @@ impl_index! {
|
||||
impl Index<TypeEnumIndex> for ComponentTypes { TypeEnum => enums }
|
||||
impl Index<TypeFlagsIndex> for ComponentTypes { TypeFlags => flags }
|
||||
impl Index<TypeUnionIndex> for ComponentTypes { TypeUnion => unions }
|
||||
impl Index<TypeOptionIndex> for ComponentTypes { TypeOption => options }
|
||||
impl Index<TypeExpectedIndex> for ComponentTypes { TypeExpected => expecteds }
|
||||
}
|
||||
|
||||
@@ -274,6 +313,7 @@ pub struct ComponentTypesBuilder {
|
||||
enums: HashMap<TypeEnum, TypeEnumIndex>,
|
||||
flags: HashMap<TypeFlags, TypeFlagsIndex>,
|
||||
unions: HashMap<TypeUnion, TypeUnionIndex>,
|
||||
options: HashMap<TypeOption, TypeOptionIndex>,
|
||||
expecteds: HashMap<TypeExpected, TypeExpectedIndex>,
|
||||
|
||||
component_types: ComponentTypes,
|
||||
@@ -599,8 +639,7 @@ impl ComponentTypesBuilder {
|
||||
wasmparser::ComponentDefinedType::Enum(e) => InterfaceType::Enum(self.enum_type(e)),
|
||||
wasmparser::ComponentDefinedType::Union(e) => InterfaceType::Union(self.union_type(e)),
|
||||
wasmparser::ComponentDefinedType::Option(e) => {
|
||||
let ty = self.valtype(e);
|
||||
InterfaceType::Option(self.add_interface_type(ty))
|
||||
InterfaceType::Option(self.option_type(e))
|
||||
}
|
||||
wasmparser::ComponentDefinedType::Expected { ok, error } => {
|
||||
InterfaceType::Expected(self.expected_type(ok, error))
|
||||
@@ -623,62 +662,90 @@ impl ComponentTypesBuilder {
|
||||
}
|
||||
|
||||
fn record_type(&mut self, record: &[(&str, wasmparser::ComponentValType)]) -> TypeRecordIndex {
|
||||
let record = TypeRecord {
|
||||
fields: record
|
||||
let fields = record
|
||||
.iter()
|
||||
.map(|(name, ty)| RecordField {
|
||||
name: name.to_string(),
|
||||
ty: self.valtype(ty),
|
||||
})
|
||||
.collect::<Box<[_]>>();
|
||||
let abi = CanonicalAbiInfo::record(
|
||||
fields
|
||||
.iter()
|
||||
.map(|(name, ty)| RecordField {
|
||||
name: name.to_string(),
|
||||
ty: self.valtype(ty),
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
self.add_record_type(record)
|
||||
.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 {
|
||||
let variant = TypeVariant {
|
||||
cases: cases
|
||||
let cases = cases
|
||||
.iter()
|
||||
.map(|case| {
|
||||
// FIXME: need to implement `refines`, not sure what that
|
||||
// is at this time.
|
||||
assert!(case.refines.is_none());
|
||||
VariantCase {
|
||||
name: case.name.to_string(),
|
||||
ty: self.valtype(&case.ty),
|
||||
}
|
||||
})
|
||||
.collect::<Box<[_]>>();
|
||||
let (info, abi) = VariantInfo::new(
|
||||
cases
|
||||
.iter()
|
||||
.map(|case| {
|
||||
// FIXME: need to implement `refines`, not sure what that
|
||||
// is at this time.
|
||||
assert!(case.refines.is_none());
|
||||
VariantCase {
|
||||
name: case.name.to_string(),
|
||||
ty: self.valtype(&case.ty),
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
self.add_variant_type(variant)
|
||||
.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 {
|
||||
let tuple = TypeTuple {
|
||||
types: types.iter().map(|ty| self.valtype(ty)).collect(),
|
||||
};
|
||||
self.add_tuple_type(tuple)
|
||||
let types = types
|
||||
.iter()
|
||||
.map(|ty| self.valtype(ty))
|
||||
.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 {
|
||||
let flags = TypeFlags {
|
||||
names: flags.iter().map(|s| s.to_string()).collect(),
|
||||
abi: CanonicalAbiInfo::flags(flags.len()),
|
||||
};
|
||||
self.add_flags_type(flags)
|
||||
}
|
||||
|
||||
fn enum_type(&mut self, variants: &[&str]) -> TypeEnumIndex {
|
||||
let e = TypeEnum {
|
||||
names: variants.iter().map(|s| s.to_string()).collect(),
|
||||
};
|
||||
self.add_enum_type(e)
|
||||
let names = variants.iter().map(|s| s.to_string()).collect::<Box<[_]>>();
|
||||
let (info, abi) = VariantInfo::new(
|
||||
names
|
||||
.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 {
|
||||
let union = TypeUnion {
|
||||
types: types.iter().map(|ty| self.valtype(ty)).collect(),
|
||||
};
|
||||
self.add_union_type(union)
|
||||
let types = types
|
||||
.iter()
|
||||
.map(|ty| self.valtype(ty))
|
||||
.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(
|
||||
@@ -686,11 +753,13 @@ impl ComponentTypesBuilder {
|
||||
ok: &wasmparser::ComponentValType,
|
||||
err: &wasmparser::ComponentValType,
|
||||
) -> TypeExpectedIndex {
|
||||
let expected = TypeExpected {
|
||||
ok: self.valtype(ok),
|
||||
err: self.valtype(err),
|
||||
};
|
||||
self.add_expected_type(expected)
|
||||
let ok = self.valtype(ok);
|
||||
let err = self.valtype(err);
|
||||
let (info, abi) = VariantInfo::new([
|
||||
self.component_types.canonical_abi(&ok),
|
||||
self.component_types.canonical_abi(&err),
|
||||
]);
|
||||
self.add_expected_type(TypeExpected { ok, err, abi, info })
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn add_expected_type(&mut self, ty: TypeExpected) -> TypeExpectedIndex {
|
||||
intern(&mut self.expecteds, &mut self.component_types.expecteds, ty)
|
||||
@@ -875,7 +949,7 @@ pub enum InterfaceType {
|
||||
Flags(TypeFlagsIndex),
|
||||
Enum(TypeEnumIndex),
|
||||
Union(TypeUnionIndex),
|
||||
Option(TypeInterfaceIndex),
|
||||
Option(TypeOptionIndex),
|
||||
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.
|
||||
///
|
||||
/// This is equivalent to a `struct` in Rust.
|
||||
@@ -907,6 +1281,8 @@ impl From<&wasmparser::PrimitiveValType> for InterfaceType {
|
||||
pub struct TypeRecord {
|
||||
/// The fields that are contained within this struct type.
|
||||
pub fields: Box<[RecordField]>,
|
||||
/// Byte information about this type in the canonical ABI.
|
||||
pub abi: CanonicalAbiInfo,
|
||||
}
|
||||
|
||||
/// One field within a record.
|
||||
@@ -927,6 +1303,10 @@ pub struct RecordField {
|
||||
pub struct TypeVariant {
|
||||
/// The list of cases that this variant can take.
|
||||
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
|
||||
@@ -947,6 +1327,8 @@ pub struct VariantCase {
|
||||
pub struct TypeTuple {
|
||||
/// The types that are contained within this tuple.
|
||||
pub types: Box<[InterfaceType]>,
|
||||
/// Byte information about this type in the canonical ABI.
|
||||
pub abi: CanonicalAbiInfo,
|
||||
}
|
||||
|
||||
/// Shape of a "flags" type in interface types.
|
||||
@@ -957,6 +1339,8 @@ pub struct TypeTuple {
|
||||
pub struct TypeFlags {
|
||||
/// The names of all flags, all of which are unique.
|
||||
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
|
||||
@@ -968,6 +1352,10 @@ pub struct TypeFlags {
|
||||
pub struct TypeEnum {
|
||||
/// The names of this enum, all of which are unique.
|
||||
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.
|
||||
@@ -979,6 +1367,21 @@ pub struct TypeEnum {
|
||||
pub struct TypeUnion {
|
||||
/// The list of types this is a union over.
|
||||
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.
|
||||
@@ -988,4 +1391,8 @@ pub struct TypeExpected {
|
||||
pub ok: InterfaceType,
|
||||
/// The `E` in `Result<T, E>`
|
||||
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::fact::{AdapterOptions, Context, Options};
|
||||
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
|
||||
/// signature.
|
||||
@@ -23,11 +23,6 @@ pub struct Signature {
|
||||
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 {
|
||||
/// Calculates the core wasm function signature for the component function
|
||||
/// type specified within `Context`.
|
||||
@@ -120,15 +115,18 @@ impl ComponentTypes {
|
||||
}
|
||||
InterfaceType::Flags(f) => {
|
||||
let flags = &self[*f];
|
||||
let nflags = align_to(flags.names.len(), 32) / 32;
|
||||
for _ in 0..nflags {
|
||||
dst.push(ValType::I32);
|
||||
match FlagsSize::from_count(flags.names.len()) {
|
||||
FlagsSize::Size0 => {}
|
||||
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::Option(t) => {
|
||||
dst.push(ValType::I32);
|
||||
self.push_flat(opts, &self[*t], dst);
|
||||
self.push_flat(opts, &self[*t].ty, dst);
|
||||
}
|
||||
InterfaceType::Variant(t) => {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -194,85 +192,12 @@ impl ComponentTypes {
|
||||
//
|
||||
// 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.
|
||||
pub(super) fn size_align(&self, opts: &Options, ty: &InterfaceType) -> (usize, usize) {
|
||||
match ty {
|
||||
InterfaceType::Unit => (0, 1),
|
||||
InterfaceType::Bool | InterfaceType::S8 | InterfaceType::U8 => (1, 1),
|
||||
InterfaceType::S16 | InterfaceType::U16 => (2, 2),
|
||||
InterfaceType::S32
|
||||
| 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!(),
|
||||
pub(super) fn size_align(&self, opts: &Options, ty: &InterfaceType) -> (u32, u32) {
|
||||
let abi = self.canonical_abi(ty);
|
||||
if opts.memory64 {
|
||||
(abi.size64, abi.align64)
|
||||
} else {
|
||||
(abi.size32, abi.align32)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,12 @@
|
||||
//! can be somewhat arbitrary, an intentional decision.
|
||||
|
||||
use crate::component::{
|
||||
ComponentTypes, InterfaceType, StringEncoding, TypeEnumIndex, TypeExpectedIndex,
|
||||
TypeFlagsIndex, TypeInterfaceIndex, TypeRecordIndex, TypeTupleIndex, TypeUnionIndex,
|
||||
TypeVariantIndex, FLAG_MAY_ENTER, FLAG_MAY_LEAVE, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS,
|
||||
CanonicalAbiInfo, ComponentTypes, InterfaceType, StringEncoding, TypeEnumIndex,
|
||||
TypeExpectedIndex, TypeFlagsIndex, TypeInterfaceIndex, TypeOptionIndex, TypeRecordIndex,
|
||||
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::traps::Trap;
|
||||
use crate::fact::{AdapterData, Body, Context, Function, FunctionId, Module, Options};
|
||||
@@ -307,7 +308,12 @@ impl Compiler<'_, '_> {
|
||||
} else {
|
||||
// If there are too many parameters then space is allocated in the
|
||||
// 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);
|
||||
Destination::Memory(self.malloc(lift_opts, size, align))
|
||||
};
|
||||
@@ -1692,13 +1698,13 @@ impl Compiler<'_, '_> {
|
||||
// Update the two loop pointers
|
||||
if src_size > 0 {
|
||||
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.instruction(LocalSet(cur_src_ptr.idx));
|
||||
}
|
||||
if dst_size > 0 {
|
||||
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.instruction(LocalSet(cur_dst_ptr.idx));
|
||||
}
|
||||
@@ -1745,7 +1751,7 @@ impl Compiler<'_, '_> {
|
||||
&mut self,
|
||||
opts: &Options,
|
||||
len_local: u32,
|
||||
elt_size: usize,
|
||||
elt_size: u32,
|
||||
) -> TempLocal {
|
||||
// Zero-size types are easy to handle here because the byte size of the
|
||||
// destination is always zero.
|
||||
@@ -1810,7 +1816,7 @@ impl Compiler<'_, '_> {
|
||||
//
|
||||
// The result of the multiplication is saved into a local as well to
|
||||
// get the result afterwards.
|
||||
self.instruction(I64Const(u32::try_from(elt_size).unwrap().into()));
|
||||
self.instruction(I64Const(elt_size.into()));
|
||||
self.instruction(I64Mul);
|
||||
let tmp = self.local_tee_new_tmp(ValType::I64);
|
||||
// Branch to success if the upper 32-bits are zero, otherwise
|
||||
@@ -1983,8 +1989,8 @@ impl Compiler<'_, '_> {
|
||||
_ => panic!("expected a variant"),
|
||||
};
|
||||
|
||||
let src_info = VariantInfo::new(self.types, src.opts(), 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 src_info = variant_info(self.types, src_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 dst_i = dst_ty
|
||||
@@ -2018,8 +2024,8 @@ impl Compiler<'_, '_> {
|
||||
_ => panic!("expected an option"),
|
||||
};
|
||||
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 dst_info = VariantInfo::new(self.types, dst.opts(), dst_ty.types.iter().copied());
|
||||
let src_info = variant_info(self.types, src_ty.types.iter().copied());
|
||||
let dst_info = variant_info(self.types, dst_ty.types.iter().copied());
|
||||
|
||||
self.convert_variant(
|
||||
src,
|
||||
@@ -2055,16 +2061,8 @@ impl Compiler<'_, '_> {
|
||||
InterfaceType::Enum(t) => &self.types[*t],
|
||||
_ => panic!("expected an option"),
|
||||
};
|
||||
let src_info = VariantInfo::new(
|
||||
self.types,
|
||||
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 src_info = variant_info(self.types, src_ty.names.iter().map(|_| InterfaceType::Unit));
|
||||
let dst_info = variant_info(self.types, dst_ty.names.iter().map(|_| InterfaceType::Unit));
|
||||
|
||||
let unit = &InterfaceType::Unit;
|
||||
self.convert_variant(
|
||||
@@ -2088,19 +2086,19 @@ impl Compiler<'_, '_> {
|
||||
|
||||
fn translate_option(
|
||||
&mut self,
|
||||
src_ty: TypeInterfaceIndex,
|
||||
src_ty: TypeOptionIndex,
|
||||
src: &Source<'_>,
|
||||
dst_ty: &InterfaceType,
|
||||
dst: &Destination,
|
||||
) {
|
||||
let src_ty = &self.types[src_ty];
|
||||
let src_ty = &self.types[src_ty].ty;
|
||||
let dst_ty = match dst_ty {
|
||||
InterfaceType::Option(t) => &self.types[*t],
|
||||
InterfaceType::Option(t) => &self.types[*t].ty,
|
||||
_ => panic!("expected an option"),
|
||||
};
|
||||
|
||||
let src_info = VariantInfo::new(self.types, src.opts(), [InterfaceType::Unit, *src_ty]);
|
||||
let dst_info = VariantInfo::new(self.types, dst.opts(), [InterfaceType::Unit, *dst_ty]);
|
||||
let src_info = variant_info(self.types, [InterfaceType::Unit, *src_ty]);
|
||||
let dst_info = variant_info(self.types, [InterfaceType::Unit, *dst_ty]);
|
||||
|
||||
self.convert_variant(
|
||||
src,
|
||||
@@ -2138,8 +2136,8 @@ impl Compiler<'_, '_> {
|
||||
_ => panic!("expected an expected"),
|
||||
};
|
||||
|
||||
let src_info = VariantInfo::new(self.types, src.opts(), [src_ty.ok, src_ty.err]);
|
||||
let dst_info = VariantInfo::new(self.types, dst.opts(), [dst_ty.ok, dst_ty.err]);
|
||||
let src_info = variant_info(self.types, [src_ty.ok, src_ty.err]);
|
||||
let dst_info = variant_info(self.types, [dst_ty.ok, dst_ty.err]);
|
||||
|
||||
self.convert_variant(
|
||||
src,
|
||||
@@ -2316,7 +2314,7 @@ impl Compiler<'_, '_> {
|
||||
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
|
||||
// check can be omitted.
|
||||
if align == 1 {
|
||||
@@ -2324,7 +2322,7 @@ impl Compiler<'_, '_> {
|
||||
}
|
||||
self.instruction(LocalGet(addr_local));
|
||||
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_if(opts, BlockType::Empty);
|
||||
self.trap(Trap::UnalignedPointer);
|
||||
@@ -2343,20 +2341,20 @@ impl Compiler<'_, '_> {
|
||||
self.instruction(LocalGet(mem.addr.idx));
|
||||
self.ptr_uconst(mem.opts, mem.offset);
|
||||
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_if(mem.opts, BlockType::Empty);
|
||||
self.trap(Trap::AssertFailed("pointer not aligned"));
|
||||
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();
|
||||
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 {
|
||||
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)),
|
||||
}
|
||||
self.instruction(Call(realloc.as_u32()));
|
||||
@@ -2364,12 +2362,7 @@ impl Compiler<'_, '_> {
|
||||
self.memory_operand(opts, addr, align)
|
||||
}
|
||||
|
||||
fn memory_operand<'a>(
|
||||
&mut self,
|
||||
opts: &'a Options,
|
||||
addr: TempLocal,
|
||||
align: usize,
|
||||
) -> Memory<'a> {
|
||||
fn memory_operand<'a>(&mut self, opts: &'a Options, addr: TempLocal, align: u32) -> Memory<'a> {
|
||||
let ret = Memory {
|
||||
addr,
|
||||
offset: 0,
|
||||
@@ -2795,9 +2788,9 @@ impl<'a> Source<'a> {
|
||||
Source::Memory(mem)
|
||||
}
|
||||
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;
|
||||
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::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)
|
||||
}
|
||||
}
|
||||
@@ -2846,9 +2843,9 @@ impl<'a> Destination<'a> {
|
||||
Destination::Memory(mem)
|
||||
}
|
||||
Destination::Stack(s, opts) => {
|
||||
let cnt = types.flatten_types(opts, [ty]).len();
|
||||
let cnt = types.flatten_types(opts, [ty]).len() as u32;
|
||||
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::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)
|
||||
}
|
||||
}
|
||||
@@ -2881,38 +2882,18 @@ impl<'a> Destination<'a> {
|
||||
}
|
||||
|
||||
fn next_field_offset<'a>(
|
||||
offset: &mut usize,
|
||||
offset: &mut u32,
|
||||
types: &ComponentTypes,
|
||||
field: &InterfaceType,
|
||||
mem: &Memory<'a>,
|
||||
) -> Memory<'a> {
|
||||
let (size, align) = types.size_align(mem.opts, field);
|
||||
*offset = align_to(*offset, align) + size;
|
||||
mem.bump(*offset - size)
|
||||
}
|
||||
|
||||
struct VariantInfo {
|
||||
size: DiscriminantSize,
|
||||
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))
|
||||
}
|
||||
let abi = types.canonical_abi(field);
|
||||
let offset = if mem.opts.memory64 {
|
||||
abi.next_field64(offset)
|
||||
} else {
|
||||
abi.next_field32(offset)
|
||||
};
|
||||
mem.bump(offset)
|
||||
}
|
||||
|
||||
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 {
|
||||
opts: self.opts,
|
||||
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,
|
||||
}
|
||||
|
||||
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 {
|
||||
Const(usize),
|
||||
Const(u32),
|
||||
Local(u32),
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user