Skip to content

Commit 83cddcd

Browse files
chore: add initial typed reference types types
Signed-off-by: Henry <mail@henrygressmann.de>
1 parent d848efe commit 83cddcd

4 files changed

Lines changed: 179 additions & 134 deletions

File tree

‎crates/types/src/instructions.rs‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::{FuncAddr, GlobalAddr, LocalAddr, TableAddr, TypeAddr, ValueCounts, WasmType};
2-
use crate::{ConstIdx, DataAddr, ElemAddr, ExternAddr, MemAddr};
2+
use crate::{ConstIdx, DataAddr, ElemAddr, MemAddr, RefValue};
33

44
/// Represents a memory immediate in a WebAssembly memory instruction.
55
#[derive(Copy, Clone, PartialEq, Eq)]
@@ -53,8 +53,7 @@ pub enum ConstInstruction {
5353
F64Const(f64),
5454
V128Const([u8; 16]),
5555
GlobalGet(GlobalAddr),
56-
RefFunc(Option<FuncAddr>),
57-
RefExtern(Option<ExternAddr>),
56+
Ref(RefValue),
5857
I32Add,
5958
I32Sub,
6059
I32Mul,

‎crates/types/src/lib.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ impl<'a> FromIterator<&'a WasmType> for ValueCounts {
408408

409409
for ty in iter {
410410
match ty {
411-
WasmType::I32 | WasmType::F32 | WasmType::RefExtern | WasmType::RefFunc => counts.c32 += 1,
411+
WasmType::I32 | WasmType::F32 | WasmType::Ref(_) => counts.c32 += 1,
412412
WasmType::I64 | WasmType::F64 => counts.c64 += 1,
413413
WasmType::V128 => counts.c128 += 1,
414414
}
@@ -511,7 +511,7 @@ pub struct TableType {
511511

512512
impl TableType {
513513
pub const fn empty() -> Self {
514-
Self::new(WasmType::RefFunc, 0, None)
514+
Self::new(WasmType::Ref(RefType::FUNCREF), 0, None)
515515
}
516516

517517
/// Create a table with 32-bit indices.

‎crates/types/src/reference.rs‎

Lines changed: 143 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,185 @@
1-
use crate::{ExternAddr, FuncAddr};
2-
3-
const NULL_REF: u32 = u32::MAX;
1+
/// An abstract WebAssembly heap type.
2+
///
3+
/// This contains exactly the abstract heap types in core Wasm 3.0.
4+
#[repr(u8)]
5+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
6+
#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
7+
pub enum AbstractHeapType {
8+
Any,
9+
Eq,
10+
I31,
11+
Struct,
12+
Array,
13+
None,
14+
15+
Func,
16+
NoFunc,
17+
18+
Exn,
19+
NoExn,
20+
21+
Extern,
22+
NoExtern,
23+
}
424

5-
#[derive(Clone, Copy, PartialEq, Eq)]
6-
pub struct ExternRef(u32);
7-
8-
#[derive(Clone, Copy, PartialEq, Eq)]
9-
pub struct FuncRef(u32);
25+
/// A WebAssembly reference type.
26+
///
27+
/// Packed as:
28+
///
29+
/// ```text
30+
/// [nullable:1 concrete:1 payload:30]
31+
/// ```
32+
///
33+
/// For concrete types, `payload` is a module type index.
34+
/// Otherwise, it is an [`AbstractHeapType`].
35+
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
36+
#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
37+
pub struct RefType(u32);
1038

1139
#[cfg(feature = "debug")]
12-
impl core::fmt::Debug for ExternRef {
40+
impl core::fmt::Debug for RefType {
1341
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
14-
match self.addr() {
15-
Some(addr) => write!(f, "extern({addr:?})"),
16-
None => write!(f, "extern(null)"),
42+
if self.is_concrete() {
43+
write!(f, "concrete({})", self.type_index().unwrap())
44+
} else {
45+
write!(f, "abstract({:?})", self.abstract_heap_type().unwrap())
1746
}
1847
}
1948
}
2049

21-
#[cfg(feature = "debug")]
22-
impl core::fmt::Debug for FuncRef {
23-
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
24-
match self.addr() {
25-
Some(addr) => write!(f, "func({addr:?})"),
26-
None => write!(f, "func(null)"),
27-
}
50+
impl RefType {
51+
const NULLABLE: u32 = 1 << 31;
52+
const CONCRETE: u32 = 1 << 30;
53+
const PAYLOAD_MASK: u32 = Self::CONCRETE - 1;
54+
55+
pub const FUNCREF: Self = Self::abstract_(true, AbstractHeapType::Func);
56+
pub const EXTERNREF: Self = Self::abstract_(true, AbstractHeapType::Extern);
57+
pub const EXNREF: Self = Self::abstract_(true, AbstractHeapType::Exn);
58+
59+
#[inline]
60+
pub const fn abstract_(nullable: bool, heap_type: AbstractHeapType) -> Self {
61+
Self((nullable as u32) << 31 | heap_type as u32)
2862
}
29-
}
3063

31-
impl FuncRef {
3264
#[inline]
33-
/// Create a new [`FuncRef`] from a [`FuncAddr`].
34-
pub const fn new(addr: Option<FuncAddr>) -> Self {
35-
match addr {
36-
Some(addr) => Self(addr),
37-
None => Self::null(),
65+
pub const fn concrete(nullable: bool, type_index: u32) -> Option<Self> {
66+
if type_index <= Self::PAYLOAD_MASK {
67+
Some(Self(((nullable as u32) << 31) | Self::CONCRETE | type_index))
68+
} else {
69+
None
3870
}
3971
}
4072

4173
#[inline]
42-
/// Create a null [`FuncRef`].
43-
pub const fn null() -> Self {
44-
Self(NULL_REF)
74+
pub const fn is_nullable(self) -> bool {
75+
self.0 & Self::NULLABLE != 0
4576
}
4677

4778
#[inline]
48-
/// Check if the [`FuncRef`] is null.
49-
pub const fn is_null(&self) -> bool {
50-
self.0 == NULL_REF
79+
pub const fn is_concrete(self) -> bool {
80+
self.0 & Self::CONCRETE != 0
5181
}
5282

5383
#[inline]
54-
/// Get the [`FuncAddr`] from the [`FuncRef`].
55-
pub const fn addr(&self) -> Option<FuncAddr> {
56-
if self.is_null() { None } else { Some(self.0) }
84+
pub const fn type_index(self) -> Option<u32> {
85+
if self.is_concrete() { Some(self.0 & Self::PAYLOAD_MASK) } else { None }
5786
}
5887

5988
#[inline]
60-
#[doc(hidden)]
61-
pub const fn from_raw(raw: u32) -> Self {
62-
Self(raw)
89+
pub const fn abstract_heap_type(self) -> Option<AbstractHeapType> {
90+
if self.is_concrete() {
91+
return None;
92+
}
93+
94+
match self.0 & Self::PAYLOAD_MASK {
95+
0 => Some(AbstractHeapType::Any),
96+
1 => Some(AbstractHeapType::Eq),
97+
2 => Some(AbstractHeapType::I31),
98+
3 => Some(AbstractHeapType::Struct),
99+
4 => Some(AbstractHeapType::Array),
100+
5 => Some(AbstractHeapType::None),
101+
6 => Some(AbstractHeapType::Func),
102+
7 => Some(AbstractHeapType::NoFunc),
103+
8 => Some(AbstractHeapType::Exn),
104+
9 => Some(AbstractHeapType::NoExn),
105+
10 => Some(AbstractHeapType::Extern),
106+
11 => Some(AbstractHeapType::NoExtern),
107+
_ => None,
108+
}
63109
}
64110

65111
#[inline]
66-
#[doc(hidden)]
67-
pub const fn raw(&self) -> u32 {
68-
self.0
112+
pub const fn with_nullability(self, nullable: bool) -> Self {
113+
Self((self.0 & !Self::NULLABLE) | ((nullable as u32) << 31))
69114
}
70115
}
71116

72-
impl ExternRef {
73-
#[inline]
74-
/// Create a new [`ExternRef`] from an [`ExternAddr`].
75-
/// Should only be used by the runtime.
76-
pub const fn new(addr: Option<ExternAddr>) -> Self {
77-
match addr {
78-
Some(addr) => Self(addr),
79-
None => Self::null(),
117+
#[derive(Clone, Copy, PartialEq, Eq)]
118+
#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
119+
pub enum RefValue {
120+
Null,
121+
Func(FuncRef),
122+
Extern(ExternRef),
123+
Any(AnyRef),
124+
Exn(ExnRef),
125+
}
126+
127+
#[cfg(feature = "debug")]
128+
impl core::fmt::Debug for RefValue {
129+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
130+
match self {
131+
Self::Null => write!(f, "null"),
132+
Self::Func(_i) => write!(f, "func()"),
133+
Self::Extern(_i) => write!(f, "extern()"),
134+
Self::Any(_i) => write!(f, "any()"),
135+
Self::Exn(_i) => write!(f, "exn()"),
80136
}
81137
}
138+
}
82139

83-
/// Create a null [`ExternRef`].
84-
#[inline]
85-
pub const fn null() -> Self {
86-
Self(NULL_REF)
87-
}
140+
#[derive(Clone, Copy, PartialEq, Eq)]
141+
#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
142+
pub struct FuncRef(u32);
88143

89-
/// Check if the [`ExternRef`] is null.
90-
#[inline]
91-
pub const fn is_null(&self) -> bool {
92-
self.0 == NULL_REF
144+
#[derive(Clone, Copy, PartialEq, Eq)]
145+
#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
146+
pub struct ExternRef(u32);
147+
148+
#[derive(Clone, Copy, PartialEq, Eq)]
149+
#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
150+
pub struct ExnRef(u32);
151+
152+
#[derive(Clone, Copy, PartialEq, Eq)]
153+
#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
154+
pub struct AnyRef(u32);
155+
156+
impl AnyRef {
157+
// Odd values are inline i31s.
158+
// Even values are GC object handles.
159+
160+
pub const fn from_i31(value: i32) -> Option<Self> {
161+
if value < -(1 << 30) || value >= (1 << 30) {
162+
return None;
163+
}
164+
165+
Some(Self(((value as u32) << 1) | 1))
93166
}
94167

95-
/// Get the [`ExternAddr`] from the [`ExternRef`].
96-
#[inline]
97-
pub const fn addr(&self) -> Option<ExternAddr> {
98-
if self.is_null() { None } else { Some(self.0) }
168+
pub const fn as_i31(self) -> Option<i32> {
169+
if self.0 & 1 == 1 { Some((self.0 as i32) >> 1) } else { None }
99170
}
100171

101-
#[inline]
102-
#[doc(hidden)]
103-
pub const fn from_raw(raw: u32) -> Self {
104-
Self(raw)
172+
pub const fn from_gc_addr(addr: u32) -> Option<Self> {
173+
match addr.checked_add(1) {
174+
Some(raw) => match raw.checked_mul(2) {
175+
Some(raw) => Some(Self(raw)),
176+
None => None,
177+
},
178+
None => None,
179+
}
105180
}
106181

107-
#[inline]
108-
#[doc(hidden)]
109-
pub const fn raw(&self) -> u32 {
110-
self.0
182+
pub const fn gc_addr(self) -> Option<u32> {
183+
if self.0 != 0 && self.0 & 1 == 0 { Some(self.0 / 2 - 1) } else { None }
111184
}
112185
}

0 commit comments

Comments
 (0)