Skip to content

Commit 071c406

Browse files
authored
winch: implement ref.null, ref.is_null, ref.func, and typed select (#12940)
* winch: implement ref.null, ref.is_null, ref.func, and typed select * add disas tests and ref.func call_indirect coverage * register wasmtime module in fuzz wast_test to fix wast_smoke_test
1 parent dad8432 commit 071c406

9 files changed

Lines changed: 286 additions & 16 deletions

File tree

crates/fuzzing/src/oracles.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,7 @@ pub fn wast_test(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<()> {
772772
suppress_prints: true,
773773
})
774774
.unwrap();
775+
wast_context.register_wasmtime().unwrap();
775776
wast_context
776777
.run_wast(test.path.to_str().unwrap(), test.contents.as_bytes())
777778
.unwrap();

crates/test-util/src/wast.rs

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -507,20 +507,6 @@ impl WastTest {
507507
"misc_testsuite/no-mixup-stack-maps.wast",
508508
"misc_testsuite/no-panic.wast",
509509
"misc_testsuite/simple_ref_is_null.wast",
510-
"misc_testsuite/table_grow_with_funcref.wast",
511-
"spec_testsuite/br_table.wast",
512-
"spec_testsuite/global.wast",
513-
"spec_testsuite/ref_func.wast",
514-
"spec_testsuite/ref_is_null.wast",
515-
"spec_testsuite/ref_null.wast",
516-
"spec_testsuite/select.wast",
517-
"spec_testsuite/table_fill.wast",
518-
"spec_testsuite/table_get.wast",
519-
"spec_testsuite/table_grow.wast",
520-
"spec_testsuite/table_set.wast",
521-
"spec_testsuite/table_size.wast",
522-
"spec_testsuite/elem.wast",
523-
"spec_testsuite/linking.wast",
524510
];
525511

526512
if unsupported.iter().any(|part| self.path.ends_with(part)) {

tests/all/func.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1425,7 +1425,7 @@ fn wrap_multiple_results(config: &mut Config) -> wasmtime::Result<()> {
14251425
}
14261426
}
14271427

1428-
#[wasmtime_test(wasm_features(reference_types, gc_types, bulk_memory))]
1428+
#[wasmtime_test(wasm_features(reference_types, bulk_memory))]
14291429
#[cfg_attr(miri, ignore)]
14301430
fn trampoline_for_declared_elem(config: &mut Config) -> wasmtime::Result<()> {
14311431
let engine = Engine::new(&config)?;

tests/disas/winch/x64/ref/func.wat

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
;;! target = "x86_64"
2+
;;! test = "winch"
3+
4+
(module
5+
(elem declare func $f)
6+
(func $f)
7+
(func (export "ref-func") (result funcref)
8+
(ref.func $f)
9+
)
10+
)
11+
;; wasm[0]::function[0]::f:
12+
;; pushq %rbp
13+
;; movq %rsp, %rbp
14+
;; movq 8(%rdi), %r11
15+
;; movq 0x18(%r11), %r11
16+
;; addq $0x10, %r11
17+
;; cmpq %rsp, %r11
18+
;; ja 0x38
19+
;; 1c: movq %rdi, %r14
20+
;; subq $0x10, %rsp
21+
;; movq %rdi, 8(%rsp)
22+
;; movq %rsi, (%rsp)
23+
;; addq $0x10, %rsp
24+
;; popq %rbp
25+
;; retq
26+
;; 38: ud2
27+
;;
28+
;; wasm[0]::function[1]:
29+
;; pushq %rbp
30+
;; movq %rsp, %rbp
31+
;; movq 8(%rdi), %r11
32+
;; movq 0x18(%r11), %r11
33+
;; addq $0x10, %r11
34+
;; cmpq %rsp, %r11
35+
;; ja 0x8a
36+
;; 5c: movq %rdi, %r14
37+
;; subq $0x10, %rsp
38+
;; movq %rdi, 8(%rsp)
39+
;; movq %rsi, (%rsp)
40+
;; movq %r14, %rdi
41+
;; movl $0, %esi
42+
;; callq 0x252
43+
;; movq 8(%rsp), %r14
44+
;; addq $0x10, %rsp
45+
;; popq %rbp
46+
;; retq
47+
;; 8a: ud2
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
;;! target = "x86_64"
2+
;;! test = "winch"
3+
4+
(module
5+
(func (export "ref-is-null") (param funcref) (result i32)
6+
(ref.is_null (local.get 0))
7+
)
8+
)
9+
;; wasm[0]::function[0]:
10+
;; pushq %rbp
11+
;; movq %rsp, %rbp
12+
;; movq 8(%rdi), %r11
13+
;; movq 0x18(%r11), %r11
14+
;; addq $0x20, %r11
15+
;; cmpq %rsp, %r11
16+
;; ja 0x4f
17+
;; 1c: movq %rdi, %r14
18+
;; subq $0x20, %rsp
19+
;; movq %rdi, 0x18(%rsp)
20+
;; movq %rsi, 0x10(%rsp)
21+
;; movq %rdx, 8(%rsp)
22+
;; movq 8(%rsp), %rax
23+
;; cmpq $0, %rax
24+
;; movl $0, %eax
25+
;; sete %al
26+
;; addq $0x20, %rsp
27+
;; popq %rbp
28+
;; retq
29+
;; 4f: ud2

tests/disas/winch/x64/ref/null.wat

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
;;! target = "x86_64"
2+
;;! test = "winch"
3+
4+
(module
5+
(func (export "ref-null") (result funcref)
6+
(ref.null func)
7+
)
8+
)
9+
;; wasm[0]::function[0]:
10+
;; pushq %rbp
11+
;; movq %rsp, %rbp
12+
;; movq 8(%rdi), %r11
13+
;; movq 0x18(%r11), %r11
14+
;; addq $0x10, %r11
15+
;; cmpq %rsp, %r11
16+
;; ja 0x3d
17+
;; 1c: movq %rdi, %r14
18+
;; subq $0x10, %rsp
19+
;; movq %rdi, 8(%rsp)
20+
;; movq %rsi, (%rsp)
21+
;; movl $0, %eax
22+
;; addq $0x10, %rsp
23+
;; popq %rbp
24+
;; retq
25+
;; 3d: ud2
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
;;! target = "x86_64"
2+
;;! test = "winch"
3+
4+
(module
5+
(func (export "typed-select") (param funcref funcref i32) (result funcref)
6+
(select (result funcref) (local.get 0) (local.get 1) (local.get 2))
7+
)
8+
)
9+
;; wasm[0]::function[0]:
10+
;; pushq %rbp
11+
;; movq %rsp, %rbp
12+
;; movq 8(%rdi), %r11
13+
;; movq 0x18(%r11), %r11
14+
;; addq $0x30, %r11
15+
;; cmpq %rsp, %r11
16+
;; ja 0x60
17+
;; 1c: movq %rdi, %r14
18+
;; subq $0x30, %rsp
19+
;; movq %rdi, 0x28(%rsp)
20+
;; movq %rsi, 0x20(%rsp)
21+
;; movq %rdx, 0x18(%rsp)
22+
;; movq %rcx, 0x10(%rsp)
23+
;; movl %r8d, 0xc(%rsp)
24+
;; movl 0xc(%rsp), %eax
25+
;; movq 0x10(%rsp), %rcx
26+
;; movq 0x18(%rsp), %rdx
27+
;; cmpl $0, %eax
28+
;; cmovneq %rdx, %rcx
29+
;; movq %rcx, %rax
30+
;; addq $0x30, %rsp
31+
;; popq %rbp
32+
;; retq
33+
;; 60: ud2
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
;;! reference_types = true
2+
3+
(module
4+
(type $i32-func (func (result i32)))
5+
6+
(func $returns-42 (type $i32-func) (i32.const 42))
7+
(func $returns-7 (type $i32-func) (i32.const 7))
8+
(func $dummy)
9+
10+
(table $t 10 funcref)
11+
(elem (table $t) (i32.const 0) func $returns-42 $returns-7)
12+
13+
;; ref.null func / ref.is_null
14+
(func (export "null-is-null") (result i32)
15+
(ref.is_null (ref.null func))
16+
)
17+
18+
;; ref.func + ref.is_null
19+
(func (export "func-is-not-null") (result i32)
20+
(ref.is_null (ref.func $returns-42))
21+
)
22+
23+
;; ref.func + call_indirect
24+
(func (export "call-indirect-0") (result i32)
25+
(call_indirect $t (type $i32-func) (i32.const 0))
26+
)
27+
(func (export "call-indirect-1") (result i32)
28+
(call_indirect $t (type $i32-func) (i32.const 1))
29+
)
30+
(func (export "call-indirect-null") (result i32)
31+
(call_indirect $t (type $i32-func) (i32.const 9))
32+
)
33+
34+
;; typed select between two funcrefs
35+
(func (export "select-funcref") (param $c i32) (result funcref)
36+
(select (result funcref) (ref.func $returns-42) (ref.func $returns-7) (local.get $c))
37+
)
38+
(func (export "select-null-first") (param $c i32) (result funcref)
39+
(select (result funcref) (ref.null func) (ref.func $returns-7) (local.get $c))
40+
)
41+
42+
;; table.get / table.set
43+
(func (export "table-get") (param $i i32) (result funcref)
44+
(table.get $t (local.get $i))
45+
)
46+
(func (export "table-set-null") (param $i i32)
47+
(table.set $t (local.get $i) (ref.null func))
48+
)
49+
(func (export "table-set-ref") (param $i i32)
50+
(table.set $t (local.get $i) (ref.func $returns-42))
51+
)
52+
53+
;; ref.func -> table.set -> call_indirect
54+
(func (export "set-and-call") (result i32)
55+
(table.set $t (i32.const 5) (ref.func $returns-42))
56+
(call_indirect $t (type $i32-func) (i32.const 5))
57+
)
58+
59+
;; table.grow
60+
(func (export "table-grow") (param $n i32) (result i32)
61+
(table.grow $t (ref.null func) (local.get $n))
62+
)
63+
(func (export "table-size") (result i32)
64+
(table.size $t)
65+
)
66+
)
67+
68+
;; ref.null / ref.is_null
69+
(assert_return (invoke "null-is-null") (i32.const 1))
70+
(assert_return (invoke "func-is-not-null") (i32.const 0))
71+
72+
;; call_indirect through elem-populated table
73+
(assert_return (invoke "call-indirect-0") (i32.const 42))
74+
(assert_return (invoke "call-indirect-1") (i32.const 7))
75+
(assert_trap (invoke "call-indirect-null") "uninitialized element")
76+
77+
;; typed select
78+
(assert_return (invoke "select-funcref" (i32.const 1)) (ref.func 0))
79+
(assert_return (invoke "select-funcref" (i32.const 0)) (ref.func 1))
80+
(assert_return (invoke "select-null-first" (i32.const 1)) (ref.null func))
81+
(assert_return (invoke "select-null-first" (i32.const 0)) (ref.func 1))
82+
83+
;; ref.func -> table.set -> call_indirect
84+
(assert_return (invoke "set-and-call") (i32.const 42))
85+
86+
;; table.get
87+
(assert_return (invoke "table-get" (i32.const 0)) (ref.func 0))
88+
(assert_return (invoke "table-get" (i32.const 1)) (ref.func 1))
89+
(assert_return (invoke "table-get" (i32.const 2)) (ref.null func))
90+
(assert_trap (invoke "table-get" (i32.const 10)) "out of bounds table access")
91+
92+
;; table.set
93+
(assert_return (invoke "table-set-null" (i32.const 0)))
94+
(assert_return (invoke "table-get" (i32.const 0)) (ref.null func))
95+
(assert_return (invoke "table-set-ref" (i32.const 0)))
96+
(assert_return (invoke "table-get" (i32.const 0)) (ref.func 0))
97+
(assert_trap (invoke "table-set-null" (i32.const 10)) "out of bounds table access")
98+
99+
;; table.grow
100+
(assert_return (invoke "table-size") (i32.const 10))
101+
(assert_return (invoke "table-grow" (i32.const 5)) (i32.const 10))
102+
(assert_return (invoke "table-size") (i32.const 15))

winch/codegen/src/visitor.rs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ use crate::{Result, bail, ensure, format_err};
2323
use regalloc2::RegClass;
2424
use smallvec::{SmallVec, smallvec};
2525
use wasmparser::{
26-
BlockType, BrTable, Ieee32, Ieee64, MemArg, V128, VisitOperator, VisitSimdOperator,
26+
BlockType, BrTable, HeapType, Ieee32, Ieee64, MemArg, V128, ValType, VisitOperator,
27+
VisitSimdOperator,
2728
};
2829
use wasmtime_cranelift::TRAP_INDIRECT_CALL_TO_NULL;
2930
use wasmtime_environ::{
@@ -203,6 +204,10 @@ macro_rules! def_unsupported {
203204
(emit GlobalGet $($rest:tt)*) => {};
204205
(emit GlobalSet $($rest:tt)*) => {};
205206
(emit Select $($rest:tt)*) => {};
207+
(emit TypedSelect $($rest:tt)*) => {};
208+
(emit RefNull $($rest:tt)*) => {};
209+
(emit RefIsNull $($rest:tt)*) => {};
210+
(emit RefFunc $($rest:tt)*) => {};
206211
(emit Drop $($rest:tt)*) => {};
207212
(emit BrTable $($rest:tt)*) => {};
208213
(emit CallIndirect $($rest:tt)*) => {};
@@ -2207,6 +2212,48 @@ where
22072212
Ok(())
22082213
}
22092214

2215+
fn visit_typed_select(&mut self, _ty: ValType) -> Self::Output {
2216+
self.visit_select()
2217+
}
2218+
2219+
fn visit_ref_null(&mut self, hty: HeapType) -> Self::Output {
2220+
match hty {
2221+
HeapType::FUNC => {
2222+
let ptr_type = self.env.ptr_type();
2223+
match ptr_type {
2224+
WasmValType::I64 => self.context.stack.push(Val::i64(0)),
2225+
WasmValType::I32 => self.context.stack.push(Val::i32(0)),
2226+
_ => bail!(CodeGenError::unsupported_wasm_type()),
2227+
}
2228+
Ok(())
2229+
}
2230+
_ => Err(format_err!(CodeGenError::unsupported_wasm_type())),
2231+
}
2232+
}
2233+
2234+
fn visit_ref_is_null(&mut self) -> Self::Output {
2235+
let (zero, size) = match self.env.ptr_type() {
2236+
WasmValType::I64 => (RegImm::i64(0), OperandSize::S64),
2237+
WasmValType::I32 => (RegImm::i32(0), OperandSize::S32),
2238+
_ => bail!(CodeGenError::unsupported_wasm_type()),
2239+
};
2240+
self.context.unop(self.masm, |masm, reg| {
2241+
masm.cmp_with_set(writable!(reg), zero, IntCmpKind::Eq, size)?;
2242+
Ok(TypedReg::i32(reg))
2243+
})
2244+
}
2245+
2246+
fn visit_ref_func(&mut self, function_index: u32) -> Self::Output {
2247+
let ref_func = self.env.builtins.ref_func::<M::ABI>()?;
2248+
self.context.stack.extend([function_index.try_into()?]);
2249+
FnCall::emit::<M>(
2250+
&mut self.env,
2251+
self.masm,
2252+
&mut self.context,
2253+
Callee::Builtin(ref_func),
2254+
)
2255+
}
2256+
22102257
fn visit_i32_load(&mut self, memarg: MemArg) -> Self::Output {
22112258
self.emit_wasm_load(
22122259
&memarg,

0 commit comments

Comments
 (0)