Optimize hot path of operators calling.
This commit is contained in:
@@ -1,10 +1,9 @@
|
||||
use crate::any::Dynamic;
|
||||
use crate::calc_fn_hash;
|
||||
use crate::engine::{
|
||||
Engine, FunctionsLib, State as EngineState, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT,
|
||||
Engine, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT,
|
||||
KEYWORD_TYPE_OF,
|
||||
};
|
||||
use crate::fn_native::FnCallArgs;
|
||||
use crate::parser::{map_dynamic_to_expr, Expr, FnDef, ReturnType, Stmt, AST};
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
|
||||
@@ -53,23 +52,19 @@ struct State<'a> {
|
||||
/// An `Engine` instance for eager function evaluation.
|
||||
engine: &'a Engine,
|
||||
/// Library of script-defined functions.
|
||||
fn_lib: &'a [(&'a str, usize)],
|
||||
lib: &'a FunctionsLib,
|
||||
/// Optimization level.
|
||||
optimization_level: OptimizationLevel,
|
||||
}
|
||||
|
||||
impl<'a> State<'a> {
|
||||
/// Create a new State.
|
||||
pub fn new(
|
||||
engine: &'a Engine,
|
||||
fn_lib: &'a [(&'a str, usize)],
|
||||
level: OptimizationLevel,
|
||||
) -> Self {
|
||||
pub fn new(engine: &'a Engine, lib: &'a FunctionsLib, level: OptimizationLevel) -> Self {
|
||||
Self {
|
||||
changed: false,
|
||||
constants: vec![],
|
||||
engine,
|
||||
fn_lib,
|
||||
lib,
|
||||
optimization_level: level,
|
||||
}
|
||||
}
|
||||
@@ -128,7 +123,8 @@ fn call_fn_with_constant_arguments(
|
||||
.engine
|
||||
.call_fn_raw(
|
||||
None,
|
||||
&mut EngineState::new(&Default::default()),
|
||||
&mut Default::default(),
|
||||
state.lib,
|
||||
fn_name,
|
||||
(hash_fn, 0),
|
||||
arg_values.iter_mut().collect::<StaticVec<_>>().as_mut(),
|
||||
@@ -557,7 +553,10 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
||||
|
||||
// First search in script-defined functions (can override built-in)
|
||||
// Cater for both normal function call style and method call style (one additional arguments)
|
||||
if !*native_only && state.fn_lib.iter().find(|(id, len)| *id == name && (*len == args.len() || *len == args.len() + 1)).is_some() {
|
||||
if !*native_only && state.lib.values().find(|f|
|
||||
&f.name == name
|
||||
&& (args.len()..=args.len() + 1).contains(&f.params.len())
|
||||
).is_some() {
|
||||
// A script-defined function overrides the built-in function - do not make the call
|
||||
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
|
||||
return Expr::FnCall(x);
|
||||
@@ -615,11 +614,11 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
||||
}
|
||||
}
|
||||
|
||||
fn optimize<'a>(
|
||||
fn optimize(
|
||||
statements: Vec<Stmt>,
|
||||
engine: &Engine,
|
||||
scope: &Scope,
|
||||
fn_lib: &'a [(&'a str, usize)],
|
||||
lib: &FunctionsLib,
|
||||
level: OptimizationLevel,
|
||||
) -> Vec<Stmt> {
|
||||
// If optimization level is None then skip optimizing
|
||||
@@ -628,7 +627,7 @@ fn optimize<'a>(
|
||||
}
|
||||
|
||||
// Set up the state
|
||||
let mut state = State::new(engine, fn_lib, level);
|
||||
let mut state = State::new(engine, lib, level);
|
||||
|
||||
// Add constants from the scope into the state
|
||||
scope
|
||||
@@ -713,41 +712,35 @@ pub fn optimize_into_ast(
|
||||
const level: OptimizationLevel = OptimizationLevel::None;
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
let fn_lib_values: StaticVec<_> = functions
|
||||
.iter()
|
||||
.map(|fn_def| (fn_def.name.as_str(), fn_def.params.len()))
|
||||
.collect();
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
let fn_lib = fn_lib_values.as_ref();
|
||||
|
||||
#[cfg(feature = "no_function")]
|
||||
const fn_lib: &[(&str, usize)] = &[];
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
let lib = FunctionsLib::from_iter(functions.iter().cloned().map(|mut fn_def| {
|
||||
let lib = {
|
||||
if !level.is_none() {
|
||||
let pos = fn_def.body.position();
|
||||
let lib = FunctionsLib::from_iter(functions.iter().cloned());
|
||||
|
||||
// Optimize the function body
|
||||
let mut body = optimize(vec![fn_def.body], engine, &Scope::new(), fn_lib, level);
|
||||
FunctionsLib::from_iter(functions.into_iter().map(|mut fn_def| {
|
||||
let pos = fn_def.body.position();
|
||||
|
||||
// {} -> Noop
|
||||
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
|
||||
// { return val; } -> val
|
||||
Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Return => {
|
||||
Stmt::Expr(Box::new(x.1.unwrap()))
|
||||
}
|
||||
// { return; } -> ()
|
||||
Stmt::ReturnWithVal(x) if x.1.is_none() && (x.0).0 == ReturnType::Return => {
|
||||
Stmt::Expr(Box::new(Expr::Unit((x.0).1)))
|
||||
}
|
||||
// All others
|
||||
stmt => stmt,
|
||||
};
|
||||
// Optimize the function body
|
||||
let mut body = optimize(vec![fn_def.body], engine, &Scope::new(), &lib, level);
|
||||
|
||||
// {} -> Noop
|
||||
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
|
||||
// { return val; } -> val
|
||||
Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Return => {
|
||||
Stmt::Expr(Box::new(x.1.unwrap()))
|
||||
}
|
||||
// { return; } -> ()
|
||||
Stmt::ReturnWithVal(x) if x.1.is_none() && (x.0).0 == ReturnType::Return => {
|
||||
Stmt::Expr(Box::new(Expr::Unit((x.0).1)))
|
||||
}
|
||||
// All others
|
||||
stmt => stmt,
|
||||
};
|
||||
fn_def
|
||||
}))
|
||||
} else {
|
||||
FunctionsLib::from_iter(functions.into_iter())
|
||||
}
|
||||
fn_def
|
||||
}));
|
||||
};
|
||||
|
||||
#[cfg(feature = "no_function")]
|
||||
let lib: FunctionsLib = Default::default();
|
||||
@@ -756,7 +749,7 @@ pub fn optimize_into_ast(
|
||||
match level {
|
||||
OptimizationLevel::None => statements,
|
||||
OptimizationLevel::Simple | OptimizationLevel::Full => {
|
||||
optimize(statements, engine, &scope, fn_lib, level)
|
||||
optimize(statements, engine, &scope, &lib, level)
|
||||
}
|
||||
},
|
||||
lib,
|
||||
|
Reference in New Issue
Block a user