Upgrade wasm-tools crates, namely the component model (#4715)

* Upgrade wasm-tools crates, namely the component model

This commit pulls in the latest versions of all of the `wasm-tools`
family of crates. There were two major changes that happened in
`wasm-tools` in the meantime:

* bytecodealliance/wasm-tools#697 - this commit introduced a new API for
  more efficiently reading binary operators from a wasm binary. The old
  `Operator`-based reading was left in place, however, and continues to
  be what Wasmtime uses. I hope to update Wasmtime in a future PR to use
  this new API, but for now the biggest change is...

* bytecodealliance/wasm-tools#703 - this commit was a major update to
  the component model AST. This commit almost entirely deals with the
  fallout of this change.

The changes made to the component model were:

1. The `unit` type no longer exists. This was generally a simple change
   where the `Unit` case in a few different locations were all removed.
2. The `expected` type was renamed to `result`. This similarly was
   relatively lightweight and mostly just a renaming on the surface. I
   took this opportunity to rename `val::Result` to `val::ResultVal` and
   `types::Result` to `types::ResultType` to avoid clashing with the
   standard library types. The `Option`-based types were handled with
   this as well.
3. The payload type of `variant` and `result` types are now optional.
   This affected many locations that calculate flat type
   representations, ABI information, etc. The `#[derive(ComponentType)]`
   macro now specifically handles Rust-defined `enum` types which have
   no payload to the equivalent in the component model.
4. Functions can now return multiple parameters. This changed the
   signature of invoking component functions because the return value is
   now bound by `ComponentNamedList` (renamed from `ComponentParams`).
   This had a large effect in the tests, fuzz test case generation, etc.
5. Function types with 2-or-more parameters/results must uniquely name
   all parameters/results. This mostly affected the text format used
   throughout the tests.

I haven't added specifically new tests for multi-return but I changed a
number of tests to use it. Additionally I've updated the fuzzers to all
exercise multi-return as well so I think we should get some good
coverage with that.

* Update version numbers

* Use crates.io
This commit is contained in:
Alex Crichton
2022-08-17 11:17:34 -05:00
committed by GitHub
parent 3629bbbd55
commit 57dca934ad
46 changed files with 1425 additions and 1133 deletions

View File

@@ -14,7 +14,7 @@ edition = "2021"
anyhow = "1.0"
cranelift-entity = { path = "../../cranelift/entity", version = "0.88.0" }
wasmtime-types = { path = "../types", version = "0.41.0" }
wasmparser = "0.88.0"
wasmparser = "0.89.0"
indexmap = { version = "1.0.2", features = ["serde-1"] }
thiserror = "1.0.4"
serde = { version = "1.0.94", features = ["derive"] }
@@ -22,15 +22,15 @@ log = { version = "0.4.8", default-features = false }
gimli = { version = "0.26.0", default-features = false, features = ['read'] }
object = { version = "0.29.0", default-features = false, features = ['read_core', 'write_core', 'elf'] }
target-lexicon = "0.12"
wasm-encoder = { version = "0.15.0", optional = true }
wasmprinter = { version = "0.2.38", optional = true }
wasm-encoder = { version = "0.16.0", optional = true }
wasmprinter = { version = "0.2.39", optional = true }
wasmtime-component-util = { path = "../component-util", version = "=0.41.0", optional = true }
[dev-dependencies]
atty = "0.2.14"
clap = { version = "3.2.8", features = ['derive'] }
env_logger = "0.9.0"
wat = "1.0.47"
wat = "1.0.48"
[[example]]
name = "factc"

View File

@@ -12,8 +12,8 @@ cargo-fuzz = true
arbitrary = { version = "1.1.0", features = ["derive"] }
env_logger = "0.9.0"
libfuzzer-sys = "0.4"
wasmparser = "0.88.0"
wasmprinter = "0.2.37"
wasmparser = "0.89.0"
wasmprinter = "0.2.39"
wat = "1.0"
wasmtime-environ = { path = ".." }
component-fuzz-util = { path = "../../misc/component-fuzz-util", optional = true }

View File

@@ -79,11 +79,11 @@ fn target(module: GenAdapterModule) {
let wat = format!(
"(component
{types}
(type (func {params} {result}))
(type (func {params} {results}))
)",
types = wat_decls.types,
params = wat_decls.params,
result = wat_decls.result,
results = wat_decls.results,
);
let wasm = wat::parse_str(&wat).unwrap();

View File

@@ -94,9 +94,9 @@ indices! {
/// 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 result type in the component model (aka a
/// `Result<T, E>`)
pub struct TypeExpectedIndex(u32);
pub struct TypeResultIndex(u32);
// ========================================================================
// Index types used to identify modules and components during compilation.
@@ -215,7 +215,7 @@ pub struct ComponentTypes {
flags: PrimaryMap<TypeFlagsIndex, TypeFlags>,
unions: PrimaryMap<TypeUnionIndex, TypeUnion>,
options: PrimaryMap<TypeOptionIndex, TypeOption>,
expecteds: PrimaryMap<TypeExpectedIndex, TypeExpected>,
results: PrimaryMap<TypeResultIndex, TypeResult>,
module_types: ModuleTypes,
}
@@ -229,8 +229,6 @@ impl ComponentTypes {
/// 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
}
@@ -255,7 +253,7 @@ impl ComponentTypes {
InterfaceType::Enum(i) => &self[*i].abi,
InterfaceType::Union(i) => &self[*i].abi,
InterfaceType::Option(i) => &self[*i].abi,
InterfaceType::Expected(i) => &self[*i].abi,
InterfaceType::Result(i) => &self[*i].abi,
}
}
}
@@ -284,7 +282,7 @@ impl_index! {
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 }
impl Index<TypeResultIndex> for ComponentTypes { TypeResult => results }
}
// Additionally forward anything that can index `ModuleTypes` to `ModuleTypes`
@@ -315,7 +313,7 @@ pub struct ComponentTypesBuilder {
flags: HashMap<TypeFlags, TypeFlagsIndex>,
unions: HashMap<TypeUnion, TypeUnionIndex>,
options: HashMap<TypeOption, TypeOptionIndex>,
expecteds: HashMap<TypeExpected, TypeExpectedIndex>,
results: HashMap<TypeResult, TypeResultIndex>,
component_types: ComponentTypes,
module_types: ModuleTypesBuilder,
@@ -637,7 +635,11 @@ impl ComponentTypesBuilder {
.iter()
.map(|(name, ty)| (name.map(|s| s.to_string()), self.valtype(ty)))
.collect(),
result: self.valtype(&ty.result),
results: ty
.results
.iter()
.map(|(name, ty)| (name.map(|s| s.to_string()), self.valtype(ty)))
.collect(),
};
self.add_func_type(ty)
}
@@ -662,8 +664,8 @@ impl ComponentTypesBuilder {
wasmparser::ComponentDefinedType::Option(e) => {
InterfaceType::Option(self.option_type(e))
}
wasmparser::ComponentDefinedType::Expected { ok, error } => {
InterfaceType::Expected(self.expected_type(ok, error))
wasmparser::ComponentDefinedType::Result { ok, err } => {
InterfaceType::Result(self.result_type(ok, err))
}
}
}
@@ -707,15 +709,14 @@ impl ComponentTypesBuilder {
assert!(case.refines.is_none());
VariantCase {
name: case.name.to_string(),
ty: self.valtype(&case.ty),
ty: case.ty.as_ref().map(|ty| self.valtype(ty)),
}
})
.collect::<Box<[_]>>();
let (info, abi) = VariantInfo::new(
cases
.iter()
.map(|c| self.component_types.canonical_abi(&c.ty)),
);
let (info, abi) = VariantInfo::new(cases.iter().map(|c| {
c.ty.as_ref()
.map(|ty| self.component_types.canonical_abi(ty))
}));
self.add_variant_type(TypeVariant { cases, abi, info })
}
@@ -742,11 +743,7 @@ impl ComponentTypesBuilder {
fn enum_type(&mut self, variants: &[&str]) -> TypeEnumIndex {
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)),
);
let (info, abi) = VariantInfo::new(names.iter().map(|_| None));
self.add_enum_type(TypeEnum { names, abi, info })
}
@@ -755,32 +752,32 @@ impl ComponentTypesBuilder {
.iter()
.map(|ty| self.valtype(ty))
.collect::<Box<[_]>>();
let (info, abi) =
VariantInfo::new(types.iter().map(|t| self.component_types.canonical_abi(t)));
let (info, abi) = VariantInfo::new(
types
.iter()
.map(|t| Some(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),
]);
let (info, abi) = VariantInfo::new([None, Some(self.component_types.canonical_abi(&ty))]);
self.add_option_type(TypeOption { ty, abi, info })
}
fn expected_type(
fn result_type(
&mut self,
ok: &wasmparser::ComponentValType,
err: &wasmparser::ComponentValType,
) -> TypeExpectedIndex {
let ok = self.valtype(ok);
let err = self.valtype(err);
ok: &Option<wasmparser::ComponentValType>,
err: &Option<wasmparser::ComponentValType>,
) -> TypeResultIndex {
let ok = ok.as_ref().map(|ty| self.valtype(ty));
let err = err.as_ref().map(|ty| self.valtype(ty));
let (info, abi) = VariantInfo::new([
self.component_types.canonical_abi(&ok),
self.component_types.canonical_abi(&err),
ok.as_ref().map(|t| self.component_types.canonical_abi(t)),
err.as_ref().map(|t| self.component_types.canonical_abi(t)),
]);
self.add_expected_type(TypeExpected { ok, err, abi, info })
self.add_result_type(TypeResult { ok, err, abi, info })
}
/// Interns a new function type within this type information.
@@ -823,12 +820,12 @@ impl ComponentTypesBuilder {
intern_and_fill_flat_types!(self, options, ty)
}
/// Interns a new expected type within this type information.
pub fn add_expected_type(&mut self, ty: TypeExpected) -> TypeExpectedIndex {
intern_and_fill_flat_types!(self, expecteds, ty)
/// Interns a new result type within this type information.
pub fn add_result_type(&mut self, ty: TypeResult) -> TypeResultIndex {
intern_and_fill_flat_types!(self, results, ty)
}
/// Interns a new expected type within this type information.
/// Interns a new type within this type information.
pub fn add_interface_type(&mut self, ty: InterfaceType) -> TypeInterfaceIndex {
intern(
&mut self.interface_types,
@@ -849,7 +846,6 @@ impl ComponentTypesBuilder {
/// in the canonical abi.
pub fn flat_types(&self, ty: &InterfaceType) -> Option<FlatTypes<'_>> {
match ty {
InterfaceType::Unit => Some(FlatTypes::EMPTY),
InterfaceType::U8
| InterfaceType::S8
| InterfaceType::Bool
@@ -870,7 +866,7 @@ impl ComponentTypesBuilder {
InterfaceType::Enum(i) => self.flat.enums[*i].as_flat_types(),
InterfaceType::Union(i) => self.flat.unions[*i].as_flat_types(),
InterfaceType::Option(i) => self.flat.options[*i].as_flat_types(),
InterfaceType::Expected(i) => self.flat.expecteds[*i].as_flat_types(),
InterfaceType::Result(i) => self.flat.results[*i].as_flat_types(),
}
}
}
@@ -973,8 +969,8 @@ pub struct TypeFunc {
/// The list of optionally named parameters for this function, and their
/// types.
pub params: Box<[(Option<String>, InterfaceType)]>,
/// The return value of this function.
pub result: InterfaceType,
/// The return values of this function.
pub results: Box<[(Option<String>, InterfaceType)]>,
}
/// All possible interface types that values can have.
@@ -986,7 +982,6 @@ pub struct TypeFunc {
#[derive(Serialize, Deserialize, Copy, Clone, Hash, Eq, PartialEq, Debug)]
#[allow(missing_docs)]
pub enum InterfaceType {
Unit,
Bool,
S8,
U8,
@@ -1008,13 +1003,12 @@ pub enum InterfaceType {
Enum(TypeEnumIndex),
Union(TypeUnionIndex),
Option(TypeOptionIndex),
Expected(TypeExpectedIndex),
Result(TypeResultIndex),
}
impl From<&wasmparser::PrimitiveValType> for InterfaceType {
fn from(ty: &wasmparser::PrimitiveValType) -> InterfaceType {
match ty {
wasmparser::PrimitiveValType::Unit => InterfaceType::Unit,
wasmparser::PrimitiveValType::Bool => InterfaceType::Bool,
wasmparser::PrimitiveValType::S8 => InterfaceType::S8,
wasmparser::PrimitiveValType::U8 => InterfaceType::U8,
@@ -1080,7 +1074,7 @@ const fn max(a: u32, b: u32) -> u32 {
impl CanonicalAbiInfo {
/// ABI information for zero-sized types.
pub const ZERO: CanonicalAbiInfo = CanonicalAbiInfo {
const ZERO: CanonicalAbiInfo = CanonicalAbiInfo {
size32: 0,
align32: 1,
size64: 0,
@@ -1204,7 +1198,7 @@ impl CanonicalAbiInfo {
fn variant<'a, I>(cases: I) -> CanonicalAbiInfo
where
I: IntoIterator<Item = &'a CanonicalAbiInfo>,
I: IntoIterator<Item = Option<&'a CanonicalAbiInfo>>,
I::IntoIter: ExactSizeIterator,
{
// NB: this is basically a duplicate definition of
@@ -1218,11 +1212,13 @@ impl CanonicalAbiInfo {
let mut max_align64 = discrim_size;
let mut max_case_count = Some(0);
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);
max_case_count = max_flat(max_case_count, case.flat_count);
if let Some(case) = case {
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);
max_case_count = max_flat(max_case_count, case.flat_count);
}
}
CanonicalAbiInfo {
size32: align_to(
@@ -1240,7 +1236,7 @@ impl CanonicalAbiInfo {
}
/// Same as `CanonicalAbiInfo::variant` but `const`-safe
pub const fn variant_static(cases: &[CanonicalAbiInfo]) -> CanonicalAbiInfo {
pub const fn variant_static(cases: &[Option<CanonicalAbiInfo>]) -> CanonicalAbiInfo {
// NB: this is basically a duplicate definition of
// `CanonicalAbiInfo::variant`, these should be kept in sync.
@@ -1256,11 +1252,13 @@ impl CanonicalAbiInfo {
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);
max_case_count = max_flat(max_case_count, case.flat_count);
if let Some(case) = case {
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);
max_case_count = max_flat(max_case_count, case.flat_count);
}
i += 1;
}
CanonicalAbiInfo {
@@ -1309,7 +1307,7 @@ impl VariantInfo {
/// cases.
pub fn new<'a, I>(cases: I) -> (VariantInfo, CanonicalAbiInfo)
where
I: IntoIterator<Item = &'a CanonicalAbiInfo>,
I: IntoIterator<Item = Option<&'a CanonicalAbiInfo>>,
I::IntoIter: ExactSizeIterator,
{
let cases = cases.into_iter();
@@ -1325,7 +1323,7 @@ impl VariantInfo {
)
}
/// TODO
pub const fn new_static(cases: &[CanonicalAbiInfo]) -> VariantInfo {
pub const fn new_static(cases: &[Option<CanonicalAbiInfo>]) -> VariantInfo {
let size = match DiscriminantSize::from_count(cases.len()) {
Some(size) => size,
None => unreachable!(),
@@ -1404,8 +1402,8 @@ pub struct TypeVariant {
pub struct VariantCase {
/// Name of the variant, unique amongst all cases in a variant.
pub name: String,
/// Type associated with this payload, maybe `Unit`.
pub ty: InterfaceType,
/// Optional type associated with this payload.
pub ty: Option<InterfaceType>,
}
/// Shape of a "tuple" type in interface types.
@@ -1473,13 +1471,13 @@ pub struct TypeOption {
pub info: VariantInfo,
}
/// Shape of an "expected" interface type.
/// Shape of a "result" interface type.
#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
pub struct TypeExpected {
pub struct TypeResult {
/// The `T` in `Result<T, E>`
pub ok: InterfaceType,
pub ok: Option<InterfaceType>,
/// The `E` in `Result<T, E>`
pub err: InterfaceType,
pub err: Option<InterfaceType>,
/// Byte information about this type in the canonical ABI.
pub abi: CanonicalAbiInfo,
/// Byte information about this variant type.
@@ -1531,7 +1529,6 @@ pub struct FlatTypes<'a> {
#[allow(missing_docs)]
impl FlatTypes<'_> {
pub const EMPTY: FlatTypes<'static> = FlatTypes::new(&[]);
pub const I32: FlatTypes<'static> = FlatTypes::new(&[FlatType::I32]);
pub const I64: FlatTypes<'static> = FlatTypes::new(&[FlatType::I64]);
pub const F32: FlatTypes<'static> = FlatTypes::new(&[FlatType::F32]);
@@ -1578,7 +1575,7 @@ struct FlatTypesCache {
flags: PrimaryMap<TypeFlagsIndex, FlatTypesStorage>,
unions: PrimaryMap<TypeUnionIndex, FlatTypesStorage>,
options: PrimaryMap<TypeOptionIndex, FlatTypesStorage>,
expecteds: PrimaryMap<TypeExpectedIndex, FlatTypesStorage>,
results: PrimaryMap<TypeResultIndex, FlatTypesStorage>,
}
struct FlatTypesStorage {
@@ -1662,22 +1659,33 @@ impl FlatTypesStorage {
/// Builds up the flat types used to represent a `variant` which notably
/// handles "join"ing types together so each case is representable as a
/// single flat list of types.
///
/// The iterator item is:
///
/// * `None` - no payload for this case
/// * `Some(None)` - this case has a payload but can't be represented with
/// flat types
/// * `Some(Some(types))` - this case has a payload and is represented with
/// the types specified in the flat representation.
fn build_variant<'a, I>(&mut self, cases: I)
where
I: IntoIterator<Item = Option<FlatTypes<'a>>>,
I: IntoIterator<Item = Option<Option<FlatTypes<'a>>>>,
{
let cases = cases.into_iter();
self.push(FlatType::I32, FlatType::I32);
for ty in cases {
let types = match ty {
Some(types) => types,
Some(Some(types)) => types,
// If this case isn't representable with a flat list of types
// then this variant also isn't representable.
None => {
Some(None) => {
self.len = u8::try_from(MAX_FLAT_TYPES + 1).unwrap();
return;
}
// If this case doesn't have a payload then it doesn't change
// whether this is representable or not.
None => continue,
};
// If the case used all of the flat types then the discriminant
// added for this variant means that this variant is no longer
@@ -1739,19 +1747,26 @@ impl FlatTypesStorage {
}
fn variants(&mut self, types: &ComponentTypesBuilder, ty: &TypeVariant) {
self.build_variant(ty.cases.iter().map(|c| types.flat_types(&c.ty)))
self.build_variant(
ty.cases
.iter()
.map(|c| c.ty.as_ref().map(|ty| types.flat_types(ty))),
)
}
fn unions(&mut self, types: &ComponentTypesBuilder, ty: &TypeUnion) {
self.build_variant(ty.types.iter().map(|t| types.flat_types(t)))
self.build_variant(ty.types.iter().map(|t| Some(types.flat_types(t))))
}
fn expecteds(&mut self, types: &ComponentTypesBuilder, ty: &TypeExpected) {
self.build_variant([types.flat_types(&ty.ok), types.flat_types(&ty.err)]);
fn results(&mut self, types: &ComponentTypesBuilder, ty: &TypeResult) {
self.build_variant([
ty.ok.as_ref().map(|ty| types.flat_types(ty)),
ty.err.as_ref().map(|ty| types.flat_types(ty)),
])
}
fn options(&mut self, types: &ComponentTypesBuilder, ty: &TypeOption) {
self.build_variant([Some(FlatTypes::EMPTY), types.flat_types(&ty.ty)]);
self.build_variant([None, Some(types.flat_types(&ty.ty))]);
}
}

View File

@@ -49,7 +49,11 @@ impl ComponentTypesBuilder {
};
let mut results_indirect = false;
let results = match self.flatten_types(&options.options, MAX_FLAT_RESULTS, [ty.result]) {
let results = match self.flatten_types(
&options.options,
MAX_FLAT_RESULTS,
ty.results.iter().map(|(_, ty)| *ty),
) {
Some(list) => list,
None => {
results_indirect = true;

View File

@@ -17,7 +17,7 @@
use crate::component::{
CanonicalAbiInfo, ComponentTypesBuilder, InterfaceType, StringEncoding, TypeEnumIndex,
TypeExpectedIndex, TypeFlagsIndex, TypeInterfaceIndex, TypeOptionIndex, TypeRecordIndex,
TypeFlagsIndex, TypeInterfaceIndex, TypeOptionIndex, TypeRecordIndex, TypeResultIndex,
TypeTupleIndex, TypeUnionIndex, TypeVariantIndex, VariantInfo, FLAG_MAY_ENTER, FLAG_MAY_LEAVE,
MAX_FLAT_PARAMS, MAX_FLAT_RESULTS,
};
@@ -345,17 +345,19 @@ impl Compiler<'_, '_> {
param_locals: &[(u32, ValType)],
result_locals: &[(u32, ValType)],
) {
let src_ty = self.types[adapter.lift.ty].result;
let dst_ty = self.types[adapter.lower.ty].result;
let src_tys = &self.types[adapter.lift.ty].results;
let src_tys = src_tys.iter().map(|(_, ty)| *ty).collect::<Vec<_>>();
let dst_tys = &self.types[adapter.lower.ty].results;
let dst_tys = dst_tys.iter().map(|(_, ty)| *ty).collect::<Vec<_>>();
let lift_opts = &adapter.lift.options;
let lower_opts = &adapter.lower.options;
let src_flat = self
.types
.flatten_types(lift_opts, MAX_FLAT_RESULTS, [src_ty]);
let dst_flat = self
.types
.flatten_types(lower_opts, MAX_FLAT_RESULTS, [dst_ty]);
let src_flat =
self.types
.flatten_types(lift_opts, MAX_FLAT_RESULTS, src_tys.iter().copied());
let dst_flat =
self.types
.flatten_types(lower_opts, MAX_FLAT_RESULTS, dst_tys.iter().copied());
let src = if src_flat.is_some() {
Source::Stack(Stack {
@@ -367,7 +369,11 @@ impl Compiler<'_, '_> {
// return value of the function itself. The imported function will
// return a linear memory address at which the values can be read
// from.
let align = self.types.align(lift_opts, &src_ty);
let align = src_tys
.iter()
.map(|t| self.types.align(lift_opts, t))
.max()
.unwrap_or(1);
assert_eq!(result_locals.len(), 1);
let (addr, ty) = result_locals[0];
assert_eq!(ty, lift_opts.ptr());
@@ -380,13 +386,25 @@ impl Compiler<'_, '_> {
// This is slightly different than `translate_params` where the
// return pointer was provided by the caller of this function
// meaning the last parameter local is a pointer into linear memory.
let align = self.types.align(lower_opts, &dst_ty);
let align = dst_tys
.iter()
.map(|t| self.types.align(lower_opts, t))
.max()
.unwrap_or(1);
let (addr, ty) = *param_locals.last().expect("no retptr");
assert_eq!(ty, lower_opts.ptr());
Destination::Memory(self.memory_operand(lower_opts, TempLocal::new(addr, ty), align))
};
self.translate(&src_ty, &src, &dst_ty, &dst);
let srcs = src
.record_field_srcs(self.types, src_tys.iter().copied())
.zip(src_tys.iter());
let dsts = dst
.record_field_dsts(self.types, dst_tys.iter().copied())
.zip(dst_tys.iter());
for ((src, src_ty), (dst, dst_ty)) in srcs.zip(dsts) {
self.translate(&src_ty, &src, &dst_ty, &dst);
}
}
fn translate(
@@ -406,8 +424,7 @@ impl Compiler<'_, '_> {
// Classify the source type as "primitive" or not as a heuristic to
// whether the translation should be split out into a helper function.
let src_primitive = match src_ty {
InterfaceType::Unit
| InterfaceType::Bool
InterfaceType::Bool
| InterfaceType::U8
| InterfaceType::S8
| InterfaceType::U16
@@ -429,7 +446,7 @@ impl Compiler<'_, '_> {
| InterfaceType::Union(_)
| InterfaceType::Enum(_)
| InterfaceType::Option(_)
| InterfaceType::Expected(_) => false,
| InterfaceType::Result(_) => false,
};
let top_level = mem::replace(&mut self.top_level_translate, false);
@@ -440,10 +457,10 @@ impl Compiler<'_, '_> {
// were translated inline then this could get arbitrarily large
//
// (type $level0 (list u8))
// (type $level1 (expected $level0 $level0))
// (type $level2 (expected $level1 $level1))
// (type $level3 (expected $level2 $level2))
// (type $level4 (expected $level3 $level3))
// (type $level1 (result $level0 $level0))
// (type $level2 (result $level1 $level1))
// (type $level3 (result $level2 $level2))
// (type $level4 (result $level3 $level3))
// ;; ...
//
// If everything we inlined then translation of `$level0` would appear
@@ -505,7 +522,6 @@ impl Compiler<'_, '_> {
}
}
match src_ty {
InterfaceType::Unit => self.translate_unit(src, dst_ty, dst),
InterfaceType::Bool => self.translate_bool(src, dst_ty, dst),
InterfaceType::U8 => self.translate_u8(src, dst_ty, dst),
InterfaceType::S8 => self.translate_s8(src, dst_ty, dst),
@@ -527,18 +543,12 @@ impl Compiler<'_, '_> {
InterfaceType::Union(u) => self.translate_union(*u, src, dst_ty, dst),
InterfaceType::Enum(t) => self.translate_enum(*t, src, dst_ty, dst),
InterfaceType::Option(t) => self.translate_option(*t, src, dst_ty, dst),
InterfaceType::Expected(t) => self.translate_expected(*t, src, dst_ty, dst),
InterfaceType::Result(t) => self.translate_result(*t, src, dst_ty, dst),
}
self.top_level_translate = top_level;
}
fn translate_unit(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
// TODO: subtyping
assert!(matches!(dst_ty, InterfaceType::Unit));
drop((src, dst));
}
fn translate_bool(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
// TODO: subtyping
assert!(matches!(dst_ty, InterfaceType::Bool));
@@ -1996,8 +2006,8 @@ impl Compiler<'_, '_> {
_ => panic!("expected a variant"),
};
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 src_info = variant_info(self.types, src_ty.cases.iter().map(|c| c.ty.as_ref()));
let dst_info = variant_info(self.types, dst_ty.cases.iter().map(|c| c.ty.as_ref()));
let iter = src_ty.cases.iter().enumerate().map(|(src_i, src_case)| {
let dst_i = dst_ty
@@ -2010,9 +2020,9 @@ impl Compiler<'_, '_> {
let dst_i = u32::try_from(dst_i).unwrap();
VariantCase {
src_i,
src_ty: &src_case.ty,
src_ty: src_case.ty.as_ref(),
dst_i,
dst_ty: &dst_case.ty,
dst_ty: dst_case.ty.as_ref(),
}
});
self.convert_variant(src, &src_info, dst, &dst_info, iter);
@@ -2031,8 +2041,8 @@ impl Compiler<'_, '_> {
_ => panic!("expected an option"),
};
assert_eq!(src_ty.types.len(), dst_ty.types.len());
let src_info = variant_info(self.types, src_ty.types.iter().copied());
let dst_info = variant_info(self.types, dst_ty.types.iter().copied());
let src_info = variant_info(self.types, src_ty.types.iter().map(Some));
let dst_info = variant_info(self.types, dst_ty.types.iter().map(Some));
self.convert_variant(
src,
@@ -2049,8 +2059,8 @@ impl Compiler<'_, '_> {
VariantCase {
src_i: i,
dst_i: i,
src_ty,
dst_ty,
src_ty: Some(src_ty),
dst_ty: Some(dst_ty),
}
}),
);
@@ -2068,10 +2078,9 @@ impl Compiler<'_, '_> {
InterfaceType::Enum(t) => &self.types[*t],
_ => panic!("expected an option"),
};
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 src_info = variant_info(self.types, src_ty.names.iter().map(|_| None));
let dst_info = variant_info(self.types, dst_ty.names.iter().map(|_| None));
let unit = &InterfaceType::Unit;
self.convert_variant(
src,
&src_info,
@@ -2084,8 +2093,8 @@ impl Compiler<'_, '_> {
VariantCase {
src_i,
dst_i,
src_ty: unit,
dst_ty: unit,
src_ty: None,
dst_ty: None,
}
}),
);
@@ -2103,9 +2112,11 @@ impl Compiler<'_, '_> {
InterfaceType::Option(t) => &self.types[*t].ty,
_ => panic!("expected an option"),
};
let src_ty = Some(src_ty);
let dst_ty = Some(dst_ty);
let src_info = variant_info(self.types, [InterfaceType::Unit, *src_ty]);
let dst_info = variant_info(self.types, [InterfaceType::Unit, *dst_ty]);
let src_info = variant_info(self.types, [None, src_ty]);
let dst_info = variant_info(self.types, [None, dst_ty]);
self.convert_variant(
src,
@@ -2116,8 +2127,8 @@ impl Compiler<'_, '_> {
VariantCase {
src_i: 0,
dst_i: 0,
src_ty: &InterfaceType::Unit,
dst_ty: &InterfaceType::Unit,
src_ty: None,
dst_ty: None,
},
VariantCase {
src_i: 1,
@@ -2130,21 +2141,21 @@ impl Compiler<'_, '_> {
);
}
fn translate_expected(
fn translate_result(
&mut self,
src_ty: TypeExpectedIndex,
src_ty: TypeResultIndex,
src: &Source<'_>,
dst_ty: &InterfaceType,
dst: &Destination,
) {
let src_ty = &self.types[src_ty];
let dst_ty = match dst_ty {
InterfaceType::Expected(t) => &self.types[*t],
_ => panic!("expected an expected"),
InterfaceType::Result(t) => &self.types[*t],
_ => panic!("expected a result"),
};
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]);
let src_info = variant_info(self.types, [src_ty.ok.as_ref(), src_ty.err.as_ref()]);
let dst_info = variant_info(self.types, [dst_ty.ok.as_ref(), dst_ty.err.as_ref()]);
self.convert_variant(
src,
@@ -2155,14 +2166,14 @@ impl Compiler<'_, '_> {
VariantCase {
src_i: 0,
dst_i: 0,
src_ty: &src_ty.ok,
dst_ty: &dst_ty.ok,
src_ty: src_ty.ok.as_ref(),
dst_ty: dst_ty.ok.as_ref(),
},
VariantCase {
src_i: 1,
dst_i: 1,
src_ty: &src_ty.err,
dst_ty: &dst_ty.err,
src_ty: src_ty.err.as_ref(),
dst_ty: dst_ty.err.as_ref(),
},
]
.into_iter(),
@@ -2254,11 +2265,18 @@ impl Compiler<'_, '_> {
},
}
// Translate the payload of this case using the various types from
// the dst/src.
let src_payload = src.payload_src(self.types, src_info, src_ty);
let dst_payload = dst.payload_dst(self.types, dst_info, dst_ty);
self.translate(src_ty, &src_payload, dst_ty, &dst_payload);
// Translate the payload of this case using the various types from
// the dst/src.
match (src_ty, dst_ty) {
(Some(src_ty), Some(dst_ty)) => {
self.translate(src_ty, &src_payload, dst_ty, &dst_payload);
}
(None, None) => {}
_ => unimplemented!(),
}
// If the results of this translation were placed on the stack then
// the stack values may need to be padded with more zeros due to
@@ -2824,11 +2842,14 @@ impl<'a> Source<'a> {
&self,
types: &ComponentTypesBuilder,
info: &VariantInfo,
case: &InterfaceType,
case: Option<&InterfaceType>,
) -> Source<'a> {
match self {
Source::Stack(s) => {
let flat_len = types.flat_types(case).unwrap().len();
let flat_len = match case {
Some(case) => types.flat_types(case).unwrap().len(),
None => 0,
};
Source::Stack(s.slice(1..s.locals.len()).slice(0..flat_len))
}
Source::Memory(mem) => {
@@ -2879,11 +2900,14 @@ impl<'a> Destination<'a> {
&self,
types: &ComponentTypesBuilder,
info: &VariantInfo,
case: &InterfaceType,
case: Option<&InterfaceType>,
) -> Destination {
match self {
Destination::Stack(s, opts) => {
let flat_len = types.flat_types(case).unwrap().len();
let flat_len = match case {
Some(case) => types.flat_types(case).unwrap().len(),
None => 0,
};
Destination::Stack(&s[1..][..flat_len], opts)
}
Destination::Memory(mem) => {
@@ -2949,17 +2973,22 @@ impl<'a> Stack<'a> {
struct VariantCase<'a> {
src_i: u32,
src_ty: &'a InterfaceType,
src_ty: Option<&'a InterfaceType>,
dst_i: u32,
dst_ty: &'a InterfaceType,
dst_ty: Option<&'a InterfaceType>,
}
fn variant_info<I>(types: &ComponentTypesBuilder, cases: I) -> VariantInfo
fn variant_info<'a, I>(types: &ComponentTypesBuilder, cases: I) -> VariantInfo
where
I: IntoIterator<Item = InterfaceType>,
I: IntoIterator<Item = Option<&'a InterfaceType>>,
I::IntoIter: ExactSizeIterator,
{
VariantInfo::new(cases.into_iter().map(|i| types.canonical_abi(&i))).0
VariantInfo::new(
cases
.into_iter()
.map(|ty| ty.map(|ty| types.canonical_abi(ty))),
)
.0
}
enum MallocSize {

View File

@@ -429,11 +429,11 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> {
match kind {
ElementKind::Active {
table_index,
init_expr,
offset_expr,
} => {
let table_index = TableIndex::from_u32(table_index);
let mut init_expr_reader = init_expr.get_binary_reader();
let (base, offset) = match init_expr_reader.read_operator()? {
let mut offset_expr_reader = offset_expr.get_binary_reader();
let (base, offset) = match offset_expr_reader.read_operator()? {
Operator::I32Const { value } => (None, value as u32),
Operator::GlobalGet { global_index } => {
(Some(GlobalIndex::from_u32(global_index)), 0)
@@ -547,12 +547,12 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> {
match kind {
DataKind::Active {
memory_index,
init_expr,
offset_expr,
} => {
let range = mk_range(&mut self.result.total_data)?;
let memory_index = MemoryIndex::from_u32(memory_index);
let mut init_expr_reader = init_expr.get_binary_reader();
let (base, offset) = match init_expr_reader.read_operator()? {
let mut offset_expr_reader = offset_expr.get_binary_reader();
let (base, offset) = match offset_expr_reader.read_operator()? {
Operator::I32Const { value } => (None, value as u64),
Operator::I64Const { value } => (None, value as u64),
Operator::GlobalGet { global_index } => {