Skip to content

Commit e67d2bf

Browse files
authored
Add wasmparser support for ref.{test,cast} (#1332)
* Add `wasmparser` support for `ref.{test,cast}` This also splits `wasm-encoder::Intruction::Ref{Test,Cast}` into nullable and non-nullable forms, since that more directly aligns with the binary format where there are actually different opcodes for nullable vs non-nullable variations (as opposed to a bit on some immediate or something). * Add copy of subset of `ref.{test,cast}` tests Not all are passing yet, but this is more related to our type canonicalization and match checking than `ref.{test,cast}`. So in the meantime, check that the subset of the spec tests that are passing continue to pass. Once we get all of them passing we can enable the spec tests and delete this partial copy. * more concise * fill out todo message
1 parent a73ed59 commit e67d2bf

Some content is hidden

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

47 files changed

+1709
-38
lines changed

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

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{encode_section, Encode, HeapType, RefType, Section, SectionId, ValType};
1+
use crate::{encode_section, Encode, HeapType, Section, SectionId, ValType};
22
use std::borrow::Cow;
33

44
/// An encoder for the code section.
@@ -546,8 +546,10 @@ pub enum Instruction<'a> {
546546
ArrayInitData(u32, u32),
547547
ArrayInitElem(u32, u32),
548548

549-
RefTest(RefType),
550-
RefCast(RefType),
549+
RefTestNonNull(HeapType),
550+
RefTestNullable(HeapType),
551+
RefCastNonNull(HeapType),
552+
RefCastNullable(HeapType),
551553
AnyConvertExtern,
552554
ExternConvertAny,
553555

@@ -1459,34 +1461,22 @@ impl Encode for Instruction<'_> {
14591461
type_index.encode(sink);
14601462
elem_index.encode(sink);
14611463
}
1462-
Instruction::RefTest(RefType {
1463-
nullable: false,
1464-
heap_type,
1465-
}) => {
1464+
Instruction::RefTestNonNull(heap_type) => {
14661465
sink.push(0xfb);
14671466
sink.push(0x14);
14681467
heap_type.encode(sink);
14691468
}
1470-
Instruction::RefTest(RefType {
1471-
nullable: true,
1472-
heap_type,
1473-
}) => {
1469+
Instruction::RefTestNullable(heap_type) => {
14741470
sink.push(0xfb);
14751471
sink.push(0x15);
14761472
heap_type.encode(sink);
14771473
}
1478-
Instruction::RefCast(RefType {
1479-
nullable: false,
1480-
heap_type,
1481-
}) => {
1474+
Instruction::RefCastNonNull(heap_type) => {
14821475
sink.push(0xfb);
14831476
sink.push(0x16);
14841477
heap_type.encode(sink);
14851478
}
1486-
Instruction::RefCast(RefType {
1487-
nullable: true,
1488-
heap_type,
1489-
}) => {
1479+
Instruction::RefCastNullable(heap_type) => {
14901480
sink.push(0xfb);
14911481
sink.push(0x17);
14921482
heap_type.encode(sink);

crates/wasm-shrink/tests/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ fn shrink_to_empty_is_error() -> Result<()> {
2929
let result = WasmShrink::default().run(wasm(), |_| Ok(true));
3030
assert!(result.is_err());
3131
let err_msg = result.err().unwrap().to_string();
32-
assert!(dbg!(err_msg).contains("empty Wasm module"));
32+
assert!(err_msg.contains("empty Wasm module"));
3333
Ok(())
3434
}
3535

crates/wasmparser/src/binary_reader.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,6 +1001,11 @@ impl<'a> BinaryReader<'a> {
10011001
{
10021002
let code = self.read_var_u32()?;
10031003
Ok(match code {
1004+
0x14 => visitor.visit_ref_test_non_null(self.read()?),
1005+
0x15 => visitor.visit_ref_test_nullable(self.read()?),
1006+
0x16 => visitor.visit_ref_cast_non_null(self.read()?),
1007+
0x17 => visitor.visit_ref_cast_nullable(self.read()?),
1008+
10041009
0x1c => visitor.visit_ref_i31(),
10051010
0x1d => visitor.visit_i31_get_s(),
10061011
0x1e => visitor.visit_i31_get_u(),

crates/wasmparser/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,10 @@ macro_rules! for_each_operator {
319319
@gc RefI31 => visit_ref_i31
320320
@gc I31GetS => visit_i31_get_s
321321
@gc I31GetU => visit_i31_get_u
322+
@gc RefTestNonNull { hty: $crate::HeapType } => visit_ref_test_non_null
323+
@gc RefTestNullable { hty: $crate::HeapType } => visit_ref_test_nullable
324+
@gc RefCastNonNull { hty: $crate::HeapType } => visit_ref_cast_non_null
325+
@gc RefCastNullable { hty: $crate::HeapType } => visit_ref_cast_nullable
322326

323327
// 0xFC operators
324328
// Non-trapping Float-to-int Conversions

crates/wasmparser/src/validator/core.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -596,7 +596,7 @@ impl Module {
596596
bail!(offset, "sub type cannot have a final super type");
597597
}
598598
if !types.matches(id, sup_id) {
599-
bail!(offset, "subtype must match supertype");
599+
bail!(offset, "sub type must match super type");
600600
}
601601
}
602602

crates/wasmparser/src/validator/operators.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,50 @@ where
970970
Ok(())
971971
}
972972

973+
/// Common helper for `ref.test` and `ref.cast` downcasting/checking
974+
/// instructions. Returns the given `heap_type` as a `ValType`.
975+
fn check_downcast(&mut self, nullable: bool, mut heap_type: HeapType) -> Result<ValType> {
976+
self.resources
977+
.check_heap_type(&mut heap_type, self.offset)?;
978+
979+
let sub_ty = RefType::new(nullable, heap_type)
980+
.map(ValType::from)
981+
.ok_or_else(|| {
982+
BinaryReaderError::new("implementation limit: type index too large", self.offset)
983+
})?;
984+
985+
let sup_ty = match self.pop_ref()? {
986+
None => bail!(
987+
self.offset,
988+
"type mismatch: expected (ref null? ...), found bottom"
989+
),
990+
Some(ty) => ty,
991+
};
992+
993+
if !self.resources.is_subtype(sub_ty, sup_ty.into()) {
994+
bail!(
995+
self.offset,
996+
"ref.test's heap type must be a sub type of the type on the stack"
997+
);
998+
}
999+
1000+
Ok(sub_ty)
1001+
}
1002+
1003+
/// Common helper for both nullable and non-nullable variants of `ref.test`
1004+
/// instructions.
1005+
fn check_ref_test(&mut self, nullable: bool, heap_type: HeapType) -> Result<()> {
1006+
self.check_downcast(nullable, heap_type)?;
1007+
self.push_operand(ValType::I32)
1008+
}
1009+
1010+
/// Common helper for both nullable and non-nullable variants of `ref.cast`
1011+
/// instructions.
1012+
fn check_ref_cast(&mut self, nullable: bool, heap_type: HeapType) -> Result<()> {
1013+
let sub_ty = self.check_downcast(nullable, heap_type)?;
1014+
self.push_operand(sub_ty)
1015+
}
1016+
9731017
fn func_type_at(&self, at: u32) -> Result<&'resources R::FuncType> {
9741018
self.resources
9751019
.func_type_at(at)
@@ -3387,6 +3431,18 @@ where
33873431
self.pop_operand(Some(ValType::I32))?;
33883432
Ok(())
33893433
}
3434+
fn visit_ref_test_non_null(&mut self, heap_type: HeapType) -> Self::Output {
3435+
self.check_ref_test(false, heap_type)
3436+
}
3437+
fn visit_ref_test_nullable(&mut self, heap_type: HeapType) -> Self::Output {
3438+
self.check_ref_test(true, heap_type)
3439+
}
3440+
fn visit_ref_cast_non_null(&mut self, heap_type: HeapType) -> Self::Output {
3441+
self.check_ref_cast(false, heap_type)
3442+
}
3443+
fn visit_ref_cast_nullable(&mut self, heap_type: HeapType) -> Self::Output {
3444+
self.check_ref_cast(true, heap_type)
3445+
}
33903446
fn visit_ref_i31(&mut self) -> Self::Output {
33913447
self.pop_operand(Some(ValType::I32))?;
33923448
self.push_operand(ValType::Ref(RefType::I31))

crates/wasmprinter/src/operator.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use super::{Printer, State};
2-
use anyhow::{bail, Result};
2+
use anyhow::{anyhow, bail, Result};
33
use std::fmt::Write;
4-
use wasmparser::{BlockType, BrTable, MemArg, VisitOperator};
4+
use wasmparser::{BlockType, BrTable, MemArg, RefType, VisitOperator};
55

66
pub struct PrintOperator<'a, 'b> {
77
pub(super) printer: &'a mut Printer,
@@ -316,6 +316,30 @@ macro_rules! define_visit {
316316
)?;
317317
}
318318
);
319+
(payload $self:ident RefTestNonNull $hty:ident) => (
320+
$self.push_str(" ");
321+
let rty = RefType::new(false, $hty)
322+
.ok_or_else(|| anyhow!("implementation limit: type index too large"))?;
323+
$self.printer.print_reftype(rty)?;
324+
);
325+
(payload $self:ident RefTestNullable $hty:ident) => (
326+
$self.push_str(" ");
327+
let rty = RefType::new(true, $hty)
328+
.ok_or_else(|| anyhow!("implementation limit: type index too large"))?;
329+
$self.printer.print_reftype(rty)?;
330+
);
331+
(payload $self:ident RefCastNonNull $hty:ident) => (
332+
$self.push_str(" ");
333+
let rty = RefType::new(false, $hty)
334+
.ok_or_else(|| anyhow!("implementation limit: type index too large"))?;
335+
$self.printer.print_reftype(rty)?;
336+
);
337+
(payload $self:ident RefCastNullable $hty:ident) => (
338+
$self.push_str(" ");
339+
let rty = RefType::new(true, $hty)
340+
.ok_or_else(|| anyhow!("implementation limit: type index too large"))?;
341+
$self.printer.print_reftype(rty)?;
342+
);
319343
(payload $self:ident $op:ident $($arg:ident)*) => (
320344
$(
321345
$self.push_str(" ");
@@ -861,6 +885,10 @@ macro_rules! define_visit {
861885
(name I16x8RelaxedQ15mulrS) => ("i16x8.relaxed_q15mulr_s");
862886
(name I16x8RelaxedDotI8x16I7x16S) => ("i16x8.relaxed_dot_i8x16_i7x16_s");
863887
(name I32x4RelaxedDotI8x16I7x16AddS) => ("i32x4.relaxed_dot_i8x16_i7x16_add_s");
888+
(name RefTestNonNull) => ("ref.test");
889+
(name RefTestNullable) => ("ref.test");
890+
(name RefCastNonNull) => ("ref.cast");
891+
(name RefCastNullable) => ("ref.cast");
864892
(name RefI31) => ("ref.i31");
865893
(name I31GetS) => ("i31.get_s");
866894
(name I31GetU) => ("i31.get_u");

tests/local/gc/gc-rec-groups-invalid.wast

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,4 @@
2727
(type $t4 (sub $t2 (struct (field (ref $t3)))))
2828
)
2929
)
30-
"subtype must match supertype")
30+
"sub type must match super type")

tests/local/gc/gc-subtypes-invalid.wast

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,21 @@
2020
(type $a (sub (func)))
2121
(type $b (sub $a (struct))) ;; invalid
2222
)
23-
"subtype must match supertype"
23+
"sub type must match super type"
2424
)
2525
(assert_invalid
2626
(module
2727
(type $a (sub (func)))
2828
(type $b (sub $a (func (param i32)))) ;; invalid
2929
)
30-
"subtype must match supertype"
30+
"sub type must match super type"
3131
)
3232
(assert_invalid
3333
(module
3434
(type $a (sub (struct (field i32))))
3535
(type $b (sub $a (struct (field i64)))) ;; invalid
3636
)
37-
"subtype must match supertype"
37+
"sub type must match super type"
3838
)
3939
(assert_invalid
4040
(module
@@ -45,21 +45,21 @@
4545
(type $g (sub (func (param (ref $e)) (result (ref $e)))))
4646
(type $i (sub $g (func (param (ref $f)) (result (ref $d))))) ;; invalid
4747
)
48-
"subtype must match supertype"
48+
"sub type must match super type"
4949
)
5050
(assert_invalid
5151
(module
5252
(type $o (sub (array i32)))
5353
(type (sub $o (array (mut i32)))) ;; invalid
5454
)
55-
"subtype must match supertype"
55+
"sub type must match super type"
5656
)
5757
(assert_invalid
5858
(module
5959
(type $o (sub (array i32)))
6060
(type (sub $o (array i64))) ;; invalid
6161
)
62-
"subtype must match supertype"
62+
"sub type must match super type"
6363
)
6464
(assert_invalid
6565
(module
@@ -68,7 +68,7 @@
6868
(type $s (sub $r (array (ref i31))))
6969
(type (sub $s (array (ref null i31)))) ;; invalid
7070
)
71-
"subtype must match supertype"
71+
"sub type must match super type"
7272
)
7373
(assert_invalid
7474
(module
@@ -77,7 +77,7 @@
7777
(type $ss (sub $rr (array (ref array))))
7878
(type (sub $ss (array (ref null array)))) ;; invalid
7979
)
80-
"subtype must match supertype"
80+
"sub type must match super type"
8181
)
8282
(assert_invalid
8383
(module
@@ -86,31 +86,31 @@
8686
(type $sss (sub $rrr (array (ref struct))))
8787
(type (sub $sss (array (ref null struct)))) ;; invalid
8888
)
89-
"subtype must match supertype"
89+
"sub type must match super type"
9090
)
9191
(assert_invalid
9292
(module
9393
(type $t (sub (array (mut funcref))))
9494
(type $u (sub $t (array (ref null func))))
9595
(type (sub $u (array (mut (ref func))))) ;; invalid
9696
)
97-
"subtype must match supertype"
97+
"sub type must match super type"
9898
)
9999
(assert_invalid
100100
(module
101101
(type $t (sub (array (mut funcref))))
102102
(type $u (sub $t (array (ref null func))))
103103
(type (sub $u (array (ref null extern)))) ;; invalid
104104
)
105-
"subtype must match supertype"
105+
"sub type must match super type"
106106
)
107107
(assert_invalid
108108
(module
109109
(type $t0 (sub (array (mut externref))))
110110
(type $u0 (sub $t0 (array (ref null extern))))
111111
(type (sub $u0 (array (mut (ref extern))))) ;; invalid
112112
)
113-
"subtype must match supertype"
113+
"sub type must match super type"
114114
)
115115
(assert_invalid
116116
(module
@@ -119,14 +119,14 @@
119119
(type $v0 (sub $u0 (array (ref extern))))
120120
(type (sub $v0 (array nullexternref))) ;; invalid
121121
)
122-
"subtype must match supertype"
122+
"sub type must match super type"
123123
)
124124
(assert_invalid
125125
(module
126126
(type $t (sub (array (mut funcref))))
127127
(type (sub $t (array nullexternref))) ;; invalid
128128
)
129-
"subtype must match supertype"
129+
"sub type must match super type"
130130
)
131131
(assert_invalid
132132
(module

0 commit comments

Comments
 (0)