Skip to content

Commit 2108dfc

Browse files
authored
threads: add shared heap types (#1600)
* threads: add `shared` heap types The shared-everything-thread [proposal] specifies that all abstract heap types can be marked `shared` by prepending them with the 0x65 prefix byte. While we wait for clarification on what this precisely means, I chose to implement the "extra byte" version, which will result in larger binaries that use `shared` types. The key change here is that `wasmparser::HeapType` now groups all of the abstract heap types (e.g., `any`) into a `wasmparser::HeapType::Abstract` variant, which allows us to mark those as `shared` in a single place. This results in a cascade of changes elsewhere, most of which are pretty harmless (an extra, internal `match` on the `ty` of the `Abstract` variant). The real business left unfinished by this commit is _what_ to do with the `shared` field in all of these locations; for now I've just noted those as TODOs to get a review on this approach so far before forging ahead. [proposal]: https://github.com/WebAssembly/shared-everything-threads [#64]: WebAssembly/shared-everything-threads#64 * threads: propagate `shared` field to `wasm_encoder` This continues the work of the previous commit by propagating the `shared` field of abstract heap types through to `wasm_encoder`, with uses in various other crates. As before, the boundary at which we cannot yet handle sharedness is marked with TODO. * threads: propagate `shared` into `wast` This finally extends `shared` into the heap types defined in `wast`. As before, a distinction is drawn between abstract and concrete heap types. * review: refactor reading of short-hand reftypes * review: run CI-specific `rustfmt` invocation * review: use short-hand `RefType` constant for brevity * review: use `Into` for wasmparser-to-wasm-encoder conversion * review: document which proposal `shared` appears in * review: fix typo, 'eqf' -> 'eq' * review: remove unnecessary TODO * review: link to shared-ness issue for reference conversions * review: fix typo * review: add `wasmparser` feature for `Into` conversions * review: use type shorthand in wasm-smith * review: use more `Into` conversions * review: add aliases for heap types * review: add clarity parentheses * review: add and use more heap type aliases (in wasmparser) * review: use aliases... more * review: integrate with upstream * review: re-add full explicit matching in subtype check * fix: re-run rustfmt * review: handle shared peeking in wast * threads: check reference types for `shared`-ness In previous commits, we were unable to determine if a reference type was shared because concrete heap types point to a type elsewhere in the module that we weren't able to retrieve at the time the check was made. This change fixes that by plumbing a `TypeList` through to the right place and implementing `TypeList::is_shared`. This will still fail with a `todo!()` at the boundary of where shared-ness is implemented; once composite types gain shared flags that `todo!()` should go away. * threads: fix parsing ambiguities When parsing a shared global in the WebAssembly text format, one might run across the following text: `(global (shared anyref))`. In the current paradigm, this should parse to a `shared` global that contains an invalid `anyref` (i.e., `(ref null any)` in long-form, which is unshared); this should be parseable but result in a validation error. The correct form is `(global (shared (shared anyref)))`. This change fixes several issues related to this as well as refactoring some of the "short-hand"/"long-hand" parsing code. * threads: add heap type tests This change adds generated tests for all of the abstract heap types when they are `shared` and placed in `shared` globals. Since globals are the only thing we can mark shared now that can touch these types, they are a good vehicle for testing the parsing, encoding, and validation of all of this. Eventually the same must be done for concrete heap types once composite types (`func`, `struct`, `array`) can be marked shared.
1 parent 34fbd2a commit 2108dfc

File tree

42 files changed

+1931
-788
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1931
-788
lines changed

crates/wasm-encoder/src/core/types.rs

+170-47
Original file line numberDiff line numberDiff line change
@@ -344,33 +344,91 @@ pub struct RefType {
344344
}
345345

346346
impl RefType {
347-
/// Alias for the `funcref` type in WebAssembly
347+
/// Alias for the `anyref` type in WebAssembly.
348+
pub const ANYREF: RefType = RefType {
349+
nullable: true,
350+
heap_type: HeapType::Abstract {
351+
shared: false,
352+
ty: AbstractHeapType::Any,
353+
},
354+
};
355+
356+
/// Alias for the `anyref` type in WebAssembly.
357+
pub const EQREF: RefType = RefType {
358+
nullable: true,
359+
heap_type: HeapType::Abstract {
360+
shared: false,
361+
ty: AbstractHeapType::Eq,
362+
},
363+
};
364+
365+
/// Alias for the `funcref` type in WebAssembly.
348366
pub const FUNCREF: RefType = RefType {
349367
nullable: true,
350-
heap_type: HeapType::Func,
368+
heap_type: HeapType::Abstract {
369+
shared: false,
370+
ty: AbstractHeapType::Func,
371+
},
351372
};
352373

353-
/// Alias for the `externref` type in WebAssembly
374+
/// Alias for the `externref` type in WebAssembly.
354375
pub const EXTERNREF: RefType = RefType {
355376
nullable: true,
356-
heap_type: HeapType::Extern,
377+
heap_type: HeapType::Abstract {
378+
shared: false,
379+
ty: AbstractHeapType::Extern,
380+
},
357381
};
358382

359-
/// Alias for the `exnref` type in WebAssembly
383+
/// Alias for the `i31ref` type in WebAssembly.
384+
pub const I31REF: RefType = RefType {
385+
nullable: true,
386+
heap_type: HeapType::Abstract {
387+
shared: false,
388+
ty: AbstractHeapType::I31,
389+
},
390+
};
391+
392+
/// Alias for the `arrayref` type in WebAssembly.
393+
pub const ARRAYREF: RefType = RefType {
394+
nullable: true,
395+
heap_type: HeapType::Abstract {
396+
shared: false,
397+
ty: AbstractHeapType::Array,
398+
},
399+
};
400+
401+
/// Alias for the `exnref` type in WebAssembly.
360402
pub const EXNREF: RefType = RefType {
361403
nullable: true,
362-
heap_type: HeapType::Exn,
404+
heap_type: HeapType::Abstract {
405+
shared: false,
406+
ty: AbstractHeapType::Exn,
407+
},
363408
};
409+
410+
/// Set the nullability of this reference type.
411+
pub fn nullable(mut self, nullable: bool) -> Self {
412+
self.nullable = nullable;
413+
self
414+
}
364415
}
365416

366417
impl Encode for RefType {
367418
fn encode(&self, sink: &mut Vec<u8>) {
368419
if self.nullable {
369420
// Favor the original encodings of `funcref` and `externref` where
370-
// possible
421+
// possible.
422+
use AbstractHeapType::*;
371423
match self.heap_type {
372-
HeapType::Func => return sink.push(0x70),
373-
HeapType::Extern => return sink.push(0x6f),
424+
HeapType::Abstract {
425+
shared: false,
426+
ty: Func,
427+
} => return sink.push(0x70),
428+
HeapType::Abstract {
429+
shared: false,
430+
ty: Extern,
431+
} => return sink.push(0x6f),
374432
_ => {}
375433
}
376434
}
@@ -405,6 +463,78 @@ impl From<RefType> for ValType {
405463
/// Part of the function references proposal.
406464
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
407465
pub enum HeapType {
466+
/// An abstract heap type; e.g., `anyref`.
467+
Abstract {
468+
/// Whether the type is shared.
469+
shared: bool,
470+
/// The actual heap type.
471+
ty: AbstractHeapType,
472+
},
473+
474+
/// A concrete Wasm-defined type at the given index.
475+
Concrete(u32),
476+
}
477+
478+
impl HeapType {
479+
/// Alias for the unshared `any` heap type.
480+
pub const ANY: Self = Self::Abstract {
481+
shared: false,
482+
ty: AbstractHeapType::Any,
483+
};
484+
485+
/// Alias for the unshared `func` heap type.
486+
pub const FUNC: Self = Self::Abstract {
487+
shared: false,
488+
ty: AbstractHeapType::Func,
489+
};
490+
491+
/// Alias for the unshared `extern` heap type.
492+
pub const EXTERN: Self = Self::Abstract {
493+
shared: false,
494+
ty: AbstractHeapType::Extern,
495+
};
496+
497+
/// Alias for the unshared `i31` heap type.
498+
pub const I31: Self = Self::Abstract {
499+
shared: false,
500+
ty: AbstractHeapType::I31,
501+
};
502+
}
503+
504+
impl Encode for HeapType {
505+
fn encode(&self, sink: &mut Vec<u8>) {
506+
match self {
507+
HeapType::Abstract { shared, ty } => {
508+
if *shared {
509+
sink.push(0x65);
510+
}
511+
ty.encode(sink);
512+
}
513+
// Note that this is encoded as a signed type rather than unsigned
514+
// as it's decoded as an s33
515+
HeapType::Concrete(i) => i64::from(*i).encode(sink),
516+
}
517+
}
518+
}
519+
520+
#[cfg(feature = "wasmparser")]
521+
impl TryFrom<wasmparser::HeapType> for HeapType {
522+
type Error = ();
523+
524+
fn try_from(heap_type: wasmparser::HeapType) -> Result<Self, Self::Error> {
525+
Ok(match heap_type {
526+
wasmparser::HeapType::Concrete(i) => HeapType::Concrete(i.as_module_index().ok_or(())?),
527+
wasmparser::HeapType::Abstract { shared, ty } => HeapType::Abstract {
528+
shared,
529+
ty: ty.into(),
530+
},
531+
})
532+
}
533+
}
534+
535+
/// An abstract heap type.
536+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
537+
pub enum AbstractHeapType {
408538
/// Untyped (any) function.
409539
Func,
410540

@@ -455,53 +585,46 @@ pub enum HeapType {
455585

456586
/// The abstract `noexn` heap type.
457587
NoExn,
458-
459-
/// A concrete Wasm-defined type at the given index.
460-
Concrete(u32),
461588
}
462589

463-
impl Encode for HeapType {
590+
impl Encode for AbstractHeapType {
464591
fn encode(&self, sink: &mut Vec<u8>) {
592+
use AbstractHeapType::*;
465593
match self {
466-
HeapType::Func => sink.push(0x70),
467-
HeapType::Extern => sink.push(0x6F),
468-
HeapType::Any => sink.push(0x6E),
469-
HeapType::None => sink.push(0x71),
470-
HeapType::NoExtern => sink.push(0x72),
471-
HeapType::NoFunc => sink.push(0x73),
472-
HeapType::Eq => sink.push(0x6D),
473-
HeapType::Struct => sink.push(0x6B),
474-
HeapType::Array => sink.push(0x6A),
475-
HeapType::I31 => sink.push(0x6C),
476-
HeapType::Exn => sink.push(0x69),
477-
HeapType::NoExn => sink.push(0x74),
478-
// Note that this is encoded as a signed type rather than unsigned
479-
// as it's decoded as an s33
480-
HeapType::Concrete(i) => i64::from(*i).encode(sink),
594+
Func => sink.push(0x70),
595+
Extern => sink.push(0x6F),
596+
Any => sink.push(0x6E),
597+
None => sink.push(0x71),
598+
NoExtern => sink.push(0x72),
599+
NoFunc => sink.push(0x73),
600+
Eq => sink.push(0x6D),
601+
Struct => sink.push(0x6B),
602+
Array => sink.push(0x6A),
603+
I31 => sink.push(0x6C),
604+
Exn => sink.push(0x69),
605+
NoExn => sink.push(0x74),
481606
}
482607
}
483608
}
484609

485610
#[cfg(feature = "wasmparser")]
486-
impl TryFrom<wasmparser::HeapType> for HeapType {
487-
type Error = ();
488-
489-
fn try_from(heap_type: wasmparser::HeapType) -> Result<Self, Self::Error> {
490-
Ok(match heap_type {
491-
wasmparser::HeapType::Concrete(i) => HeapType::Concrete(i.as_module_index().ok_or(())?),
492-
wasmparser::HeapType::Func => HeapType::Func,
493-
wasmparser::HeapType::Extern => HeapType::Extern,
494-
wasmparser::HeapType::Any => HeapType::Any,
495-
wasmparser::HeapType::None => HeapType::None,
496-
wasmparser::HeapType::NoExtern => HeapType::NoExtern,
497-
wasmparser::HeapType::NoFunc => HeapType::NoFunc,
498-
wasmparser::HeapType::Eq => HeapType::Eq,
499-
wasmparser::HeapType::Struct => HeapType::Struct,
500-
wasmparser::HeapType::Array => HeapType::Array,
501-
wasmparser::HeapType::I31 => HeapType::I31,
502-
wasmparser::HeapType::Exn => HeapType::Exn,
503-
wasmparser::HeapType::NoExn => HeapType::NoExn,
504-
})
611+
impl From<wasmparser::AbstractHeapType> for AbstractHeapType {
612+
fn from(value: wasmparser::AbstractHeapType) -> Self {
613+
use wasmparser::AbstractHeapType::*;
614+
match value {
615+
Func => AbstractHeapType::Func,
616+
Extern => AbstractHeapType::Extern,
617+
Any => AbstractHeapType::Any,
618+
None => AbstractHeapType::None,
619+
NoExtern => AbstractHeapType::NoExtern,
620+
NoFunc => AbstractHeapType::NoFunc,
621+
Eq => AbstractHeapType::Eq,
622+
Struct => AbstractHeapType::Struct,
623+
Array => AbstractHeapType::Array,
624+
I31 => AbstractHeapType::I31,
625+
Exn => AbstractHeapType::Exn,
626+
NoExn => AbstractHeapType::NoExn,
627+
}
505628
}
506629
}
507630

crates/wasm-mutate/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ workspace = true
1414
clap = { workspace = true, optional = true }
1515
thiserror = "1.0.28"
1616
wasmparser = { workspace = true }
17-
wasm-encoder = { workspace = true }
17+
wasm-encoder = { workspace = true, features = ["wasmparser"] }
1818
rand = { workspace = true }
1919
log = { workspace = true }
2020
egg = "0.6.0"

crates/wasm-mutate/src/module.rs

+4-12
Original file line numberDiff line numberDiff line change
@@ -80,19 +80,11 @@ pub fn map_ref_type(ref_ty: wasmparser::RefType) -> Result<RefType> {
8080
Ok(RefType {
8181
nullable: ref_ty.is_nullable(),
8282
heap_type: match ref_ty.heap_type() {
83-
wasmparser::HeapType::Func => HeapType::Func,
84-
wasmparser::HeapType::Extern => HeapType::Extern,
85-
wasmparser::HeapType::Any => HeapType::Any,
86-
wasmparser::HeapType::None => HeapType::None,
87-
wasmparser::HeapType::NoExtern => HeapType::NoExtern,
88-
wasmparser::HeapType::NoFunc => HeapType::NoFunc,
89-
wasmparser::HeapType::Eq => HeapType::Eq,
90-
wasmparser::HeapType::Struct => HeapType::Struct,
91-
wasmparser::HeapType::Array => HeapType::Array,
92-
wasmparser::HeapType::I31 => HeapType::I31,
93-
wasmparser::HeapType::Exn => HeapType::Exn,
94-
wasmparser::HeapType::NoExn => HeapType::NoExn,
9583
wasmparser::HeapType::Concrete(i) => HeapType::Concrete(i.as_module_index().unwrap()),
84+
wasmparser::HeapType::Abstract { shared, ty } => {
85+
let ty = ty.into();
86+
HeapType::Abstract { shared, ty }
87+
}
9688
},
9789
})
9890
}

crates/wasm-mutate/src/mutators/add_function.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use super::Mutator;
44
use crate::module::{PrimitiveTypeInfo, TypeInfo};
55
use crate::{Result, WasmMutate};
66
use rand::Rng;
7-
use wasm_encoder::{HeapType, Instruction, Module};
7+
use wasm_encoder::{AbstractHeapType, HeapType, Instruction, Module};
88

99
/// Mutator that adds new, empty functions to a Wasm module.
1010
#[derive(Clone, Copy)]
@@ -62,10 +62,16 @@ impl Mutator for AddFunctionMutator {
6262
func.instruction(&Instruction::V128Const(0));
6363
}
6464
PrimitiveTypeInfo::FuncRef => {
65-
func.instruction(&Instruction::RefNull(HeapType::Func));
65+
func.instruction(&Instruction::RefNull(HeapType::Abstract {
66+
shared: false,
67+
ty: AbstractHeapType::Func,
68+
}));
6669
}
6770
PrimitiveTypeInfo::ExternRef => {
68-
func.instruction(&Instruction::RefNull(HeapType::Extern));
71+
func.instruction(&Instruction::RefNull(HeapType::Abstract {
72+
shared: false,
73+
ty: AbstractHeapType::Extern,
74+
}));
6975
}
7076
PrimitiveTypeInfo::Empty => unreachable!(),
7177
}

crates/wasm-mutate/src/mutators/modify_const_exprs.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,14 @@ impl<'cfg, 'wasm> Translator for InitTranslator<'cfg, 'wasm> {
124124
} else {
125125
f64::from_bits(self.config.rng().gen())
126126
}),
127-
T::FUNCREF => CE::ref_null(wasm_encoder::HeapType::Func),
128-
T::EXTERNREF => CE::ref_null(wasm_encoder::HeapType::Extern),
127+
T::FUNCREF => CE::ref_null(wasm_encoder::HeapType::Abstract {
128+
shared: false,
129+
ty: wasm_encoder::AbstractHeapType::Func,
130+
}),
131+
T::EXTERNREF => CE::ref_null(wasm_encoder::HeapType::Abstract {
132+
shared: false,
133+
ty: wasm_encoder::AbstractHeapType::Func,
134+
}),
129135
T::Ref(_) => unimplemented!(),
130136
}
131137
} else {

crates/wasm-mutate/src/mutators/peephole/dfg.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -758,12 +758,12 @@ impl<'a> DFGBuilder {
758758
}
759759

760760
Operator::RefNull {
761-
hty: wasmparser::HeapType::Extern,
761+
hty: wasmparser::HeapType::EXTERN,
762762
} => {
763763
self.push_node(Lang::RefNull(RefType::Extern), idx);
764764
}
765765
Operator::RefNull {
766-
hty: wasmparser::HeapType::Func,
766+
hty: wasmparser::HeapType::FUNC,
767767
} => {
768768
self.push_node(Lang::RefNull(RefType::Func), idx);
769769
}

crates/wasm-mutate/src/mutators/peephole/eggsy/lang.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use egg::Id;
44
use std::fmt::{self, Display};
55
use std::str::FromStr;
6-
use wasm_encoder::HeapType;
6+
use wasm_encoder::{AbstractHeapType, HeapType};
77

88
/// This is a macro used to define the `Lang` enum.
99
///
@@ -1072,8 +1072,14 @@ pub enum RefType {
10721072
impl From<RefType> for HeapType {
10731073
fn from(rt: RefType) -> Self {
10741074
match rt {
1075-
RefType::Func => HeapType::Func,
1076-
RefType::Extern => HeapType::Extern,
1075+
RefType::Func => HeapType::Abstract {
1076+
shared: false,
1077+
ty: AbstractHeapType::Func,
1078+
},
1079+
RefType::Extern => HeapType::Abstract {
1080+
shared: false,
1081+
ty: AbstractHeapType::Extern,
1082+
},
10771083
}
10781084
}
10791085
}

crates/wasm-mutate/src/mutators/snip_function.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,10 @@ impl Mutator for SnipMutator {
6161
f.instruction(&Instruction::V128Const(0));
6262
}
6363
PrimitiveTypeInfo::FuncRef => {
64-
f.instruction(&Instruction::RefNull(HeapType::Func));
64+
f.instruction(&Instruction::RefNull(HeapType::FUNC));
6565
}
6666
PrimitiveTypeInfo::ExternRef => {
67-
f.instruction(&Instruction::RefNull(HeapType::Extern));
67+
f.instruction(&Instruction::RefNull(HeapType::EXTERN));
6868
}
6969
PrimitiveTypeInfo::Empty => {
7070
unreachable!()

0 commit comments

Comments
 (0)