Skip to main content

vihaco_cpu/
word.rs

1// SPDX-FileCopyrightText: 2026 The vihaco Authors
2// SPDX-License-Identifier: MIT
3
4//! The CPU's raw word representation and its typed bit conversions.
5
6/// The value stored by the word-based CPU.
7pub type Word = u64;
8
9/// Encodes an [`i32`] in the low 32 bits of a word.
10#[inline]
11pub const fn encode_i32(value: i32) -> Word {
12    value as u32 as Word
13}
14
15/// Decodes an [`i32`] from the low 32 bits of a word.
16#[inline]
17pub const fn decode_i32(word: Word) -> i32 {
18    word as u32 as i32
19}
20
21/// Encodes a [`u32`] in the low 32 bits of a word.
22#[inline]
23pub const fn encode_u32(value: u32) -> Word {
24    value as Word
25}
26
27/// Decodes a [`u32`] from the low 32 bits of a word.
28#[inline]
29pub const fn decode_u32(word: Word) -> u32 {
30    word as u32
31}
32
33/// Encodes an [`i64`] without changing its bit pattern.
34#[inline]
35pub const fn encode_i64(value: i64) -> Word {
36    value as Word
37}
38
39/// Decodes an [`i64`] without changing its bit pattern.
40#[inline]
41pub const fn decode_i64(word: Word) -> i64 {
42    word as i64
43}
44
45/// Encodes a [`u64`] without changing its bit pattern.
46#[inline]
47pub const fn encode_u64(value: u64) -> Word {
48    value
49}
50
51/// Decodes a [`u64`] without changing its bit pattern.
52#[inline]
53pub const fn decode_u64(word: Word) -> u64 {
54    word
55}
56
57/// Encodes an [`f32`] using its IEEE-754 representation in the low 32 bits.
58#[inline]
59pub const fn encode_f32(value: f32) -> Word {
60    value.to_bits() as Word
61}
62
63/// Decodes an [`f32`] from its IEEE-754 representation in the low 32 bits.
64#[inline]
65pub const fn decode_f32(word: Word) -> f32 {
66    f32::from_bits(word as u32)
67}
68
69/// Encodes an [`f64`] using its IEEE-754 representation.
70#[inline]
71pub const fn encode_f64(value: f64) -> Word {
72    value.to_bits()
73}
74
75/// Decodes an [`f64`] from its IEEE-754 representation.
76#[inline]
77pub const fn decode_f64(word: Word) -> f64 {
78    f64::from_bits(word)
79}
80
81/// Encodes a boolean as its canonical word representation, `0` or `1`.
82#[inline]
83pub const fn encode_bool(value: bool) -> Word {
84    value as Word
85}
86
87/// Decodes a boolean word. Zero is false and every nonzero word is true.
88#[inline]
89pub const fn decode_bool(word: Word) -> bool {
90    word != 0
91}
92
93/// Encodes a numeric function reference.
94#[inline]
95pub const fn encode_function_ref(id: u32) -> Word {
96    encode_u32(id)
97}
98
99/// Decodes a numeric function reference.
100#[inline]
101pub const fn decode_function_ref(word: Word) -> u32 {
102    decode_u32(word)
103}
104
105/// Encodes a numeric heap reference.
106#[inline]
107pub const fn encode_heap_ref(id: u32) -> Word {
108    encode_u32(id)
109}
110
111/// Decodes a numeric heap reference.
112#[inline]
113pub const fn decode_heap_ref(word: Word) -> u32 {
114    decode_u32(word)
115}
116
117/// Encodes an interned string identifier.
118#[inline]
119pub const fn encode_string_id(id: u32) -> Word {
120    encode_u32(id)
121}
122
123/// Decodes an interned string identifier.
124#[inline]
125pub const fn decode_string_id(word: Word) -> u32 {
126    decode_u32(word)
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132
133    #[test]
134    fn narrow_integers_are_canonicalized_to_low_32_bits() {
135        assert_eq!(encode_i32(-1), u64::from(u32::MAX));
136        assert_eq!(decode_i32(u64::MAX), -1);
137        assert_eq!(encode_u32(u32::MAX), u64::from(u32::MAX));
138        assert_eq!(decode_u32(u64::MAX), u32::MAX);
139    }
140
141    #[test]
142    fn wide_integers_preserve_bits() {
143        let signed = i64::MIN + 123;
144        assert_eq!(decode_i64(encode_i64(signed)), signed);
145
146        let unsigned = u64::MAX - 123;
147        assert_eq!(decode_u64(encode_u64(unsigned)), unsigned);
148    }
149
150    #[test]
151    fn floats_preserve_bits_including_nan_payloads() {
152        let f32_bits = 0x7fc1_2345;
153        assert_eq!(encode_f32(f32::from_bits(f32_bits)), u64::from(f32_bits));
154        assert_eq!(decode_f32(u64::MAX).to_bits(), u32::MAX);
155
156        let f64_bits = 0x7ff8_1234_5678_9abc;
157        assert_eq!(encode_f64(f64::from_bits(f64_bits)), f64_bits);
158        assert_eq!(decode_f64(f64_bits).to_bits(), f64_bits);
159    }
160
161    #[test]
162    fn booleans_use_canonical_encoding() {
163        assert_eq!(encode_bool(false), 0);
164        assert_eq!(encode_bool(true), 1);
165        assert!(!decode_bool(0));
166        assert!(decode_bool(1));
167        assert!(decode_bool(Word::MAX));
168    }
169
170    #[test]
171    fn references_and_string_ids_are_numeric_words() {
172        assert_eq!(decode_function_ref(encode_function_ref(7)), 7);
173        assert_eq!(decode_heap_ref(encode_heap_ref(11)), 11);
174        assert_eq!(decode_string_id(encode_string_id(13)), 13);
175        assert_eq!(encode_heap_ref(u32::MAX), u64::from(u32::MAX));
176    }
177}