Skip to content

Commit 76c0c48

Browse files
authored
Add support for the stack switching proposal (#1802)
* Stack switching feature flag * Stubs for stack switching operators. * Type structure * Stack switching instructions validation. * Simplify validation API * Wast support * Skip AssertSuspension during roundtrip testing * Fix bugs; particularly label resolution bug in resume tables. Add test file. * Add snapshots * Fix validation bugs * Fix validation bugs. Test interaction between continuation types and gc types. * Add more tests * Add sub_type_at_id to EmptyResources. Formatting. * Format * Comma * Fix validation bugs in cont.bind and switch. Extend validation testsuite. * Address Alex's feedback * Update signature of sub_type_at_id in EmptyResources * Format * [wast] Translate Cont and NoCont heap types. * Address Alex's second round of feedback. * Revert encoding of continuation types back to s33 to stay in sync with the reference interpreter.
1 parent 51ade65 commit 76c0c48

File tree

160 files changed

+12877
-33
lines changed

Some content is hidden

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

160 files changed

+12877
-33
lines changed

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

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,6 +1203,26 @@ pub enum Instruction<'a> {
12031203
array_type_index: u32,
12041204
},
12051205
RefI31Shared,
1206+
// Stack switching
1207+
ContNew(u32),
1208+
ContBind {
1209+
argument_index: u32,
1210+
result_index: u32,
1211+
},
1212+
Suspend(u32),
1213+
Resume {
1214+
cont_type_index: u32,
1215+
resume_table: Cow<'a, [Handle]>,
1216+
},
1217+
ResumeThrow {
1218+
cont_type_index: u32,
1219+
tag_index: u32,
1220+
resume_table: Cow<'a, [Handle]>,
1221+
},
1222+
Switch {
1223+
cont_type_index: u32,
1224+
tag_index: u32,
1225+
},
12061226
}
12071227

12081228
impl Encode for Instruction<'_> {
@@ -3695,6 +3715,48 @@ impl Encode for Instruction<'_> {
36953715
sink.push(0xFE);
36963716
sink.push(0x72);
36973717
}
3718+
Instruction::ContNew(type_index) => {
3719+
sink.push(0xE0);
3720+
type_index.encode(sink);
3721+
}
3722+
Instruction::ContBind {
3723+
argument_index,
3724+
result_index,
3725+
} => {
3726+
sink.push(0xE1);
3727+
argument_index.encode(sink);
3728+
result_index.encode(sink);
3729+
}
3730+
Instruction::Suspend(tag_index) => {
3731+
sink.push(0xE2);
3732+
tag_index.encode(sink);
3733+
}
3734+
Instruction::Resume {
3735+
cont_type_index,
3736+
ref resume_table,
3737+
} => {
3738+
sink.push(0xE3);
3739+
cont_type_index.encode(sink);
3740+
resume_table.encode(sink);
3741+
}
3742+
Instruction::ResumeThrow {
3743+
cont_type_index,
3744+
tag_index,
3745+
ref resume_table,
3746+
} => {
3747+
sink.push(0xE4);
3748+
cont_type_index.encode(sink);
3749+
tag_index.encode(sink);
3750+
resume_table.encode(sink);
3751+
}
3752+
Instruction::Switch {
3753+
cont_type_index,
3754+
tag_index,
3755+
} => {
3756+
sink.push(0xE5);
3757+
cont_type_index.encode(sink);
3758+
tag_index.encode(sink);
3759+
}
36983760
}
36993761
}
37003762
}
@@ -3733,6 +3795,29 @@ impl Encode for Catch {
37333795
}
37343796
}
37353797

3798+
#[derive(Clone, Debug)]
3799+
#[allow(missing_docs)]
3800+
pub enum Handle {
3801+
OnLabel { tag: u32, label: u32 },
3802+
OnSwitch { tag: u32 },
3803+
}
3804+
3805+
impl Encode for Handle {
3806+
fn encode(&self, sink: &mut Vec<u8>) {
3807+
match self {
3808+
Handle::OnLabel { tag, label } => {
3809+
sink.push(0x00);
3810+
tag.encode(sink);
3811+
label.encode(sink);
3812+
}
3813+
Handle::OnSwitch { tag } => {
3814+
sink.push(0x01);
3815+
tag.encode(sink);
3816+
}
3817+
}
3818+
}
3819+
}
3820+
37363821
/// A constant expression.
37373822
///
37383823
/// Usable in contexts such as offsets or initializers.

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ pub enum CompositeInnerType {
3131
Array(ArrayType),
3232
/// The type is for a struct.
3333
Struct(StructType),
34+
/// The type is for a continuation.
35+
Cont(ContType),
3436
}
3537

3638
/// Represents a type of a function in a WebAssembly module.
@@ -88,6 +90,10 @@ impl StorageType {
8890
}
8991
}
9092

93+
/// Represents a type of a continuation in a WebAssembly module.
94+
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
95+
pub struct ContType(pub u32);
96+
9197
/// The type of a core WebAssembly value.
9298
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
9399
pub enum ValType {
@@ -443,6 +449,12 @@ pub enum AbstractHeapType {
443449

444450
/// The abstract `noexn` heap type.
445451
NoExn,
452+
453+
/// The abstract `cont` heap type.
454+
Cont,
455+
456+
/// The abstract `nocont` heap type.
457+
NoCont,
446458
}
447459

448460
impl Encode for AbstractHeapType {
@@ -461,6 +473,8 @@ impl Encode for AbstractHeapType {
461473
I31 => sink.push(0x6C),
462474
Exn => sink.push(0x69),
463475
NoExn => sink.push(0x74),
476+
Cont => sink.push(0x68),
477+
NoCont => sink.push(0x75),
464478
}
465479
}
466480
}
@@ -611,6 +625,11 @@ impl<'a> CoreTypeEncoder<'a> {
611625
}
612626
}
613627

628+
fn encode_cont(&mut self, ty: &ContType) {
629+
self.bytes.push(0x5d);
630+
i64::from(ty.0).encode(self.bytes);
631+
}
632+
614633
/// Define an explicit subtype in this type section.
615634
pub fn subtype(mut self, ty: &SubType) {
616635
self.encode_subtype(ty)
@@ -643,6 +662,7 @@ impl<'a> CoreTypeEncoder<'a> {
643662
self.encode_array(&ty.element_type, ty.mutable)
644663
}
645664
CompositeInnerType::Struct(ty) => self.encode_struct(ty.fields.iter().cloned()),
665+
CompositeInnerType::Cont(ty) => self.encode_cont(ty),
646666
}
647667
}
648668

crates/wasm-encoder/src/reencode.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,13 +127,24 @@ pub trait Reencode {
127127
utils::func_type(self, func_ty)
128128
}
129129

130+
fn cont_type(
131+
&mut self,
132+
cont_ty: wasmparser::ContType,
133+
) -> Result<crate::ContType, Error<Self::Error>> {
134+
utils::cont_type(self, cont_ty)
135+
}
136+
130137
fn global_type(
131138
&mut self,
132139
global_ty: wasmparser::GlobalType,
133140
) -> Result<crate::GlobalType, Error<Self::Error>> {
134141
utils::global_type(self, global_ty)
135142
}
136143

144+
fn handle(&mut self, on: wasmparser::Handle) -> crate::Handle {
145+
utils::handle(self, on)
146+
}
147+
137148
fn heap_type(
138149
&mut self,
139150
heap_type: wasmparser::HeapType,
@@ -883,6 +894,21 @@ pub mod utils {
883894
}
884895
}
885896

897+
pub fn handle<T: ?Sized + Reencode>(
898+
reencoder: &mut T,
899+
arg: wasmparser::Handle,
900+
) -> crate::Handle {
901+
match arg {
902+
wasmparser::Handle::OnLabel { tag, label } => crate::Handle::OnLabel {
903+
tag: reencoder.tag_index(tag),
904+
label,
905+
},
906+
wasmparser::Handle::OnSwitch { tag } => crate::Handle::OnSwitch {
907+
tag: reencoder.tag_index(tag),
908+
},
909+
}
910+
}
911+
886912
/// Parses the input `section` given from the `wasmparser` crate and
887913
/// adds the custom section to the `module`.
888914
pub fn parse_custom_section<T: ?Sized + Reencode>(
@@ -989,6 +1015,8 @@ pub mod utils {
9891015
I31 => crate::AbstractHeapType::I31,
9901016
Exn => crate::AbstractHeapType::Exn,
9911017
NoExn => crate::AbstractHeapType::NoExn,
1018+
Cont => crate::AbstractHeapType::Cont,
1019+
NoCont => crate::AbstractHeapType::NoCont,
9921020
}
9931021
}
9941022

@@ -1052,6 +1080,9 @@ pub mod utils {
10521080
wasmparser::CompositeInnerType::Struct(s) => {
10531081
crate::CompositeInnerType::Struct(reencoder.struct_type(s)?)
10541082
}
1083+
wasmparser::CompositeInnerType::Cont(c) => {
1084+
crate::CompositeInnerType::Cont(reencoder.cont_type(c)?)
1085+
}
10551086
};
10561087
Ok(crate::CompositeType {
10571088
inner,
@@ -1114,6 +1145,15 @@ pub mod utils {
11141145
})
11151146
}
11161147

1148+
pub fn cont_type<T: ?Sized + Reencode>(
1149+
reencoder: &mut T,
1150+
cont_ty: wasmparser::ContType,
1151+
) -> Result<crate::ContType, Error<T::Error>> {
1152+
Ok(crate::ContType(
1153+
reencoder.type_index_unpacked(cont_ty.0.unpack())?,
1154+
))
1155+
}
1156+
11171157
pub fn val_type<T: ?Sized + Reencode>(
11181158
reencoder: &mut T,
11191159
val_ty: wasmparser::ValType,
@@ -1555,6 +1595,12 @@ pub mod utils {
15551595
(map $arg:ident array_size) => ($arg);
15561596
(map $arg:ident field_index) => ($arg);
15571597
(map $arg:ident try_table) => ($arg);
1598+
(map $arg:ident argument_index) => (reencoder.type_index($arg));
1599+
(map $arg:ident result_index) => (reencoder.type_index($arg));
1600+
(map $arg:ident cont_type_index) => (reencoder.type_index($arg));
1601+
(map $arg:ident resume_table) => ((
1602+
$arg.handlers.into_iter().map(|h| reencoder.handle(h)).collect::<Vec<_>>().into()
1603+
));
15581604

15591605
// This case takes the arguments of a wasmparser instruction and creates
15601606
// a wasm-encoder instruction. There are a few special cases for where
@@ -1785,6 +1831,12 @@ impl TryFrom<wasmparser::GlobalType> for crate::GlobalType {
17851831
}
17861832
}
17871833

1834+
impl From<wasmparser::Handle> for crate::Handle {
1835+
fn from(arg: wasmparser::Handle) -> Self {
1836+
RoundtripReencoder.handle(arg)
1837+
}
1838+
}
1839+
17881840
impl TryFrom<wasmparser::TypeRef> for crate::EntityType {
17891841
type Error = Error;
17901842

crates/wasm-smith/src/core.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,7 @@ impl Module {
797797
Extern => {
798798
choices.push(ht(NoExtern));
799799
}
800-
Exn | NoExn | None | NoExtern | NoFunc => {}
800+
Exn | NoExn | None | NoExtern | NoFunc | Cont | NoCont => {}
801801
}
802802
}
803803
HT::Concrete(idx) => {
@@ -918,7 +918,10 @@ impl Module {
918918
Eq => {
919919
choices.push(ht(Any));
920920
}
921-
Exn | Any | Func | Extern => {}
921+
NoCont => {
922+
choices.push(ht(Cont));
923+
}
924+
Exn | Any | Func | Extern | Cont => {}
922925
}
923926
}
924927
HT::Concrete(mut idx) => {

crates/wasmparser/src/binary_reader.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,15 @@ impl<'a> BinaryReader<'a> {
10891089
0xd5 => visitor.visit_br_on_null(self.read_var_u32()?),
10901090
0xd6 => visitor.visit_br_on_non_null(self.read_var_u32()?),
10911091

1092+
0xe0 => visitor.visit_cont_new(self.read_var_u32()?),
1093+
0xe1 => visitor.visit_cont_bind(self.read_var_u32()?, self.read_var_u32()?),
1094+
0xe2 => visitor.visit_suspend(self.read_var_u32()?),
1095+
0xe3 => visitor.visit_resume(self.read_var_u32()?, self.read()?),
1096+
0xe4 => {
1097+
visitor.visit_resume_throw(self.read_var_u32()?, self.read_var_u32()?, self.read()?)
1098+
}
1099+
0xe5 => visitor.visit_switch(self.read_var_u32()?, self.read_var_u32()?),
1100+
10921101
0xfb => self.visit_0xfb_operator(pos, visitor)?,
10931102
0xfc => self.visit_0xfc_operator(pos, visitor)?,
10941103
0xfd => self.visit_0xfd_operator(pos, visitor)?,

crates/wasmparser/src/features.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,8 @@ define_wasm_features! {
225225
/// feature. Those are expected to not require a full garbage collector
226226
/// so are not gated by this.
227227
pub gc_types: GC_TYPES(1 << 26) = true;
228+
/// The WebAssembly [stack-switching proposal](https://github.com/WebAssembly/stack-switching).
229+
pub stack_switching: STACK_SWITCHING(1 << 27) = false;
228230
}
229231
}
230232

crates/wasmparser/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,14 @@ macro_rules! for_each_operator {
803803
@function_references RefAsNonNull => visit_ref_as_non_null
804804
@function_references BrOnNull { relative_depth: u32 } => visit_br_on_null
805805
@function_references BrOnNonNull { relative_depth: u32 } => visit_br_on_non_null
806+
807+
// Stack switching
808+
@stack_switching ContNew { cont_type_index: u32 } => visit_cont_new
809+
@stack_switching ContBind { argument_index: u32, result_index: u32 } => visit_cont_bind
810+
@stack_switching Suspend { tag_index: u32 } => visit_suspend
811+
@stack_switching Resume { cont_type_index: u32, resume_table: $crate::ResumeTable } => visit_resume
812+
@stack_switching ResumeThrow { cont_type_index: u32, tag_index: u32, resume_table: $crate::ResumeTable } => visit_resume_throw
813+
@stack_switching Switch { cont_type_index: u32, tag_index: u32 } => visit_switch
806814
}
807815
};
808816
}

crates/wasmparser/src/limits.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ pub const MAX_WASM_BR_TABLE_SIZE: usize = MAX_WASM_FUNCTION_SIZE;
3939
pub const MAX_WASM_STRUCT_FIELDS: usize = 10_000;
4040
pub const MAX_WASM_CATCHES: usize = 10_000;
4141
pub const MAX_WASM_SUBTYPING_DEPTH: usize = 63;
42+
pub const MAX_WASM_HANDLERS: usize = 10_000;
4243

4344
pub const DEFAULT_WASM_PAGE_SIZE: u64 = 1 << 16;
4445

0 commit comments

Comments
 (0)