From 6cb02c31fbf95201438cbe658e981f9b6385c837 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sat, 17 Feb 2024 15:53:33 +0100 Subject: [PATCH] specialize interning `f64`s that fit in a usize --- crates/dash_vm/src/js_std/number.rs | 10 +++++----- crates/dash_vm/src/util.rs | 21 +++++++++++++++------ crates/dash_vm/src/value/primitive.rs | 4 ++-- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/crates/dash_vm/src/js_std/number.rs b/crates/dash_vm/src/js_std/number.rs index a021c1a3..e78f5227 100644 --- a/crates/dash_vm/src/js_std/number.rs +++ b/crates/dash_vm/src/js_std/number.rs @@ -1,5 +1,5 @@ use crate::throw; -use crate::util::format_f64; +use crate::util::intern_f64; use crate::value::function::native::CallContext; use crate::value::ops::conversions::ValueConversion; use crate::value::primitive::{Number, MAX_SAFE_INTEGERF}; @@ -30,13 +30,13 @@ pub fn to_string(cx: CallContext) -> Result { }; let re = match radix { - 2 => format!("{:b}", num as u64), - 10 => format_f64(num), - 16 => format!("{:x}", num as u64), + 2 => cx.scope.intern(format!("{:b}", num as u64).as_ref()), + 10 => intern_f64(cx.scope, num), + 16 => cx.scope.intern(format!("{:x}", num as u64)), _ => throw!(cx.scope, RangeError, "Invalid radix: {}", radix), }; - Ok(Value::String(cx.scope.intern(re.as_ref()).into())) + Ok(Value::String(re.into())) } pub fn is_finite(cx: CallContext) -> Result { diff --git a/crates/dash_vm/src/util.rs b/crates/dash_vm/src/util.rs index 8b6e7f94..48cdf67f 100644 --- a/crates/dash_vm/src/util.rs +++ b/crates/dash_vm/src/util.rs @@ -1,5 +1,9 @@ use std::num::FpCategory; +use dash_middle::interner::{sym, Symbol}; + +use crate::localscope::LocalScope; + /// Marks the code path leading to this call as cold, or "unlikely" #[cold] pub fn cold_path() {} @@ -19,11 +23,16 @@ pub trait Captures<'a> {} impl<'a, T: ?Sized> Captures<'a> for T {} -pub fn format_f64(n: f64) -> String { - // TODO: specialize zero, infinity, NaN by "interning" them in vm.statics +pub fn intern_f64(sc: &mut LocalScope, n: f64) -> Symbol { + if n.trunc() == n && n >= 0.0 && n <= usize::MAX as f64 { + // Happy path: no fractional part and fits in a usize + // This can use the specialized usize interner + return sc.intern_usize(n as usize); + } + match n.classify() { - FpCategory::Infinite => "Infinity".into(), - FpCategory::Nan => "NaN".into(), + FpCategory::Infinite => sym::Infinity, + FpCategory::Nan => sym::NaN, _ if n >= 1e21f64 || n <= -1e21f64 => { let mut digits = 0; let mut n = n; @@ -35,8 +44,8 @@ pub fn format_f64(n: f64) -> String { n /= 10f64; digits += 1; } - format!("{n:.0}e+{digits}") + sc.intern(format!("{n:.0}e+{digits}").as_ref()) } - _ => format!("{n}"), + _ => sc.intern(n.to_string().as_ref()), } } diff --git a/crates/dash_vm/src/value/primitive.rs b/crates/dash_vm/src/value/primitive.rs index 652ef52e..a313c49e 100644 --- a/crates/dash_vm/src/value/primitive.rs +++ b/crates/dash_vm/src/value/primitive.rs @@ -9,7 +9,7 @@ use crate::gc::handle::Handle; use crate::gc::interner::sym; use crate::localscope::LocalScope; use crate::throw; -use crate::util::{format_f64, Captures}; +use crate::util::{intern_f64, Captures}; use super::boxed::{Boolean as BoxedBoolean, Number as BoxedNumber, Symbol as BoxedSymbol}; use super::object::{Object, PropertyKey, PropertyValue}; @@ -481,7 +481,7 @@ impl ValueConversion for f64 { } fn to_js_string(&self, sc: &mut LocalScope) -> Result { - Ok(sc.intern(format_f64(*self).as_ref()).into()) + Ok(intern_f64(sc, *self).into()) } fn length_of_array_like(&self, _sc: &mut LocalScope) -> Result {