wasmtime_environ/gc.rs
1//! Target- and pointer-width-agnostic definitions of GC-related types and
2//! constants.
3//!
4//! These definitions are suitable for use both during compilation and at
5//! runtime.
6//!
7//! Note: We don't bother gating these on `cfg(feature = "gc")` because that
8//! makes downstream uses pretty annoying, and the primary thing we want to gate
9//! on our various `gc` cargo features is the actual garbage collection
10//! functions and their associated impact on binary size anyways.
11
12/// Discriminant to check whether GC reference is an `i31ref` or not.
13pub const I31_DISCRIMINANT: u64 = 1;
14
15/// A mask that can be used to check for non-null and non-i31ref GC references
16/// with a single bitwise-and operation.
17pub const NON_NULL_NON_I31_MASK: u64 = !I31_DISCRIMINANT;
18
19/// The kind of an object in a GC heap.
20///
21/// Note that this type is accessed from Wasm JIT code.
22///
23/// `VMGcKind` is a bitset where to test if `a` is a subtype of an
24/// "abstract-ish" type `b`, we can simply use a single bitwise-and operation:
25///
26/// ```ignore
27/// a <: b iff a & b == b
28/// ```
29///
30/// For example, because `VMGcKind::AnyRef` has the high bit set, every kind
31/// representing some subtype of `anyref` also has its high bit set.
32///
33/// We say "abstract-ish" type because in addition to the abstract heap types
34/// (other than `i31`) we also have variants for `externref`s that have been
35/// converted into an `anyref` via `extern.convert_any` and `externref`s that
36/// have been converted into an `anyref` via `any.convert_extern`. Note that in
37/// the latter case, because `any.convert_extern $foo` produces a value that is
38/// not an instance of `eqref`, `VMGcKind::AnyOfExternRef & VMGcKind::EqRef !=
39/// VMGcKind::EqRef`.
40///
41/// Furthermore, this type only uses the highest 6 bits of its `u32`
42/// representation, allowing the lower 26 bytes to be bitpacked with other stuff
43/// as users see fit.
44#[repr(u32)]
45#[derive(Clone, Copy, Debug, PartialEq, Eq)]
46#[rustfmt::skip]
47#[allow(missing_docs)]
48pub enum VMGcKind {
49 ExternRef = 0b010000 << 26,
50 ExternOfAnyRef = 0b011000 << 26,
51 AnyRef = 0b100000 << 26,
52 AnyOfExternRef = 0b100100 << 26,
53 EqRef = 0b101000 << 26,
54 ArrayRef = 0b101001 << 26,
55 StructRef = 0b101010 << 26,
56}
57
58impl VMGcKind {
59 /// Mask this value with a `u32` to get just the bits that `VMGcKind` uses.
60 pub const MASK: u32 = 0b111111 << 26;
61
62 /// Mask this value with a `u32` that potentially contains a `VMGcKind` to
63 /// get the bits that `VMGcKind` doesn't use.
64 pub const UNUSED_MASK: u32 = !Self::MASK;
65
66 /// Convert the given value into a `VMGcKind` by masking off the unused
67 /// bottom bits.
68 pub fn from_high_bits_of_u32(val: u32) -> VMGcKind {
69 let masked = val & Self::MASK;
70 match masked {
71 x if x == Self::ExternRef as u32 => Self::ExternRef,
72 x if x == Self::ExternOfAnyRef as u32 => Self::ExternOfAnyRef,
73 x if x == Self::AnyRef as u32 => Self::AnyRef,
74 x if x == Self::AnyOfExternRef as u32 => Self::AnyOfExternRef,
75 x if x == Self::EqRef as u32 => Self::EqRef,
76 x if x == Self::ArrayRef as u32 => Self::ArrayRef,
77 x if x == Self::StructRef as u32 => Self::StructRef,
78 _ => panic!("invalid `VMGcKind`: {masked:#032b}"),
79 }
80 }
81
82 /// Does this kind match the other kind?
83 ///
84 /// That is, is this kind a subtype of the other kind?
85 pub fn matches(self, other: Self) -> bool {
86 (self as u32) & (other as u32) == (other as u32)
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::VMGcKind::*;
93 use crate::prelude::*;
94
95 #[test]
96 fn kind_matches() {
97 let all = [
98 ExternRef,
99 ExternOfAnyRef,
100 AnyRef,
101 AnyOfExternRef,
102 EqRef,
103 ArrayRef,
104 StructRef,
105 ];
106
107 for (sup, subs) in [
108 (ExternRef, vec![ExternOfAnyRef]),
109 (ExternOfAnyRef, vec![]),
110 (AnyRef, vec![AnyOfExternRef, EqRef, ArrayRef, StructRef]),
111 (AnyOfExternRef, vec![]),
112 (EqRef, vec![ArrayRef, StructRef]),
113 (ArrayRef, vec![]),
114 (StructRef, vec![]),
115 ] {
116 assert!(sup.matches(sup));
117 for sub in &subs {
118 assert!(sub.matches(sup));
119 }
120 for kind in all.iter().filter(|k| **k != sup && !subs.contains(k)) {
121 assert!(!kind.matches(sup));
122 }
123 }
124 }
125}