From cbad703b00cbe573f0a71e0acce34f6ae9eb713a Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 10 Mar 2021 13:32:09 +0800 Subject: [PATCH] Flatten data structures and more aggressive inlining. --- src/ast.rs | 91 ++++++++++++++++++++++++++++++++-------------- src/engine_api.rs | 2 +- src/lib.rs | 2 +- src/module/mod.rs | 19 ++++++++++ src/parse_error.rs | 3 +- src/parser.rs | 4 +- src/utils.rs | 7 ++++ tests/optimizer.rs | 8 ++-- 8 files changed, 98 insertions(+), 38 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index caaecf11..64fe39e8 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -11,7 +11,6 @@ use crate::stdlib::{ num::NonZeroUsize, ops::{Add, AddAssign}, string::String, - vec, vec::Vec, }; use crate::token::Token; @@ -61,9 +60,9 @@ pub struct ScriptFnDef { pub params: StaticVec, /// Access to external variables. #[cfg(not(feature = "no_closure"))] - pub externals: Vec, + pub externals: StaticVec, /// Function doc-comments (if any). - pub comments: Vec, + pub comments: StaticVec, } impl fmt::Display for ScriptFnDef { @@ -149,7 +148,7 @@ pub struct AST { /// Source of the [`AST`]. source: Option, /// Global statements. - statements: Vec, + body: StmtBlock, /// Script-defined functions. functions: Shared, /// Embedded module resolver, if any. @@ -162,7 +161,7 @@ impl Default for AST { fn default() -> Self { Self { source: None, - statements: Vec::with_capacity(16), + body: Default::default(), functions: Default::default(), #[cfg(not(feature = "no_module"))] resolver: None, @@ -179,7 +178,10 @@ impl AST { ) -> Self { Self { source: None, - statements: statements.into_iter().collect(), + body: StmtBlock { + statements: statements.into_iter().collect(), + pos: Position::NONE, + }, functions: functions.into(), #[cfg(not(feature = "no_module"))] resolver: None, @@ -194,7 +196,10 @@ impl AST { ) -> Self { Self { source: Some(source.into()), - statements: statements.into_iter().collect(), + body: StmtBlock { + statements: statements.into_iter().collect(), + pos: Position::NONE, + }, functions: functions.into(), #[cfg(not(feature = "no_module"))] resolver: None, @@ -231,7 +236,7 @@ impl AST { #[cfg(not(feature = "internals"))] #[inline(always)] pub(crate) fn statements(&self) -> &[Stmt] { - &self.statements + &self.body.statements } /// _(INTERNALS)_ Get the statements. /// Exported under the `internals` feature only. @@ -244,8 +249,8 @@ impl AST { /// Get a mutable reference to the statements. #[cfg(not(feature = "no_optimize"))] #[inline(always)] - pub(crate) fn statements_mut(&mut self) -> &mut Vec { - &mut self.statements + pub(crate) fn statements_mut(&mut self) -> &mut StaticVec { + &mut self.body.statements } /// Get the internal shared [`Module`] containing all script-defined functions. #[cfg(not(feature = "internals"))] @@ -333,7 +338,7 @@ impl AST { functions.merge_filtered(&self.functions, &filter); Self { source: self.source.clone(), - statements: Default::default(), + body: Default::default(), functions: functions.into(), #[cfg(not(feature = "no_module"))] resolver: self.resolver.clone(), @@ -345,7 +350,7 @@ impl AST { pub fn clone_statements_only(&self) -> Self { Self { source: self.source.clone(), - statements: self.statements.clone(), + body: self.body.clone(), functions: Default::default(), #[cfg(not(feature = "no_module"))] resolver: self.resolver.clone(), @@ -515,20 +520,19 @@ impl AST { filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool, ) -> Self { let Self { - statements, - functions, - .. + body, functions, .. } = self; - let ast = match (statements.is_empty(), other.statements.is_empty()) { + let merged = match (body.is_empty(), other.body.is_empty()) { (false, false) => { - let mut statements = statements.clone(); - statements.extend(other.statements.iter().cloned()); - statements + let mut body = body.clone(); + body.statements + .extend(other.body.statements.iter().cloned()); + body } - (false, true) => statements.clone(), - (true, false) => other.statements.clone(), - (true, true) => vec![], + (false, true) => body.clone(), + (true, false) => other.body.clone(), + (true, true) => Default::default(), }; let source = other.source.clone().or_else(|| self.source.clone()); @@ -537,9 +541,9 @@ impl AST { functions.merge_filtered(&other.functions, &filter); if let Some(source) = source { - Self::new_with_source(ast, functions, source) + Self::new_with_source(merged.statements, functions, source) } else { - Self::new(ast, functions) + Self::new(merged.statements, functions) } } /// Combine one [`AST`] with another. The second [`AST`] is consumed. @@ -599,7 +603,10 @@ impl AST { other: Self, filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool, ) -> &mut Self { - self.statements.extend(other.statements.into_iter()); + self.body + .statements + .extend(other.body.statements.into_iter()); + if !other.functions.is_empty() { shared_make_mut(&mut self.functions).merge_filtered(&other.functions, &filter); } @@ -673,7 +680,7 @@ impl AST { /// Clear all statements in the [`AST`], leaving only function definitions. #[inline(always)] pub fn clear_statements(&mut self) { - self.statements = vec![]; + self.body = Default::default(); } /// Recursively walk the [`AST`], including function bodies (if any). #[cfg(not(feature = "internals"))] @@ -810,7 +817,7 @@ impl<'a> From<&'a Expr> for ASTNode<'a> { /// # Volatile Data Structure /// /// This type is volatile and may change. -#[derive(Debug, Clone, Hash)] +#[derive(Clone, Hash, Default)] pub struct StmtBlock { pub statements: StaticVec, pub pos: Position, @@ -818,15 +825,27 @@ pub struct StmtBlock { impl StmtBlock { /// Is this statements block empty? + #[inline(always)] pub fn is_empty(&self) -> bool { self.statements.is_empty() } /// Number of statements in this statements block. + #[inline(always)] pub fn len(&self) -> usize { self.statements.len() } } +impl fmt::Debug for StmtBlock { + #[inline(always)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if !self.pos.is_none() { + write!(f, "{} @ ", self.pos)?; + } + fmt::Debug::fmt(&self.statements, f) + } +} + /// _(INTERNALS)_ A statement. /// Exported under the `internals` feature only. /// @@ -1242,6 +1261,7 @@ pub struct FloatWrapper(FLOAT); #[cfg(not(feature = "no_float"))] impl Hash for FloatWrapper { + #[inline(always)] fn hash(&self, state: &mut H) { self.0.to_ne_bytes().hash(state); } @@ -1249,6 +1269,7 @@ impl Hash for FloatWrapper { #[cfg(not(feature = "no_float"))] impl AsRef for FloatWrapper { + #[inline(always)] fn as_ref(&self) -> &FLOAT { &self.0 } @@ -1256,6 +1277,7 @@ impl AsRef for FloatWrapper { #[cfg(not(feature = "no_float"))] impl AsMut for FloatWrapper { + #[inline(always)] fn as_mut(&mut self) -> &mut FLOAT { &mut self.0 } @@ -1265,6 +1287,7 @@ impl AsMut for FloatWrapper { impl crate::stdlib::ops::Deref for FloatWrapper { type Target = FLOAT; + #[inline(always)] fn deref(&self) -> &Self::Target { &self.0 } @@ -1272,6 +1295,7 @@ impl crate::stdlib::ops::Deref for FloatWrapper { #[cfg(not(feature = "no_float"))] impl crate::stdlib::ops::DerefMut for FloatWrapper { + #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } @@ -1279,6 +1303,7 @@ impl crate::stdlib::ops::DerefMut for FloatWrapper { #[cfg(not(feature = "no_float"))] impl fmt::Debug for FloatWrapper { + #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, f) } @@ -1286,6 +1311,7 @@ impl fmt::Debug for FloatWrapper { #[cfg(not(feature = "no_float"))] impl fmt::Display for FloatWrapper { + #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(feature = "no_std")] use num_traits::Float; @@ -1301,6 +1327,7 @@ impl fmt::Display for FloatWrapper { #[cfg(not(feature = "no_float"))] impl From for FloatWrapper { + #[inline(always)] fn from(value: FLOAT) -> Self { Self::new(value) } @@ -1310,6 +1337,7 @@ impl From for FloatWrapper { impl FromStr for FloatWrapper { type Err = ::Err; + #[inline(always)] fn from_str(s: &str) -> Result { FLOAT::from_str(s).map(Into::::into) } @@ -1317,6 +1345,7 @@ impl FromStr for FloatWrapper { #[cfg(not(feature = "no_float"))] impl FloatWrapper { + #[inline(always)] pub const fn new(value: FLOAT) -> Self { Self(value) } @@ -1384,6 +1413,7 @@ impl Expr { /// Get the [`Dynamic`] value of a constant expression. /// /// Returns [`None`] if the expression is not constant. + #[inline] pub fn get_constant_value(&self) -> Option { Some(match self { Self::DynamicConstant(x, _) => x.as_ref().clone(), @@ -1434,6 +1464,7 @@ impl Expr { } } /// Get the [position][Position] of the expression. + #[inline] pub fn position(&self) -> Position { match self { #[cfg(not(feature = "no_float"))] @@ -1462,6 +1493,7 @@ impl Expr { } } /// Override the [position][Position] of the expression. + #[inline] pub fn set_position(&mut self, new_pos: Position) -> &mut Self { match self { #[cfg(not(feature = "no_float"))] @@ -1490,6 +1522,7 @@ impl Expr { /// Is the expression pure? /// /// A pure expression has no side effects. + #[inline] pub fn is_pure(&self) -> bool { match self { Self::Array(x, _) => x.iter().all(Self::is_pure), @@ -1516,6 +1549,7 @@ impl Expr { } } /// Is the expression a constant? + #[inline] pub fn is_constant(&self) -> bool { match self { #[cfg(not(feature = "no_float"))] @@ -1539,6 +1573,7 @@ impl Expr { } } /// Is a particular [token][Token] allowed as a postfix operator to this expression? + #[inline] pub fn is_valid_postfix(&self, token: &Token) -> bool { match token { #[cfg(not(feature = "no_object"))] @@ -1591,7 +1626,7 @@ impl Expr { } } /// Recursively walk this expression. - #[inline(always)] + #[inline] pub fn walk<'a>(&'a self, path: &mut Vec>, on_node: &mut impl FnMut(&[ASTNode])) { path.push(self.into()); on_node(path); diff --git a/src/engine_api.rs b/src/engine_api.rs index 9ac38a97..c2f0bb28 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -1832,7 +1832,7 @@ impl Engine { let lib = Default::default(); let stmt = crate::stdlib::mem::take(ast.statements_mut()); - crate::optimize::optimize_into_ast(self, scope, stmt, lib, optimization_level) + crate::optimize::optimize_into_ast(self, scope, stmt.into_vec(), lib, optimization_level) } /// Generate a list of all registered functions. /// diff --git a/src/lib.rs b/src/lib.rs index d36ec5b9..503890d5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -192,7 +192,7 @@ pub use token::{get_next_token, parse_string_literal, InputStream, Token, Tokeni #[deprecated = "this type is volatile and may change"] pub use ast::{ ASTNode, BinaryExpr, CustomExpr, Expr, FloatWrapper, FnCallExpr, FnHash, Ident, OpAssignment, - ReturnType, ScriptFnDef, Stmt, + ReturnType, ScriptFnDef, Stmt, StmtBlock, }; #[cfg(feature = "internals")] diff --git a/src/module/mod.rs b/src/module/mod.rs index 0727c8fb..2a0c2edc 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -39,6 +39,7 @@ pub enum FnNamespace { } impl Default for FnNamespace { + #[inline(always)] fn default() -> Self { Self::Internal } @@ -150,6 +151,7 @@ pub struct Module { } impl Default for Module { + #[inline(always)] fn default() -> Self { Self { id: None, @@ -226,6 +228,7 @@ impl AsRef for Module { impl> Add for &Module { type Output = Module; + #[inline(always)] fn add(self, rhs: M) -> Self::Output { let mut module = self.clone(); module.merge(rhs.as_ref()); @@ -236,6 +239,7 @@ impl> Add for &Module { impl> Add for Module { type Output = Self; + #[inline(always)] fn add(mut self, rhs: M) -> Self::Output { self.merge(rhs.as_ref()); self @@ -243,6 +247,7 @@ impl> Add for Module { } impl> AddAssign for Module { + #[inline(always)] fn add_assign(&mut self, rhs: M) { self.combine(rhs.into()); } @@ -1953,16 +1958,19 @@ impl Module { } /// Does a type iterator exist in the entire module tree? + #[inline(always)] pub fn contains_qualified_iter(&self, id: TypeId) -> bool { self.all_type_iterators.contains_key(&id) } /// Does a type iterator exist in the module? + #[inline(always)] pub fn contains_iter(&self, id: TypeId) -> bool { self.type_iterators.contains_key(&id) } /// Set a type iterator into the [`Module`]. + #[inline(always)] pub fn set_iter(&mut self, typ: TypeId, func: IteratorFn) -> &mut Self { self.type_iterators.insert(typ, func); self.indexed = false; @@ -1971,6 +1979,7 @@ impl Module { } /// Set a type iterator into the [`Module`]. + #[inline(always)] pub fn set_iterable(&mut self) -> &mut Self where T: Variant + Clone + IntoIterator, @@ -1982,6 +1991,7 @@ impl Module { } /// Set an iterator type into the [`Module`] as a type iterator. + #[inline(always)] pub fn set_iterator(&mut self) -> &mut Self where T: Variant + Clone + Iterator, @@ -1993,11 +2003,13 @@ impl Module { } /// Get the specified type iterator. + #[inline(always)] pub(crate) fn get_qualified_iter(&self, id: TypeId) -> Option { self.all_type_iterators.get(&id).cloned() } /// Get the specified type iterator. + #[inline(always)] pub(crate) fn get_iter(&self, id: TypeId) -> Option { self.type_iterators.get(&id).cloned() } @@ -2021,6 +2033,7 @@ pub struct NamespaceRef { } impl fmt::Debug for NamespaceRef { + #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(index) = self.index { write!(f, "{} -> ", index)?; @@ -2040,18 +2053,21 @@ impl fmt::Debug for NamespaceRef { impl Deref for NamespaceRef { type Target = StaticVec; + #[inline(always)] fn deref(&self) -> &Self::Target { &self.path } } impl DerefMut for NamespaceRef { + #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.path } } impl fmt::Display for NamespaceRef { + #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for Ident { name, .. } in self.path.iter() { write!(f, "{}{}", name, Token::DoubleColon.syntax())?; @@ -2061,6 +2077,7 @@ impl fmt::Display for NamespaceRef { } impl From> for NamespaceRef { + #[inline(always)] fn from(path: StaticVec) -> Self { Self { index: None, path } } @@ -2068,11 +2085,13 @@ impl From> for NamespaceRef { impl NamespaceRef { /// Get the [`Scope`][crate::Scope] index offset. + #[inline(always)] pub(crate) fn index(&self) -> Option { self.index } /// Set the [`Scope`][crate::Scope] index offset. #[cfg(not(feature = "no_module"))] + #[inline(always)] pub(crate) fn set_index(&mut self, index: Option) { self.index = index } diff --git a/src/parse_error.rs b/src/parse_error.rs index a965af0b..b31608b6 100644 --- a/src/parse_error.rs +++ b/src/parse_error.rs @@ -38,6 +38,7 @@ pub enum LexError { impl Error for LexError {} impl fmt::Display for LexError { + #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::UnexpectedInput(s) => write!(f, "Unexpected '{}'", s), @@ -293,7 +294,7 @@ pub struct ParseError(pub Box, pub Position); impl Error for ParseError {} impl fmt::Display for ParseError { - #[inline] + #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.0, f)?; diff --git a/src/parser.rs b/src/parser.rs index 56460f8b..1c19ab8c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2434,7 +2434,7 @@ fn parse_stmt( ) -> Result { use AccessMode::{ReadOnly, ReadWrite}; - let mut _comments: Vec = Default::default(); + let mut _comments: StaticVec = Default::default(); #[cfg(not(feature = "no_function"))] { @@ -2678,7 +2678,7 @@ fn parse_fn( lib: &mut FunctionsLib, access: FnAccess, mut settings: ParseSettings, - comments: Vec, + comments: StaticVec, ) -> Result { #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; diff --git a/src/utils.rs b/src/utils.rs index a51501e9..dd768752 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -116,16 +116,19 @@ pub(crate) fn combine_hashes(a: u64, b: u64) -> u64 { pub struct HashableHashMap(HashMap); impl From> for HashableHashMap { + #[inline(always)] fn from(value: HashMap) -> Self { Self(value) } } impl AsRef> for HashableHashMap { + #[inline(always)] fn as_ref(&self) -> &HashMap { &self.0 } } impl AsMut> for HashableHashMap { + #[inline(always)] fn as_mut(&mut self) -> &mut HashMap { &mut self.0 } @@ -133,21 +136,25 @@ impl AsMut> for HashableHashMap impl Deref for HashableHashMap { type Target = HashMap; + #[inline(always)] fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for HashableHashMap { + #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl Debug for HashableHashMap { + #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } impl Hash for HashableHashMap { + #[inline(always)] fn hash(&self, state: &mut B) { let mut keys: Vec<_> = self.0.keys().collect(); keys.sort(); diff --git a/tests/optimizer.rs b/tests/optimizer.rs index 29c1c991..4c7cfb49 100644 --- a/tests/optimizer.rs +++ b/tests/optimizer.rs @@ -55,20 +55,18 @@ fn test_optimizer_parse() -> Result<(), Box> { let ast = engine.compile("{ const DECISION = false; if DECISION { 42 } else { 123 } }")?; - assert!(format!("{:?}", ast).starts_with(r#"AST { source: None, statements: [Block([Const(BoolConstant(false, 1:20), Ident("DECISION" @ 1:9), false, 1:3), Expr(IntegerConstant(123, 1:53))], 1:1)], functions: Module("#)); + assert!(format!("{:?}", ast).starts_with(r#"AST { source: None, body: [Block([Const(BoolConstant(false, 1:20), Ident("DECISION" @ 1:9), false, 1:3), Expr(IntegerConstant(123, 1:53))], 1:1)], functions: Module("#)); let ast = engine.compile("if 1 == 2 { 42 }")?; - assert!( - format!("{:?}", ast).starts_with("AST { source: None, statements: [], functions: Module(") - ); + assert!(format!("{:?}", ast).starts_with("AST { source: None, body: [], functions: Module(")); engine.set_optimization_level(OptimizationLevel::Full); let ast = engine.compile("abs(-42)")?; assert!(format!("{:?}", ast) - .starts_with(r"AST { source: None, statements: [Expr(IntegerConstant(42, 1:1))]")); + .starts_with(r"AST { source: None, body: [Expr(IntegerConstant(42, 1:1))]")); Ok(()) }