Skip to content

Commit

Permalink
WIP wasm gc instructions in wasm-smith
Browse files Browse the repository at this point in the history
  • Loading branch information
fitzgen committed Jan 18, 2024
1 parent f401690 commit adf93b8
Show file tree
Hide file tree
Showing 3 changed files with 2,293 additions and 606 deletions.
51 changes: 50 additions & 1 deletion crates/wasm-encoder/src/core/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ impl TryFrom<wasmparser::FuncType> for FuncType {
}

/// Represents a type of an array in a WebAssembly module.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct ArrayType(pub FieldType);

#[cfg(feature = "wasmparser")]
Expand Down Expand Up @@ -178,6 +178,21 @@ impl TryFrom<wasmparser::StorageType> for StorageType {
}
}

impl StorageType {
/// Is this storage type defaultable?
pub fn is_defaultable(&self) -> bool {
self.unpack().is_defaultable()
}

/// Unpack this storage type into a value type.
pub fn unpack(&self) -> ValType {
match self {
StorageType::I8 | StorageType::I16 => ValType::I32,
StorageType::Val(v) => *v,
}
}
}

/// The type of a core WebAssembly value.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum ValType {
Expand Down Expand Up @@ -216,6 +231,32 @@ impl TryFrom<wasmparser::ValType> for ValType {
}
}

impl ValType {
/// Is this a numeric value type?
pub fn is_numeric(&self) -> bool {
match self {
ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 => true,
ValType::V128 | ValType::Ref(_) => false,
}
}

/// Is this a vector type?
pub fn is_vector(&self) -> bool {
match self {
ValType::V128 => true,
ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::Ref(_) => false,
}
}

/// Is this a reference type?
pub fn is_reference(&self) -> bool {
match self {
ValType::Ref(_) => true,
ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => false,
}
}
}

impl FuncType {
/// Creates a new [`FuncType`] from the given `params` and `results`.
pub fn new<P, R>(params: P, results: R) -> Self
Expand Down Expand Up @@ -257,6 +298,14 @@ impl ValType {
pub const EXTERNREF: ValType = ValType::Ref(RefType::EXTERNREF);
/// Alias for the `exnref` type in WebAssembly
pub const EXNREF: ValType = ValType::Ref(RefType::EXNREF);

/// Is this value defaultable?
pub fn is_defaultable(&self) -> bool {
match self {
ValType::Ref(r) => r.nullable,
ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => true,
}
}
}

impl Encode for StorageType {
Expand Down
119 changes: 100 additions & 19 deletions crates/wasm-smith/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,13 +249,50 @@ pub(crate) struct SubType {
pub(crate) composite_type: CompositeType,
}

impl SubType {
fn unwrap_struct(&self) -> &StructType {
self.composite_type.unwrap_struct()
}

fn unwrap_func(&self) -> &Rc<FuncType> {
self.composite_type.unwrap_func()
}

fn unwrap_array(&self) -> &ArrayType {
self.composite_type.unwrap_array()
}
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) enum CompositeType {
Array(ArrayType),
Func(Rc<FuncType>),
Struct(StructType),
}

impl CompositeType {
fn unwrap_struct(&self) -> &StructType {
match self {
CompositeType::Struct(s) => s,
_ => panic!("not a struct"),
}
}

fn unwrap_func(&self) -> &Rc<FuncType> {
match self {
CompositeType::Func(f) => f,
_ => panic!("not a func"),
}
}

fn unwrap_array(&self) -> &ArrayType {
match self {
CompositeType::Array(a) => a,
_ => panic!("not an array"),
}
}
}

/// A function signature.
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub(crate) struct FuncType {
Expand Down Expand Up @@ -392,6 +429,24 @@ impl Module {
Ok(())
}

fn val_type_is_sub_type(&self, a: ValType, b: ValType) -> bool {
match (a, b) {
(a, b) if a == b => true,
(ValType::Ref(a), ValType::Ref(b)) => self.ref_type_is_sub_type(a, b),
_ => false,
}
}

/// Is `a` a subtype of `b`?
fn ref_type_is_sub_type(&self, a: RefType, b: RefType) -> bool {
if a == b {
return true;
}

// TODO FITZGEN: do proper subtype checking
false
}

fn arbitrary_types(&mut self, u: &mut Unstructured) -> Result<()> {
assert!(self.config.min_types <= self.config.max_types);
while self.types.len() < self.config.min_types {
Expand Down Expand Up @@ -601,28 +656,14 @@ impl Module {
}
}

fn arbitrary_matching_ref_type(
&mut self,
u: &mut Unstructured,
ty: RefType,
) -> Result<RefType> {
fn arbitrary_matching_ref_type(&self, u: &mut Unstructured, ty: RefType) -> Result<RefType> {
Ok(RefType {
// TODO: For now, only create allow nullable reference
// types. Eventually we should support non-nullable reference types,
// but this means that we will also need to recognize when it is
// impossible to create an instance of the reference (eg `(ref
// nofunc)` has no instances, and self-referential types that
// contain a non-null self-reference are also impossible to create).
nullable: true,
nullable: ty.nullable,
heap_type: self.arbitrary_matching_heap_type(u, ty.heap_type)?,
})
}

fn arbitrary_matching_heap_type(
&mut self,
u: &mut Unstructured,
ty: HeapType,
) -> Result<HeapType> {
fn arbitrary_matching_heap_type(&self, u: &mut Unstructured, ty: HeapType) -> Result<HeapType> {
use HeapType as HT;
let mut choices = vec![ty];
match ty {
Expand Down Expand Up @@ -716,7 +757,7 @@ impl Module {
}

fn arbitrary_super_type_of_ref_type(
&mut self,
&self,
u: &mut Unstructured,
ty: RefType,
) -> Result<RefType> {
Expand All @@ -733,7 +774,7 @@ impl Module {
}

fn arbitrary_super_type_of_heap_type(
&mut self,
&self,
u: &mut Unstructured,
ty: HeapType,
) -> Result<HeapType> {
Expand Down Expand Up @@ -860,6 +901,45 @@ impl Module {
}
}

fn arbitrary_ref_type(&self, u: &mut Unstructured) -> Result<RefType> {
Ok(RefType {
nullable: true,
heap_type: self.arbitrary_heap_type(u)?,
})
}

fn arbitrary_heap_type(&self, u: &mut Unstructured) -> Result<HeapType> {
assert!(self.config.reference_types_enabled);

if self.config.gc_enabled && !self.types.is_empty() && u.arbitrary()? {
let type_ref_limit = u32::try_from(self.types.len()).unwrap();
let idx = u.int_in_range(0..=type_ref_limit)?;
return Ok(HeapType::Concrete(idx));
}

let mut choices = vec![HeapType::Func, HeapType::Extern];
if self.config.exceptions_enabled {
choices.push(HeapType::Exn);
}
if self.config.gc_enabled {
choices.extend(
[
HeapType::Any,
HeapType::None,
HeapType::NoExtern,
HeapType::NoFunc,
HeapType::Eq,
HeapType::Struct,
HeapType::Array,
HeapType::I31,
]
.iter()
.copied(),
);
}
u.choose(&choices).copied()
}

fn arbitrary_func_type(
&mut self,
u: &mut Unstructured,
Expand Down Expand Up @@ -2212,6 +2292,7 @@ flags! {
Table,
Memory,
Control,
Aggregate,
}
}

Expand Down
Loading

0 comments on commit adf93b8

Please sign in to comment.