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:
@@ -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