feat: init Rust VM

This commit is contained in:
2026-03-14 11:42:39 +08:00
parent 40d00a6c47
commit 198d847151
26 changed files with 3621 additions and 994 deletions

8
boxing/Cargo.toml Normal file
View File

@@ -0,0 +1,8 @@
[package]
name = "boxing"
version = "0.1.3"
edition = "2021"
description = "NaN-boxing primitives (local fork with bool fix)"
[dependencies]
sptr = "0.3"

2
boxing/src/lib.rs Normal file
View File

@@ -0,0 +1,2 @@
pub mod nan;
mod utils;

7
boxing/src/nan.rs Normal file
View File

@@ -0,0 +1,7 @@
pub mod raw;
pub use raw::RawBox;
const SIGN_MASK: u64 = 0x7FFF_FFFF_FFFF_FFFF;
const QUIET_NAN: u64 = 0x7FF8_0000_0000_0000;
const NEG_QUIET_NAN: u64 = 0xFFF8_0000_0000_0000;

471
boxing/src/nan/raw.rs Normal file
View File

@@ -0,0 +1,471 @@
use super::{NEG_QUIET_NAN, QUIET_NAN, SIGN_MASK};
use crate::utils::ArrayExt;
use sptr::Strict;
use std::fmt;
use std::mem::ManuallyDrop;
use std::num::NonZeroU8;
pub trait RawStore: Sized {
fn to_val(self, value: &mut Value);
fn from_val(value: &Value) -> Self;
}
impl RawStore for [u8; 6] {
#[inline]
fn to_val(self, value: &mut Value) {
value.set_data(self);
}
#[inline]
fn from_val(value: &Value) -> Self {
*value.data()
}
}
impl RawStore for bool {
#[inline]
fn to_val(self, value: &mut Value) {
value.set_data([u8::from(self)].truncate_to());
}
#[inline]
fn from_val(value: &Value) -> Self {
value.data()[0] == 1
}
}
macro_rules! int_store {
($ty:ty) => {
impl RawStore for $ty {
#[inline]
fn to_val(self, value: &mut Value) {
let bytes = self.to_ne_bytes();
value.set_data(bytes.truncate_to());
}
#[inline]
fn from_val(value: &Value) -> Self {
<$ty>::from_ne_bytes(value.data().truncate_to())
}
}
};
}
int_store!(u8);
int_store!(u16);
int_store!(u32);
int_store!(i8);
int_store!(i16);
int_store!(i32);
fn store_ptr<P: Strict + Copy>(value: &mut Value, ptr: P) {
#[cfg(target_pointer_width = "64")]
{
assert!(
ptr.addr() <= 0x0000_FFFF_FFFF_FFFF,
"Pointer too large to store in NaN box"
);
let val = (unsafe { value.whole_mut() } as *mut [u8; 8]).cast::<P>();
let ptr = Strict::map_addr(ptr, |addr| {
addr | (usize::from(value.header().into_raw()) << 48)
});
unsafe { val.write(ptr) };
}
#[cfg(target_pointer_width = "32")]
{
let _ = (value, ptr);
unimplemented!("32-bit pointer storage not supported");
}
}
fn load_ptr<P: Strict>(value: &Value) -> P {
#[cfg(target_pointer_width = "64")]
{
let val = (unsafe { value.whole() } as *const [u8; 8]).cast::<P>();
let ptr = unsafe { val.read() };
Strict::map_addr(ptr, |addr| addr & 0x0000_FFFF_FFFF_FFFF)
}
#[cfg(target_pointer_width = "32")]
{
let _ = value;
unimplemented!("32-bit pointer storage not supported");
}
}
impl<T> RawStore for *const T {
fn to_val(self, value: &mut Value) {
store_ptr::<*const T>(value, self);
}
fn from_val(value: &Value) -> Self {
load_ptr::<*const T>(value)
}
}
impl<T> RawStore for *mut T {
fn to_val(self, value: &mut Value) {
store_ptr::<*mut T>(value, self);
}
fn from_val(value: &Value) -> Self {
load_ptr::<*mut T>(value)
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
enum TagVal {
_P1,
_P2,
_P3,
_P4,
_P5,
_P6,
_P7,
_N1,
_N2,
_N3,
_N4,
_N5,
_N6,
_N7,
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct RawTag(TagVal);
impl RawTag {
#[inline]
#[must_use]
pub fn new(neg: bool, val: NonZeroU8) -> RawTag {
unsafe { Self::new_unchecked(neg, val.get() & 0x07) }
}
#[inline]
#[must_use]
pub fn new_checked(neg: bool, val: u8) -> Option<RawTag> {
Some(RawTag(match (neg, val) {
(false, 1) => TagVal::_P1,
(false, 2) => TagVal::_P2,
(false, 3) => TagVal::_P3,
(false, 4) => TagVal::_P4,
(false, 5) => TagVal::_P5,
(false, 6) => TagVal::_P6,
(false, 7) => TagVal::_P7,
(true, 1) => TagVal::_N1,
(true, 2) => TagVal::_N2,
(true, 3) => TagVal::_N3,
(true, 4) => TagVal::_N4,
(true, 5) => TagVal::_N5,
(true, 6) => TagVal::_N6,
(true, 7) => TagVal::_N7,
_ => return None,
}))
}
/// # Safety
///
/// `val` must be in the range `1..8`
#[inline]
#[must_use]
pub unsafe fn new_unchecked(neg: bool, val: u8) -> RawTag {
RawTag(match (neg, val) {
(false, 1) => TagVal::_P1,
(false, 2) => TagVal::_P2,
(false, 3) => TagVal::_P3,
(false, 4) => TagVal::_P4,
(false, 5) => TagVal::_P5,
(false, 6) => TagVal::_P6,
(false, 7) => TagVal::_P7,
(true, 1) => TagVal::_N1,
(true, 2) => TagVal::_N2,
(true, 3) => TagVal::_N3,
(true, 4) => TagVal::_N4,
(true, 5) => TagVal::_N5,
(true, 6) => TagVal::_N6,
(true, 7) => TagVal::_N7,
_ => unsafe { core::hint::unreachable_unchecked() },
})
}
#[inline]
#[must_use]
pub fn is_neg(self) -> bool {
matches!(self.0, |TagVal::_N1| TagVal::_N2
| TagVal::_N3
| TagVal::_N4
| TagVal::_N5
| TagVal::_N6
| TagVal::_N7)
}
#[inline]
#[must_use]
pub fn val(self) -> NonZeroU8 {
match self.0 {
TagVal::_P1 | TagVal::_N1 => NonZeroU8::MIN,
TagVal::_P2 | TagVal::_N2 => NonZeroU8::MIN.saturating_add(1),
TagVal::_P3 | TagVal::_N3 => NonZeroU8::MIN.saturating_add(2),
TagVal::_P4 | TagVal::_N4 => NonZeroU8::MIN.saturating_add(3),
TagVal::_P5 | TagVal::_N5 => NonZeroU8::MIN.saturating_add(4),
TagVal::_P6 | TagVal::_N6 => NonZeroU8::MIN.saturating_add(5),
TagVal::_P7 | TagVal::_N7 => NonZeroU8::MIN.saturating_add(6),
}
}
#[inline]
#[must_use]
pub fn neg_val(self) -> (bool, u8) {
match self.0 {
TagVal::_P1 => (false, 1),
TagVal::_P2 => (false, 2),
TagVal::_P3 => (false, 3),
TagVal::_P4 => (false, 4),
TagVal::_P5 => (false, 5),
TagVal::_P6 => (false, 6),
TagVal::_P7 => (false, 7),
TagVal::_N1 => (true, 1),
TagVal::_N2 => (true, 2),
TagVal::_N3 => (true, 3),
TagVal::_N4 => (true, 4),
TagVal::_N5 => (true, 5),
TagVal::_N6 => (true, 6),
TagVal::_N7 => (true, 7),
}
}
}
impl fmt::Debug for RawTag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RawTag")
.field("neg", &self.is_neg())
.field("val", &self.val())
.finish()
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(transparent)]
struct Header(u16);
impl Header {
#[inline]
fn new(tag: RawTag) -> Header {
let (neg, val) = tag.neg_val();
Header(0x7FF8 | (u16::from(neg) << 15) | u16::from(val))
}
#[inline]
fn tag(self) -> RawTag {
unsafe { RawTag::new_unchecked(self.get_sign(), self.get_tag()) }
}
#[inline]
fn get_sign(self) -> bool {
self.0 & 0x8000 != 0
}
#[inline]
fn get_tag(self) -> u8 {
(self.0 & 0x0007) as u8
}
#[inline]
fn into_raw(self) -> u16 {
self.0
}
}
#[derive(Clone, Debug, PartialEq)]
#[repr(C, align(8))]
pub struct Value {
#[cfg(target_endian = "big")]
header: Header,
data: [u8; 6],
#[cfg(target_endian = "little")]
header: Header,
}
impl Value {
#[inline]
pub fn new(tag: RawTag, data: [u8; 6]) -> Value {
Value {
header: Header::new(tag),
data,
}
}
#[inline]
pub fn empty(tag: RawTag) -> Value {
Value::new(tag, [0; 6])
}
pub fn store<T: RawStore>(tag: RawTag, val: T) -> Value {
let mut v = Value::new(tag, [0; 6]);
T::to_val(val, &mut v);
v
}
pub fn load<T: RawStore>(self) -> T {
T::from_val(&self)
}
#[inline]
#[must_use]
pub fn tag(&self) -> RawTag {
self.header.tag()
}
#[inline]
fn header(&self) -> &Header {
&self.header
}
#[inline]
pub fn set_data(&mut self, val: [u8; 6]) {
self.data = val;
}
#[inline]
#[must_use]
pub fn data(&self) -> &[u8; 6] {
&self.data
}
#[inline]
#[must_use]
pub fn data_mut(&mut self) -> &mut [u8; 6] {
&mut self.data
}
#[inline]
#[must_use]
pub unsafe fn whole(&self) -> &[u8; 8] {
let ptr = (self as *const Value).cast::<[u8; 8]>();
unsafe { &*ptr }
}
#[inline]
#[must_use]
pub unsafe fn whole_mut(&mut self) -> &mut [u8; 8] {
let ptr = (self as *mut Value).cast::<[u8; 8]>();
unsafe { &mut *ptr }
}
}
#[repr(C)]
pub union RawBox {
float: f64,
value: ManuallyDrop<Value>,
bits: u64,
#[cfg(target_pointer_width = "64")]
ptr: *const (),
#[cfg(target_pointer_width = "32")]
ptr: (u32, *const ()),
}
impl RawBox {
#[inline]
#[must_use]
pub fn from_float(val: f64) -> RawBox {
match (val.is_nan(), val.is_sign_positive()) {
(true, true) => RawBox {
float: f64::from_bits(QUIET_NAN),
},
(true, false) => RawBox {
float: f64::from_bits(NEG_QUIET_NAN),
},
(false, _) => RawBox { float: val },
}
}
#[inline]
#[must_use]
pub fn from_value(value: Value) -> RawBox {
RawBox {
value: ManuallyDrop::new(value),
}
}
#[inline]
#[must_use]
pub fn tag(&self) -> Option<RawTag> {
if self.is_value() {
Some(unsafe { self.value.tag() })
} else {
None
}
}
#[inline]
#[must_use]
pub fn is_float(&self) -> bool {
(unsafe { !self.float.is_nan() } || unsafe { self.bits & SIGN_MASK == QUIET_NAN })
}
#[inline]
#[must_use]
pub fn is_value(&self) -> bool {
(unsafe { self.float.is_nan() } && unsafe { self.bits & SIGN_MASK != QUIET_NAN })
}
#[inline]
#[must_use]
pub fn float(&self) -> Option<&f64> {
if self.is_float() {
Some(unsafe { &self.float })
} else {
None
}
}
#[inline]
#[must_use]
pub fn value(&self) -> Option<&Value> {
if self.is_value() {
Some(unsafe { &self.value })
} else {
None
}
}
#[inline]
pub fn into_float_unchecked(self) -> f64 {
unsafe { self.float }
}
}
impl Clone for RawBox {
#[inline]
fn clone(&self) -> Self {
RawBox {
ptr: unsafe { self.ptr },
}
}
}
impl fmt::Debug for RawBox {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.float() {
Some(val) => f.debug_tuple("RawBox::Float").field(val).finish(),
None => {
let val = self.value().expect("RawBox is neither float nor value");
f.debug_struct("RawBox::Data")
.field("tag", &val.tag())
.field("data", val.data())
.finish()
}
}
}
}

16
boxing/src/utils.rs Normal file
View File

@@ -0,0 +1,16 @@
pub trait ArrayExt<const LEN: usize> {
type Elem;
fn truncate_to<const M: usize>(self) -> [Self::Elem; M];
}
impl<T: Default + Copy, const N: usize> ArrayExt<N> for [T; N] {
type Elem = T;
fn truncate_to<const M: usize>(self) -> [Self::Elem; M] {
let copy_len = usize::min(N, M);
let mut out = [T::default(); M];
out[0..copy_len].copy_from_slice(&self[0..copy_len]);
out
}
}