wasmtime_environ/
key.rs

1//! Keys for identifying functions during compilation, in call graphs, and when
2//! resolving relocations.
3
4#[cfg(feature = "component-model")]
5use crate::component;
6use crate::{
7    BuiltinFunctionIndex, DefinedFuncIndex, HostCall, ModuleInternedTypeIndex, StaticModuleIndex,
8};
9use core::{cmp, fmt};
10use serde_derive::{Deserialize, Serialize};
11
12/// The kind of a function that is being compiled, linked, or otherwise
13/// referenced.
14///
15/// This is like a `FuncKey` but without any payload values.
16#[repr(u32)]
17#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
18#[cfg_attr(test, derive(arbitrary::Arbitrary))]
19pub enum FuncKeyKind {
20    /// A Wasm-defined function.
21    DefinedWasmFunction = FuncKey::new_kind(0b0000),
22
23    /// A trampoline from an array-caller to the given Wasm-callee.
24    ArrayToWasmTrampoline = FuncKey::new_kind(0b0001),
25
26    /// A trampoline from a Wasm-caller to an array-callee of the given type.
27    WasmToArrayTrampoline = FuncKey::new_kind(0b0010),
28
29    /// A trampoline from a Wasm-caller to the given builtin.
30    WasmToBuiltinTrampoline = FuncKey::new_kind(0b0011),
31
32    /// A trampoline from the patchable ABI to the given builtin.
33    PatchableToBuiltinTrampoline = FuncKey::new_kind(0b0100),
34
35    /// A Pulley-specific host call.
36    PulleyHostCall = FuncKey::new_kind(0b0101),
37
38    /// A Wasm-caller to component builtin trampoline.
39    #[cfg(feature = "component-model")]
40    ComponentTrampoline = FuncKey::new_kind(0b0110),
41
42    /// A Wasm-caller to array-callee `resource.drop` trampoline.
43    #[cfg(feature = "component-model")]
44    ResourceDropTrampoline = FuncKey::new_kind(0b0111),
45
46    /// A Wasmtime unsafe intrinsic function.
47    #[cfg(feature = "component-model")]
48    UnsafeIntrinsic = FuncKey::new_kind(0b1000),
49}
50
51impl From<FuncKeyKind> for u32 {
52    fn from(kind: FuncKeyKind) -> Self {
53        kind as u32
54    }
55}
56
57impl FuncKeyKind {
58    /// Get this kind's raw representation.
59    pub fn into_raw(self) -> u32 {
60        self.into()
61    }
62
63    /// Construct a `FuncKind` from its raw representation.
64    ///
65    /// Panics when given invalid raw representations.
66    pub fn from_raw(raw: u32) -> Self {
67        match raw {
68            x if x == Self::DefinedWasmFunction.into() => Self::DefinedWasmFunction,
69            x if x == Self::ArrayToWasmTrampoline.into() => Self::ArrayToWasmTrampoline,
70            x if x == Self::WasmToArrayTrampoline.into() => Self::WasmToArrayTrampoline,
71            x if x == Self::WasmToBuiltinTrampoline.into() => Self::WasmToBuiltinTrampoline,
72            x if x == Self::PatchableToBuiltinTrampoline.into() => {
73                Self::PatchableToBuiltinTrampoline
74            }
75            x if x == Self::PulleyHostCall.into() => Self::PulleyHostCall,
76
77            #[cfg(feature = "component-model")]
78            x if x == Self::ComponentTrampoline.into() => Self::ComponentTrampoline,
79            #[cfg(feature = "component-model")]
80            x if x == Self::ResourceDropTrampoline.into() => Self::ResourceDropTrampoline,
81            #[cfg(feature = "component-model")]
82            x if x == Self::UnsafeIntrinsic.into() => Self::UnsafeIntrinsic,
83
84            _ => panic!("invalid raw value passed to `FuncKind::from_raw`: {raw}"),
85        }
86    }
87}
88
89/// The namespace half of a `FuncKey`.
90///
91/// This is an opaque combination of the key's kind and module index, if any.
92#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
93pub struct FuncKeyNamespace(u32);
94
95impl fmt::Debug for FuncKeyNamespace {
96    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97        struct Hex<T: fmt::LowerHex>(T);
98        impl<T: fmt::LowerHex> fmt::Debug for Hex<T> {
99            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100                write!(f, "{:#x}", self.0)
101            }
102        }
103        f.debug_struct("FuncKeyNamespace")
104            .field("raw", &Hex(self.0))
105            .field("kind", &self.kind())
106            .field("module", &self.module())
107            .finish()
108    }
109}
110
111impl From<FuncKeyNamespace> for u32 {
112    fn from(ns: FuncKeyNamespace) -> Self {
113        ns.0
114    }
115}
116
117impl FuncKeyNamespace {
118    /// Get this `FuncNamespace`'s raw representation.
119    pub fn into_raw(self) -> u32 {
120        self.0
121    }
122
123    /// Construct a `FuncNamespace` from its raw representation.
124    ///
125    /// Panics when given invalid raw representations.
126    pub fn from_raw(raw: u32) -> Self {
127        match FuncKeyKind::from_raw(raw & FuncKey::KIND_MASK) {
128            FuncKeyKind::DefinedWasmFunction | FuncKeyKind::ArrayToWasmTrampoline => Self(raw),
129            FuncKeyKind::WasmToArrayTrampoline
130            | FuncKeyKind::WasmToBuiltinTrampoline
131            | FuncKeyKind::PatchableToBuiltinTrampoline
132            | FuncKeyKind::PulleyHostCall => {
133                assert_eq!(raw & FuncKey::MODULE_MASK, 0);
134                Self(raw)
135            }
136
137            #[cfg(feature = "component-model")]
138            FuncKeyKind::ComponentTrampoline => {
139                let _ = Abi::from_raw(raw & FuncKey::MODULE_MASK);
140                Self(raw)
141            }
142
143            #[cfg(feature = "component-model")]
144            FuncKeyKind::ResourceDropTrampoline => {
145                assert_eq!(raw & FuncKey::MODULE_MASK, 0);
146                Self(raw)
147            }
148
149            #[cfg(feature = "component-model")]
150            FuncKeyKind::UnsafeIntrinsic => {
151                let _ = Abi::from_raw(raw & FuncKey::MODULE_MASK);
152                Self(raw)
153            }
154        }
155    }
156
157    /// Get this `FuncNamespace`'s kind.
158    pub fn kind(&self) -> FuncKeyKind {
159        let raw = self.0 & FuncKey::KIND_MASK;
160        FuncKeyKind::from_raw(raw)
161    }
162
163    fn module(&self) -> u32 {
164        self.0 & FuncKey::MODULE_MASK
165    }
166}
167
168/// The index half of a `FuncKey`.
169#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
170pub struct FuncKeyIndex(u32);
171
172impl From<FuncKeyIndex> for u32 {
173    fn from(index: FuncKeyIndex) -> Self {
174        index.0
175    }
176}
177
178impl FuncKeyIndex {
179    /// Get this index's raw representation.
180    pub fn into_raw(self) -> u32 {
181        self.0
182    }
183
184    /// Construct a `FuncKeyIndex` from its raw representation.
185    ///
186    /// Invalid raw representations will not be caught eagerly, but will cause
187    /// panics when paired with a `FuncKeyNamespace` to create a whole
188    /// `FuncKey`.
189    pub fn from_raw(raw: u32) -> Self {
190        FuncKeyIndex(raw)
191    }
192}
193
194/// ABI signature of functions that are generated here.
195#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
196#[cfg_attr(test, derive(arbitrary::Arbitrary))]
197pub enum Abi {
198    /// The "wasm" ABI, or suitable to be a `wasm_call` field of a `VMFuncRef`.
199    Wasm = 0,
200    /// The "array" ABI, or suitable to be an `array_call` field.
201    Array = 1,
202    /// The "patchable" ABI. Signature same as Wasm ABI, but
203    /// Cranelift-level (machine-level) ABI is different (no
204    /// clobbers).
205    Patchable = 2,
206}
207
208#[cfg(feature = "component-model")]
209impl Abi {
210    fn from_raw(raw: u32) -> Self {
211        match raw {
212            x if x == Self::Wasm.into_raw() => Self::Wasm,
213            x if x == Self::Array.into_raw() => Self::Array,
214            x if x == Self::Patchable.into_raw() => Self::Patchable,
215            _ => panic!("invalid raw representation passed to `Abi::from_raw`: {raw}"),
216        }
217    }
218
219    fn into_raw(self) -> u32 {
220        (self as u8).into()
221    }
222}
223
224/// A sortable, comparable function key for compilation output, call graph
225/// edges, and relocations.
226#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
227pub enum FuncKey {
228    /// A Wasm-defined function.
229    DefinedWasmFunction(StaticModuleIndex, DefinedFuncIndex),
230
231    /// A trampoline from an array-caller to the given Wasm-callee.
232    ArrayToWasmTrampoline(StaticModuleIndex, DefinedFuncIndex),
233
234    /// A trampoline from a Wasm-caller to an array-callee of the given type.
235    WasmToArrayTrampoline(ModuleInternedTypeIndex),
236
237    /// A trampoline from a Wasm-caller to the given builtin.
238    WasmToBuiltinTrampoline(BuiltinFunctionIndex),
239
240    /// A Pulley-specific host call.
241    PulleyHostCall(HostCall),
242
243    /// A trampoline from the patchable ABI to the given builtin.
244    PatchableToBuiltinTrampoline(BuiltinFunctionIndex),
245
246    /// A Wasm-caller to component builtin trampoline.
247    #[cfg(feature = "component-model")]
248    ComponentTrampoline(Abi, component::TrampolineIndex),
249
250    /// A Wasm-caller to array-callee `resource.drop` trampoline.
251    #[cfg(feature = "component-model")]
252    ResourceDropTrampoline,
253
254    /// A Wasmtime intrinsic function.
255    #[cfg(feature = "component-model")]
256    UnsafeIntrinsic(Abi, component::UnsafeIntrinsic),
257}
258
259impl Ord for FuncKey {
260    fn cmp(&self, other: &Self) -> cmp::Ordering {
261        // Make sure to sort by our raw parts, because `CompiledFunctionsTable`
262        // relies on this for its binary search tables.
263        let raw_self = self.into_raw_parts();
264        let raw_other = other.into_raw_parts();
265        raw_self.cmp(&raw_other)
266    }
267}
268
269impl PartialOrd for FuncKey {
270    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
271        Some(self.cmp(other))
272    }
273}
274
275impl FuncKey {
276    const KIND_BITS: u32 = 4;
277    const KIND_OFFSET: u32 = 32 - Self::KIND_BITS;
278    const KIND_MASK: u32 = ((1 << Self::KIND_BITS) - 1) << Self::KIND_OFFSET;
279    const MODULE_MASK: u32 = !Self::KIND_MASK;
280
281    const fn new_kind(kind: u32) -> u32 {
282        assert!(kind < (1 << Self::KIND_BITS));
283        kind << Self::KIND_OFFSET
284    }
285
286    /// Split this key into its namespace and index halves.
287    #[inline]
288    pub fn into_parts(self) -> (FuncKeyNamespace, FuncKeyIndex) {
289        let (namespace, index) = match self {
290            FuncKey::DefinedWasmFunction(module, def_func) => {
291                assert_eq!(module.as_u32() & Self::KIND_MASK, 0);
292                let namespace = FuncKeyKind::DefinedWasmFunction.into_raw() | module.as_u32();
293                let index = def_func.as_u32();
294                (namespace, index)
295            }
296            FuncKey::ArrayToWasmTrampoline(module, def_func) => {
297                assert_eq!(module.as_u32() & Self::KIND_MASK, 0);
298                let namespace = FuncKeyKind::ArrayToWasmTrampoline.into_raw() | module.as_u32();
299                let index = def_func.as_u32();
300                (namespace, index)
301            }
302            FuncKey::WasmToArrayTrampoline(ty) => {
303                let namespace = FuncKeyKind::WasmToArrayTrampoline.into_raw();
304                let index = ty.as_u32();
305                (namespace, index)
306            }
307            FuncKey::WasmToBuiltinTrampoline(builtin) => {
308                let namespace = FuncKeyKind::WasmToBuiltinTrampoline.into_raw();
309                let index = builtin.index();
310                (namespace, index)
311            }
312            FuncKey::PatchableToBuiltinTrampoline(builtin) => {
313                let namespace = FuncKeyKind::PatchableToBuiltinTrampoline.into_raw();
314                let index = builtin.index();
315                (namespace, index)
316            }
317            FuncKey::PulleyHostCall(host_call) => {
318                let namespace = FuncKeyKind::PulleyHostCall.into_raw();
319                let index = host_call.index();
320                (namespace, index)
321            }
322
323            #[cfg(feature = "component-model")]
324            FuncKey::ComponentTrampoline(abi, trampoline) => {
325                let abi = abi.into_raw();
326                assert_eq!(abi & Self::KIND_MASK, 0);
327                let namespace = FuncKeyKind::ComponentTrampoline.into_raw() | abi;
328                let index = trampoline.as_u32();
329                (namespace, index)
330            }
331            #[cfg(feature = "component-model")]
332            FuncKey::ResourceDropTrampoline => {
333                let namespace = FuncKeyKind::ResourceDropTrampoline.into_raw();
334                let index = 0;
335                (namespace, index)
336            }
337            #[cfg(feature = "component-model")]
338            FuncKey::UnsafeIntrinsic(abi, intrinsic) => {
339                let abi = abi.into_raw();
340                assert_eq!(abi & Self::KIND_MASK, 0);
341                let namespace = FuncKeyKind::UnsafeIntrinsic.into_raw() | abi;
342                let index = intrinsic.index();
343                (namespace, index)
344            }
345        };
346        (FuncKeyNamespace(namespace), FuncKeyIndex(index))
347    }
348
349    /// Get this key's kind.
350    pub fn kind(self) -> FuncKeyKind {
351        self.namespace().kind()
352    }
353
354    /// Get this key's namespace.
355    pub fn namespace(self) -> FuncKeyNamespace {
356        self.into_parts().0
357    }
358
359    /// Get this key's index.
360    pub fn index(self) -> FuncKeyIndex {
361        self.into_parts().1
362    }
363
364    /// Get ABI of the function that this key is defining.
365    pub fn abi(self) -> Abi {
366        match self {
367            FuncKey::DefinedWasmFunction(_, _) => Abi::Wasm,
368            FuncKey::ArrayToWasmTrampoline(_, _) => Abi::Array,
369            FuncKey::WasmToArrayTrampoline(_) => Abi::Wasm,
370            FuncKey::WasmToBuiltinTrampoline(_) => Abi::Wasm,
371            FuncKey::PatchableToBuiltinTrampoline(_) => Abi::Patchable,
372            FuncKey::PulleyHostCall(_) => Abi::Wasm,
373            #[cfg(feature = "component-model")]
374            FuncKey::ComponentTrampoline(abi, _) => abi,
375            #[cfg(feature = "component-model")]
376            FuncKey::ResourceDropTrampoline => Abi::Wasm,
377            #[cfg(feature = "component-model")]
378            FuncKey::UnsafeIntrinsic(abi, _) => abi,
379        }
380    }
381
382    /// Get the raw, underlying `(namespace, index)` representation of this
383    /// compilation key.
384    ///
385    /// The resulting values should only be used for (eventually) calling
386    /// `FuncKey::from_raw_parts` or `FuncKey{Namespace,Index}::from_raw`.
387    //
388    // NB: We use two `u32`s to exactly match
389    // `cranelift_codegen::ir::UserExternalName` and ensure that we can map
390    // one-to-one between that and `FuncKey`.
391    pub fn into_raw_parts(self) -> (u32, u32) {
392        let (ns, index) = self.into_parts();
393        (ns.into_raw(), index.into_raw())
394    }
395
396    /// Create a key from its namespace and index parts.
397    ///
398    /// Should only be called with namespaces and indices that are ultimately
399    /// derived from the same key. For example, if you attempt to pair an index
400    /// and namespace that come from different keys, that may panic. If it
401    /// happens not to panic, you'll end up with a valid key that names an
402    /// arbitrary function in the given namespace, but that function probably
403    /// does not actually exist in the compilation artifact.
404    pub fn from_parts(namespace: FuncKeyNamespace, index: FuncKeyIndex) -> Self {
405        Self::from_raw_parts(namespace.into_raw(), index.into_raw())
406    }
407
408    /// Create a key from its raw, underlying representation.
409    ///
410    /// Should only be given the results of a previous call to
411    /// `FuncKey::into_raw_parts`.
412    ///
413    /// Panics when given invalid raw parts.
414    pub fn from_raw_parts(a: u32, b: u32) -> Self {
415        match FuncKeyKind::from_raw(a & Self::KIND_MASK) {
416            FuncKeyKind::DefinedWasmFunction => {
417                let module = StaticModuleIndex::from_u32(a & Self::MODULE_MASK);
418                let def_func = DefinedFuncIndex::from_u32(b);
419                Self::DefinedWasmFunction(module, def_func)
420            }
421            FuncKeyKind::ArrayToWasmTrampoline => {
422                let module = StaticModuleIndex::from_u32(a & Self::MODULE_MASK);
423                let def_func = DefinedFuncIndex::from_u32(b);
424                Self::ArrayToWasmTrampoline(module, def_func)
425            }
426            FuncKeyKind::WasmToArrayTrampoline => {
427                assert_eq!(a & Self::MODULE_MASK, 0);
428                let ty = ModuleInternedTypeIndex::from_u32(b);
429                Self::WasmToArrayTrampoline(ty)
430            }
431            FuncKeyKind::WasmToBuiltinTrampoline => {
432                assert_eq!(a & Self::MODULE_MASK, 0);
433                let builtin = BuiltinFunctionIndex::from_u32(b);
434                Self::WasmToBuiltinTrampoline(builtin)
435            }
436            FuncKeyKind::PatchableToBuiltinTrampoline => {
437                assert_eq!(a & Self::MODULE_MASK, 0);
438                let builtin = BuiltinFunctionIndex::from_u32(b);
439                Self::PatchableToBuiltinTrampoline(builtin)
440            }
441            FuncKeyKind::PulleyHostCall => {
442                assert_eq!(a & Self::MODULE_MASK, 0);
443                let host_call = HostCall::from_index(b);
444                Self::PulleyHostCall(host_call)
445            }
446
447            #[cfg(feature = "component-model")]
448            FuncKeyKind::ComponentTrampoline => {
449                let abi = Abi::from_raw(a & Self::MODULE_MASK);
450                let trampoline = component::TrampolineIndex::from_u32(b);
451                Self::ComponentTrampoline(abi, trampoline)
452            }
453            #[cfg(feature = "component-model")]
454            FuncKeyKind::ResourceDropTrampoline => {
455                assert_eq!(a & Self::MODULE_MASK, 0);
456                assert_eq!(b, 0);
457                Self::ResourceDropTrampoline
458            }
459            #[cfg(feature = "component-model")]
460            FuncKeyKind::UnsafeIntrinsic => {
461                let abi = Abi::from_raw(a & Self::MODULE_MASK);
462                let intrinsic = component::UnsafeIntrinsic::from_u32(b);
463                Self::UnsafeIntrinsic(abi, intrinsic)
464            }
465        }
466    }
467
468    /// Create a key from a raw packed `u64` representation.
469    ///
470    /// Should only be given a value produced by `into_raw_u64()`.
471    ///
472    /// Panics when given an invalid value.
473    pub fn from_raw_u64(value: u64) -> Self {
474        let hi = u32::try_from(value >> 32).unwrap();
475        let lo = u32::try_from(value & 0xffff_ffff).unwrap();
476        FuncKey::from_raw_parts(hi, lo)
477    }
478
479    /// Produce a packed `u64` representation of this key.
480    ///
481    /// May be used with `from_raw_64()` to reconstruct this key.
482    pub fn into_raw_u64(&self) -> u64 {
483        let (hi, lo) = self.into_raw_parts();
484        (u64::from(hi) << 32) | u64::from(lo)
485    }
486
487    /// Unwrap a `FuncKey::DefinedWasmFunction` or else panic.
488    pub fn unwrap_defined_wasm_function(self) -> (StaticModuleIndex, DefinedFuncIndex) {
489        match self {
490            Self::DefinedWasmFunction(module, def_func) => (module, def_func),
491            _ => panic!("`FuncKey::unwrap_defined_wasm_function` called on {self:?}"),
492        }
493    }
494
495    /// Unwrap a `FuncKey::ArrayToWasmTrampoline` or else panic.
496    pub fn unwrap_array_to_wasm_trampoline(self) -> (StaticModuleIndex, DefinedFuncIndex) {
497        match self {
498            Self::ArrayToWasmTrampoline(module, def_func) => (module, def_func),
499            _ => panic!("`FuncKey::unwrap_array_to_wasm_trampoline` called on {self:?}"),
500        }
501    }
502
503    /// Unwrap a `FuncKey::WasmToArrayTrampoline` or else panic.
504    pub fn unwrap_wasm_to_array_trampoline(self) -> ModuleInternedTypeIndex {
505        match self {
506            Self::WasmToArrayTrampoline(ty) => ty,
507            _ => panic!("`FuncKey::unwrap_wasm_to_array_trampoline` called on {self:?}"),
508        }
509    }
510
511    /// Unwrap a `FuncKey::WasmToBuiltinTrampoline` or else panic.
512    pub fn unwrap_wasm_to_builtin_trampoline(self) -> BuiltinFunctionIndex {
513        match self {
514            Self::WasmToBuiltinTrampoline(builtin) => builtin,
515            _ => panic!("`FuncKey::unwrap_wasm_to_builtin_trampoline` called on {self:?}"),
516        }
517    }
518
519    /// Unwrap a `FuncKey::PulleyHostCall` or else panic.
520    pub fn unwrap_pulley_host_call(self) -> HostCall {
521        match self {
522            Self::PulleyHostCall(host_call) => host_call,
523            _ => panic!("`FuncKey::unwrap_pulley_host_call` called on {self:?}"),
524        }
525    }
526
527    /// Unwrap a `FuncKey::ComponentTrampoline` or else panic.
528    #[cfg(feature = "component-model")]
529    pub fn unwrap_component_trampoline(self) -> (crate::Abi, component::TrampolineIndex) {
530        match self {
531            Self::ComponentTrampoline(abi, trampoline) => (abi, trampoline),
532            _ => panic!("`FuncKey::unwrap_component_trampoline` called on {self:?}"),
533        }
534    }
535
536    /// Unwrap a `FuncKey::ResourceDropTrampoline` or else panic.
537    #[cfg(feature = "component-model")]
538    pub fn unwrap_resource_drop_trampoline(self) {
539        match self {
540            Self::ResourceDropTrampoline => {}
541            _ => panic!("`FuncKey::unwrap_resource_drop_trampoline` called on {self:?}"),
542        }
543    }
544
545    /// Is this "Store-invariant"? This allows us to execute
546    /// EngineCode directly rather than StoreCode.
547    ///
548    /// Any function that is either directly from Wasm code, or calls
549    /// it directly (not indirected through a runtime-provided
550    /// function pointer), is "store-variant": we need to use a
551    /// StoreCode-specific version of the code to hit any patching
552    /// that our specific instantiations may have (due to debugging
553    /// breakpoints, etc). Trampolines into the runtime cannot be
554    /// patched and so can use EngineCode instead. This allows for
555    /// less complex plumbing in some places where we can avoid
556    /// looking up the StoreCode (or having access to the Store).
557    pub fn is_store_invariant(&self) -> bool {
558        match self {
559            Self::DefinedWasmFunction(..) | Self::ArrayToWasmTrampoline(..) => false,
560            Self::WasmToArrayTrampoline(..)
561            | Self::WasmToBuiltinTrampoline(..)
562            | Self::PatchableToBuiltinTrampoline(..)
563            | Self::PulleyHostCall(..) => true,
564            #[cfg(feature = "component-model")]
565            Self::ComponentTrampoline(..)
566            | Self::ResourceDropTrampoline
567            | Self::UnsafeIntrinsic(..) => true,
568        }
569    }
570}