Skip to content

Commit

Permalink
perf(semantic): reduce storage size for symbol redeclarations (#4463)
Browse files Browse the repository at this point in the history
Most symbols don't have redeclarations.

So instead of storing `Vec<Span>` directly in `redeclare_variables` (24 bytes per symbol), store `Option<RedeclarationId>` (4 bytes).

`RedeclarationId` indexes into `redeclarations` where the actual `Vec<Span>` is stored. But for symbols with no redeclarations (the vast majority), it takes 4 bytes per symbol only.
  • Loading branch information
overlookmotel committed Jul 26, 2024
1 parent f17254a commit 6a9f4db
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 12 deletions.
4 changes: 2 additions & 2 deletions crates/oxc_linter/src/rules/eslint/no_redeclare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ impl Rule for NoRedeclare {
AstKind::VariableDeclarator(var) => {
if let BindingPatternKind::BindingIdentifier(ident) = &var.id.kind {
if symbol_name == ident.name.as_str() {
for span in ctx.symbols().get_redeclare_variables(symbol_id) {
for span in ctx.symbols().get_redeclarations(symbol_id) {
self.report_diagnostic(ctx, *span, ident);
}
}
Expand All @@ -75,7 +75,7 @@ impl Rule for NoRedeclare {
AstKind::FormalParameter(param) => {
if let BindingPatternKind::BindingIdentifier(ident) = &param.pattern.kind {
if symbol_name == ident.name.as_str() {
for span in ctx.symbols().get_redeclare_variables(symbol_id) {
for span in ctx.symbols().get_redeclarations(symbol_id) {
self.report_diagnostic(ctx, *span, ident);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ impl Rule for NoShadowRestrictedNames {
}

check_and_diagnostic(name, ctx.symbols().get_span(symbol_id), ctx);
for span in ctx.symbols().get_redeclare_variables(symbol_id) {
for span in ctx.symbols().get_redeclarations(symbol_id) {
check_and_diagnostic(name, *span, ctx);
}
});
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_semantic/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ impl<'a> SemanticBuilder<'a> {
}

pub fn add_redeclare_variable(&mut self, symbol_id: SymbolId, span: Span) {
self.symbols.add_redeclare_variable(symbol_id, span);
self.symbols.add_redeclaration(symbol_id, span);
}

fn add_export_flag_to_export_identifiers(&mut self, program: &Program<'a>) {
Expand Down
32 changes: 24 additions & 8 deletions crates/oxc_semantic/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use oxc_index::IndexVec;
use oxc_span::{CompactStr, Span};
pub use oxc_syntax::{
scope::ScopeId,
symbol::{SymbolFlags, SymbolId},
symbol::{RedeclarationId, SymbolFlags, SymbolId},
};
#[cfg(feature = "serialize")]
use serde::Serialize;
Expand All @@ -26,6 +26,10 @@ export type IndexVec<I, T> = Array<T>;
/// Symbol Table
///
/// `SoA` (Struct of Arrays) for memory efficiency.
///
/// Most symbols won't have redeclarations, so instead of storing `Vec<Span>` directly in
/// `redeclare_variables` (32 bytes per symbol), store `Option<RedeclarationId>` (4 bytes).
/// That ID indexes into `redeclarations` where the actual `Vec<Span>` is stored.
#[derive(Debug, Default)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify), serde(rename_all = "camelCase"))]
pub struct SymbolTable {
Expand All @@ -36,7 +40,9 @@ pub struct SymbolTable {
/// Pointer to the AST Node where this symbol is declared
pub declarations: IndexVec<SymbolId, AstNodeId>,
pub resolved_references: IndexVec<SymbolId, Vec<ReferenceId>>,
pub redeclare_variables: IndexVec<SymbolId, Vec<Span>>,
redeclarations: IndexVec<SymbolId, Option<RedeclarationId>>,

redeclaration_spans: IndexVec<RedeclarationId, Vec<Span>>,

pub references: IndexVec<ReferenceId, Reference>,
}
Expand Down Expand Up @@ -90,8 +96,13 @@ impl SymbolTable {
self.flags[symbol_id]
}

pub fn get_redeclare_variables(&self, symbol_id: SymbolId) -> &Vec<Span> {
&self.redeclare_variables[symbol_id]
pub fn get_redeclarations(&self, symbol_id: SymbolId) -> &[Span] {
if let Some(redeclaration_id) = self.redeclarations[symbol_id] {
&self.redeclaration_spans[redeclaration_id]
} else {
static EMPTY: &[Span] = &[];
EMPTY
}
}

pub fn union_flag(&mut self, symbol_id: SymbolId, includes: SymbolFlags) {
Expand Down Expand Up @@ -128,11 +139,16 @@ impl SymbolTable {
self.scope_ids.push(scope_id);
self.declarations.push(node_id);
self.resolved_references.push(vec![]);
self.redeclare_variables.push(vec![])
self.redeclarations.push(None)
}

pub fn add_redeclare_variable(&mut self, symbol_id: SymbolId, span: Span) {
self.redeclare_variables[symbol_id].push(span);
pub fn add_redeclaration(&mut self, symbol_id: SymbolId, span: Span) {
if let Some(redeclaration_id) = self.redeclarations[symbol_id] {
self.redeclaration_spans[redeclaration_id].push(span);
} else {
let redeclaration_id = self.redeclaration_spans.push(vec![span]);
self.redeclarations[symbol_id] = Some(redeclaration_id);
};
}

pub fn create_reference(&mut self, reference: Reference) -> ReferenceId {
Expand Down Expand Up @@ -200,7 +216,7 @@ impl SymbolTable {
self.scope_ids.reserve(additional_symbols);
self.declarations.reserve(additional_symbols);
self.resolved_references.reserve(additional_symbols);
self.redeclare_variables.reserve(additional_symbols);
self.redeclarations.reserve(additional_symbols);

self.references.reserve(additional_references);
}
Expand Down
18 changes: 18 additions & 0 deletions crates/oxc_syntax/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,29 @@ impl Idx for SymbolId {
}
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
pub struct RedeclarationId(NonZeroU32);

impl Idx for RedeclarationId {
#[allow(clippy::cast_possible_truncation)]
fn from_usize(idx: usize) -> Self {
// SAFETY: + 1 is always non-zero.

unsafe { Self(NonZeroU32::new_unchecked(idx as u32 + 1)) }
}

fn index(self) -> usize {
self.0.get() as usize - 1
}
}

#[cfg(feature = "serialize")]
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = r#"
export type SymbolId = number;
export type SymbolFlags = unknown;
export type RedeclarationId = unknown;
"#;

bitflags! {
Expand Down

0 comments on commit 6a9f4db

Please sign in to comment.