Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 27 additions & 11 deletions crates/wasmparser/src/validator/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,10 +255,25 @@ impl Concurrency {
}
}

#[derive(Clone, Copy)]
pub(crate) enum PtrSize {
Ptr32,
Ptr64,
}

impl PtrSize {
pub(crate) fn core_type(&self) -> ValType {
match self {
PtrSize::Ptr32 => ValType::I32,
PtrSize::Ptr64 => ValType::I64,
}
}
}

#[derive(Clone, Copy)]
pub(crate) struct CanonicalOptions {
pub(crate) string_encoding: StringEncoding,
pub(crate) memory: Option<u32>,
pub(crate) memory: Option<(u32, PtrSize)>,
pub(crate) realloc: Option<u32>,
pub(crate) post_return: Option<u32>,
pub(crate) concurrency: Concurrency,
Expand Down Expand Up @@ -2716,8 +2731,8 @@ impl ComponentState {
CanonicalOption::Memory(idx) => {
memory = match memory {
None => {
self.cabi_memory_at(*idx, offset)?;
Some(*idx)
let ptr_size = self.cabi_memory_at(*idx, offset)?;
Some((*idx, ptr_size))
}
Some(_) => {
return Err(BinaryReaderError::new(
Expand Down Expand Up @@ -2858,19 +2873,15 @@ impl ComponentState {

// Validate `realloc`
if let Some(realloc_idx) = realloc {
let mty = match memory {
Some(i) => self.memory_at(i, offset)?,
let addr_type = match memory {
Some((_, ptr_size)) => ptr_size.core_type(),
None => {
return Err(BinaryReaderError::new(
"canonical option `realloc` requires `memory` to also be specified",
offset,
));
}
};
let addr_type = match mty.memory64 {
true => ValType::I64,
false => ValType::I32,
};
let ty_id = self.core_function_at(realloc_idx, offset)?;
let func_ty = types[ty_id].unwrap_func();
if func_ty.params() != [addr_type, addr_type, addr_type, addr_type]
Expand Down Expand Up @@ -4481,7 +4492,7 @@ impl ComponentState {
///
/// At this time this requires that the memory is a plain 32-bit or 64-bit linear
/// memory. Notably this disallows shared memory.
fn cabi_memory_at(&self, idx: u32, offset: usize) -> Result<()> {
fn cabi_memory_at(&self, idx: u32, offset: usize) -> Result<PtrSize> {
let ty = self.memory_at(idx, offset)?;
let valid_memory_type = MemoryType {
initial: 0,
Expand All @@ -4496,7 +4507,12 @@ impl ComponentState {
"64-bit memories require the `cm64` feature to be enabled"
);
}
SubtypeCx::memory_type(ty, &valid_memory_type, offset)
SubtypeCx::memory_type(ty, &valid_memory_type, offset)?;
Ok(if ty.memory64 {
PtrSize::Ptr64
} else {
PtrSize::Ptr32
})
}

/// Completes the translation of this component, performing final
Expand Down
70 changes: 50 additions & 20 deletions crates/wasmparser/src/validator/component_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use super::component::ExternKind;
use super::{CanonicalOptions, Concurrency};
use crate::validator::StringEncoding;
use crate::validator::component::PtrSize;
use crate::validator::names::KebabString;
use crate::validator::types::{
CoreTypeId, EntityType, SnapshotList, TypeAlloc, TypeData, TypeIdentifier, TypeInfo, TypeList,
Expand Down Expand Up @@ -302,7 +303,11 @@ impl PrimitiveValType {
}
}

fn push_primitive_wasm_types(ty: &PrimitiveValType, lowered_types: &mut LoweredTypes) -> bool {
fn push_primitive_wasm_types(
ptr_size: PtrSize,
ty: &PrimitiveValType,
lowered_types: &mut LoweredTypes,
) -> bool {
match ty {
PrimitiveValType::Bool
| PrimitiveValType::S8
Expand All @@ -317,7 +322,8 @@ fn push_primitive_wasm_types(ty: &PrimitiveValType, lowered_types: &mut LoweredT
PrimitiveValType::F32 => lowered_types.try_push(ValType::F32),
PrimitiveValType::F64 => lowered_types.try_push(ValType::F64),
PrimitiveValType::String => {
lowered_types.try_push(ValType::I32) && lowered_types.try_push(ValType::I32)
lowered_types.try_push(ptr_size.core_type())
&& lowered_types.try_push(ptr_size.core_type())
}
}
}
Expand Down Expand Up @@ -731,10 +737,15 @@ impl ComponentValType {
}
}

fn push_wasm_types(&self, types: &TypeList, lowered_types: &mut LoweredTypes) -> bool {
fn push_wasm_types(
&self,
ptr_size: PtrSize,
types: &TypeList,
lowered_types: &mut LoweredTypes,
) -> bool {
match self {
Self::Primitive(ty) => push_primitive_wasm_types(ty, lowered_types),
Self::Type(id) => types[*id].push_wasm_types(types, lowered_types),
Self::Primitive(ty) => push_primitive_wasm_types(ptr_size, ty, lowered_types),
Self::Type(id) => types[*id].push_wasm_types(ptr_size, types, lowered_types),
}
}

Expand Down Expand Up @@ -1211,6 +1222,11 @@ impl ComponentFuncType {
return self.lower_gc(types, abi, options, offset);
}

let ptr_size = match options.memory {
None => PtrSize::Ptr32,
Some((_, ptr_size)) => ptr_size,
};

if abi == Abi::Lower && options.concurrency.is_async() {
sig.params.max = MAX_FLAT_ASYNC_PARAMS;
}
Expand All @@ -1231,7 +1247,7 @@ impl ComponentFuncType {
}
}

if !ty.push_wasm_types(types, &mut sig.params) {
if !ty.push_wasm_types(ptr_size, types, &mut sig.params) {
// Too many parameters to pass directly
// Function will have a single pointer parameter to pass the arguments
// via linear memory
Expand All @@ -1258,7 +1274,7 @@ impl ComponentFuncType {
abi == Abi::Lower && ty.contains_ptr(types)
})?;

if !ty.push_wasm_types(types, &mut sig.results) {
if !ty.push_wasm_types(ptr_size, types, &mut sig.results) {
// Too many results to return directly, either a retptr
// parameter will be used (import) or a single pointer
// will be returned (export).
Expand Down Expand Up @@ -1295,8 +1311,11 @@ impl ComponentFuncType {
// Note that the return type itself has no effect on the
// expected core signature of the lifted function.

let overflow =
!ty.push_wasm_types(types, &mut LoweredTypes::new(MAX_FLAT_FUNC_PARAMS));
let overflow = !ty.push_wasm_types(
ptr_size,
types,
&mut LoweredTypes::new(MAX_FLAT_FUNC_PARAMS),
);

options.require_memory_if(offset, || overflow || ty.contains_ptr(types))?;
}
Expand Down Expand Up @@ -1562,45 +1581,56 @@ impl ComponentDefinedType {
}
}

fn push_wasm_types(&self, types: &TypeList, lowered_types: &mut LoweredTypes) -> bool {
fn push_wasm_types(
&self,
ptr_size: PtrSize,
types: &TypeList,
lowered_types: &mut LoweredTypes,
) -> bool {
match self {
Self::Primitive(ty) => push_primitive_wasm_types(ty, lowered_types),
Self::Primitive(ty) => push_primitive_wasm_types(ptr_size, ty, lowered_types),
Self::Record(r) => r
.fields
.iter()
.all(|(_, ty)| ty.push_wasm_types(types, lowered_types)),
.all(|(_, ty)| ty.push_wasm_types(ptr_size, types, lowered_types)),
Self::Variant(v) => Self::push_variant_wasm_types(
v.cases.iter().filter_map(|(_, case)| case.ty.as_ref()),
ptr_size,
types,
lowered_types,
),
Self::List(_) | Self::Map(_, _) => {
lowered_types.try_push(ValType::I32) && lowered_types.try_push(ValType::I32)
lowered_types.try_push(ptr_size.core_type())
&& lowered_types.try_push(ptr_size.core_type())
}
Self::FixedLengthList(ty, length) => {
(0..*length).all(|_n| ty.push_wasm_types(types, lowered_types))
(0..*length).all(|_n| ty.push_wasm_types(ptr_size, types, lowered_types))
}
Self::Tuple(t) => t
.types
.iter()
.all(|ty| ty.push_wasm_types(types, lowered_types)),
.all(|ty| ty.push_wasm_types(ptr_size, types, lowered_types)),
Self::Flags(names) => {
(0..(names.len() + 31) / 32).all(|_| lowered_types.try_push(ValType::I32))
}
Self::Enum(_) | Self::Own(_) | Self::Borrow(_) | Self::Future(_) | Self::Stream(_) => {
lowered_types.try_push(ValType::I32)
}
Self::Option(ty) => {
Self::push_variant_wasm_types([ty].into_iter(), types, lowered_types)
}
Self::Result { ok, err } => {
Self::push_variant_wasm_types(ok.iter().chain(err.iter()), types, lowered_types)
Self::push_variant_wasm_types([ty].into_iter(), ptr_size, types, lowered_types)
}
Self::Result { ok, err } => Self::push_variant_wasm_types(
ok.iter().chain(err.iter()),
ptr_size,
types,
lowered_types,
),
}
}

fn push_variant_wasm_types<'a>(
cases: impl Iterator<Item = &'a ComponentValType>,
ptr_size: PtrSize,
types: &TypeList,
lowered_types: &mut LoweredTypes,
) -> bool {
Expand All @@ -1614,7 +1644,7 @@ impl ComponentDefinedType {
for ty in cases {
let mut temp = LoweredTypes::new(lowered_types.max);

if !ty.push_wasm_types(types, &mut temp) {
if !ty.push_wasm_types(ptr_size, types, &mut temp) {
return false;
}

Expand Down
13 changes: 13 additions & 0 deletions tests/cli/component-model/memory64/string.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
;; RUN: wast --assert default --snapshot tests/snapshots -f cm64 %

(component
(core module $m
(func (export "f") (param i64 i64))
(func (export "realloc") (param i64 i64 i64 i64) (result i64) i64.const 0)
(memory (export "memory") i64 1)
)
(core instance $m (instantiate $m))
(func (export "a") (param "a" string)
(canon lift (core func $m "f") (realloc (func $m "realloc")) (memory $m "memory"))
)
)
11 changes: 11 additions & 0 deletions tests/snapshots/cli/component-model/memory64/string.wast.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"source_filename": "tests/cli/component-model/memory64/string.wast",
"commands": [
{
"type": "module",
"line": 3,
"filename": "string.0.wasm",
"module_type": "binary"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
(component
(core module $m (;0;)
(type (;0;) (func (param i64 i64)))
(type (;1;) (func (param i64 i64 i64 i64) (result i64)))
(memory (;0;) i64 1)
(export "f" (func 0))
(export "realloc" (func 1))
(export "memory" (memory 0))
(func (;0;) (type 0) (param i64 i64))
(func (;1;) (type 1) (param i64 i64 i64 i64) (result i64)
i64.const 0
)
)
(core instance $m (;0;) (instantiate $m))
(type (;0;) (func (param "a" string)))
(alias core export $m "f" (core func (;0;)))
(alias core export $m "realloc" (core func (;1;)))
(alias core export $m "memory" (core memory (;0;)))
(func (;0;) (type 0) (canon lift (core func 0) (realloc 1) (memory 0)))
(export (;1;) "a" (func 0))
)
Loading