Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions crates/jsshaker/src/analyzer/rw_tracking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl<'a> ReadWriteTarget<'a> {
}

#[derive(Debug, Clone, Copy)]
pub enum TrackReadCachable<'a> {
pub enum TrackReadCacheable<'a> {
Immutable,
Mutable(EntityOrTDZ<'a>),
}
Expand All @@ -43,10 +43,11 @@ impl<'a> Analyzer<'a> {
&mut self,
scope: CfScopeId,
target: ReadWriteTarget<'a>,
cacheable: Option<TrackReadCachable<'a>>,
cacheable: Option<TrackReadCacheable<'a>>,
) {
let target_depth = self.find_first_different_cf_scope(scope);
let mut registered = false;
let mut call_effect = None;
for depth in (target_depth..self.scoping.cf.stack.len()).rev() {
let scope = self.scoping.cf.get_mut_from_depth(depth);
if let Some(data) = scope.exhaustive_data_mut() {
Expand All @@ -63,7 +64,11 @@ impl<'a> Analyzer<'a> {
}
}
if let Some(data) = scope.fn_cache_tracking_data_mut() {
data.track_read(target, cacheable);
if let Some(call_effect) = call_effect {
data.track_call(call_effect);
} else {
call_effect = data.track_read(target, cacheable);
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/jsshaker/src/scope/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ impl<'a> Analyzer<'a> {
let CfScopeKind::Function(tracking_data) = &mut cf_scope.kind else {
unreachable!();
};
let tracking_data = mem::take(*tracking_data);
let tracking_data = mem::replace(*tracking_data, FnCacheTrackingData::UnTrackable);

self.pop_variable_scope();
self.replace_variable_scope(old_variable_scope);
Expand Down
6 changes: 3 additions & 3 deletions crates/jsshaker/src/scope/variable_scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use oxc::{
use super::cf_scope::CfScopeId;
use crate::{
analyzer::Analyzer,
analyzer::rw_tracking::{ReadWriteTarget, TrackReadCachable},
analyzer::rw_tracking::{ReadWriteTarget, TrackReadCacheable},
ast::DeclarationKind,
define_box_bump_idx,
dep::{Dep, LazyDep},
Expand Down Expand Up @@ -206,9 +206,9 @@ impl<'a> Analyzer<'a> {
cf_scope,
ReadWriteTarget::Variable(scope, symbol),
Some(if may_change {
TrackReadCachable::Mutable(value)
TrackReadCacheable::Mutable(value)
} else {
TrackReadCachable::Immutable
TrackReadCacheable::Immutable
}),
);
value
Expand Down
12 changes: 6 additions & 6 deletions crates/jsshaker/src/utils/callee_info.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::hash;
use std::{hash, ptr};

use oxc::{
ast::{
Expand Down Expand Up @@ -51,14 +51,14 @@ impl GetSpan for CalleeNode<'_> {

impl PartialEq for CalleeNode<'_> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
match (*self, *other) {
(CalleeNode::Module, CalleeNode::Module) => true,
(CalleeNode::Function(a), CalleeNode::Function(b)) => a.span() == b.span(),
(CalleeNode::Function(a), CalleeNode::Function(b)) => ptr::eq(a, b),
(CalleeNode::ArrowFunctionExpression(a), CalleeNode::ArrowFunctionExpression(b)) => {
a.span() == b.span()
ptr::eq(a, b)
}
(CalleeNode::ClassStatics(a), CalleeNode::ClassStatics(b)) => a.span() == b.span(),
(CalleeNode::ClassConstructor(a), CalleeNode::ClassConstructor(b)) => a.span() == b.span(),
(CalleeNode::ClassStatics(a), CalleeNode::ClassStatics(b)) => ptr::eq(a, b),
(CalleeNode::ClassConstructor(a), CalleeNode::ClassConstructor(b)) => ptr::eq(a, b),
(CalleeNode::BoundFunction(a), CalleeNode::BoundFunction(b)) => a.span == b.span,
_ => false,
}
Expand Down
105 changes: 81 additions & 24 deletions crates/jsshaker/src/value/function/cache.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use oxc::allocator;
use oxc::allocator::{self, Allocator};

use crate::{
Analyzer,
analyzer::rw_tracking::{ReadWriteTarget, TrackReadCachable},
analyzer::rw_tracking::{ReadWriteTarget, TrackReadCacheable},
entity::Entity,
scope::variable_scope::EntityOrTDZ,
value::{ArgumentsValue, cacheable::Cacheable, call::FnCallInfo},
value::{ArgumentsValue, FunctionValue, cacheable::Cacheable, call::FnCallInfo},
};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
Expand All @@ -15,19 +15,45 @@ pub struct FnCachedInput<'a> {
pub args: &'a [Cacheable<'a>],
}

#[derive(Debug, Clone, Copy)]
pub struct FnCallEffect<'a> {
pub func: &'a FunctionValue<'a>,
pub input: FnCachedInput<'a>,
}
impl<'a> PartialEq for FnCallEffect<'a> {
fn eq(&self, other: &Self) -> bool {
std::ptr::eq(self.func, other.func) && self.input == other.input
}
}
impl<'a> Eq for FnCallEffect<'a> {}
impl<'a> std::hash::Hash for FnCallEffect<'a> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
(self.func as *const _, &self.input).hash(state);
}
}

#[derive(Debug)]
pub struct FnCachedEffects<'a> {
pub reads: allocator::HashMap<'a, ReadWriteTarget<'a>, EntityOrTDZ<'a>>,
pub writes: allocator::HashMap<'a, ReadWriteTarget<'a>, (bool, Entity<'a>)>,
pub calls: allocator::HashSet<'a, FnCallEffect<'a>>,
}

impl<'a> FnCachedEffects<'a> {
pub fn new_in(allocator: &'a Allocator) -> Self {
Self {
reads: allocator::HashMap::new_in(allocator),
writes: allocator::HashMap::new_in(allocator),
calls: allocator::HashSet::new_in(allocator),
}
}
}

#[derive(Debug, Default)]
#[derive(Debug)]
pub enum FnCacheTrackingData<'a> {
#[default]
UnTrackable,
Tracked {
effects: FnCachedEffects<'a>,
},
Tracked { self_call_effect: FnCallEffect<'a>, effects: FnCachedEffects<'a> },
Failed { self_call_effect: FnCallEffect<'a> },
}

impl<'a> FnCachedEffects<'a> {
Expand All @@ -41,32 +67,55 @@ impl<'a> FnCachedEffects<'a> {

impl<'a> FnCacheTrackingData<'a> {
pub fn worst_case() -> Self {
Self::default()
FnCacheTrackingData::UnTrackable
}

pub fn new_in(allocator: &'a allocator::Allocator, info: FnCallInfo<'a>) -> Self {
if let Some(_cache_key) = info.cache_key {
Self::Tracked { effects: FnCachedEffects::new_in(allocator) }
if let Some(cache_key) = info.cache_key {
Self::Tracked {
self_call_effect: FnCallEffect { func: info.func, input: cache_key },
effects: FnCachedEffects::new_in(allocator),
}
} else {
FnCacheTrackingData::UnTrackable
}
}

pub fn failed(&mut self) {
let Self::Tracked { self_call_effect, .. } = self else {
unreachable!();
};
*self = Self::Failed { self_call_effect: *self_call_effect };
}

pub fn call_effect(&self) -> Option<FnCallEffect<'a>> {
match self {
Self::Tracked { self_call_effect, .. } => Some(*self_call_effect),
Self::Failed { self_call_effect } => Some(*self_call_effect),
Self::UnTrackable => None,
}
}

pub fn track_read(
&mut self,
target: ReadWriteTarget<'a>,
cacheable: Option<TrackReadCachable<'a>>,
) {
let Self::Tracked { effects, .. } = self else {
return;
cacheable: Option<TrackReadCacheable<'a>>,
) -> Option<FnCallEffect<'a>> {
let Self::Tracked { self_call_effect, effects } = self else {
return None;
};
let self_call_effect = *self_call_effect;
let Some(cacheable) = cacheable else {
*self = Self::UnTrackable;
return;
self.failed();
return Some(self_call_effect);
};
let TrackReadCachable::Mutable(current_value) = cacheable else {
return;
let TrackReadCacheable::Mutable(current_value) = cacheable else {
return None;
};
if effects.reads.len() > 8 {
self.failed();
return Some(self_call_effect);
}
match effects.reads.entry(target) {
allocator::hash_map::Entry::Occupied(v) => {
// TODO: Remove these?
Expand All @@ -82,6 +131,7 @@ impl<'a> FnCacheTrackingData<'a> {
v.insert(current_value);
}
}
Some(self_call_effect)
}

pub fn track_write(
Expand All @@ -93,11 +143,18 @@ impl<'a> FnCacheTrackingData<'a> {
return;
};
let Some(cacheable) = cacheable else {
*self = Self::UnTrackable;
self.failed();
return;
};
effects.writes.insert(target, cacheable);
}

pub fn track_call(&mut self, effect: FnCallEffect<'a>) {
let Self::Tracked { effects, .. } = self else {
return;
};
effects.calls.insert(effect);
}
}

#[derive(Debug)]
Expand Down Expand Up @@ -135,8 +192,8 @@ impl<'a> FnCache<'a> {
analyzer: &mut Analyzer<'a>,
key: &FnCachedInput<'a>,
) -> Option<Entity<'a>> {
if let Some((effects, ret)) = self.table.get(key) {
for (&target, &last_value) in &effects.reads {
if let Some((cached, ret)) = self.table.get(key) {
for (&target, &last_value) in &cached.reads {
let current_value = analyzer.get_rw_target_current_value(target);
if match (last_value, current_value) {
(Some(e1), Some(e2)) => !e1.exactly_same(e2),
Expand All @@ -147,7 +204,7 @@ impl<'a> FnCache<'a> {
}
}

for (&target, &(indeterminate, cacheable)) in &effects.writes {
for (&target, &(indeterminate, cacheable)) in &cached.writes {
analyzer.set_rw_target_current_value(target, cacheable, indeterminate);
}

Expand All @@ -163,7 +220,7 @@ impl<'a> FnCache<'a> {
ret: Entity<'a>,
tracking: FnCacheTrackingData<'a>,
) {
let FnCacheTrackingData::Tracked { effects } = tracking else {
let FnCacheTrackingData::Tracked { effects, .. } = tracking else {
return;
};
let Some(ret) = ret.as_cacheable() else {
Expand Down
Loading