Change optimize_ast to take optimization level as parameter.
This commit is contained in:
parent
b74c85f04c
commit
e0bb2e5c97
13
README.md
13
README.md
@ -1753,6 +1753,19 @@ An [`Engine`]'s optimization level is set via a call to `set_optimization_level`
|
|||||||
engine.set_optimization_level(rhai::OptimizationLevel::Full);
|
engine.set_optimization_level(rhai::OptimizationLevel::Full);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If it is ever needed to _re_-optimize an `AST`, use the `optimize_ast` method.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Compile script to AST
|
||||||
|
let ast = engine.compile("40 + 2")?;
|
||||||
|
|
||||||
|
// Create a new 'Scope' - put constants in it to aid optimization if using 'OptimizationLevel::Full'
|
||||||
|
let scope = Scope::new();
|
||||||
|
|
||||||
|
// Re-optimize the AST
|
||||||
|
let ast = engine.optimize_ast(&scope, &ast, OptimizationLevel::Full);
|
||||||
|
```
|
||||||
|
|
||||||
When the optimization level is [`OptimizationLevel::Full`], the [`Engine`] assumes all functions to be _pure_ and will _eagerly_
|
When the optimization level is [`OptimizationLevel::Full`], the [`Engine`] assumes all functions to be _pure_ and will _eagerly_
|
||||||
evaluated all function calls with constant arguments, using the result to replace the call. This also applies to all operators
|
evaluated all function calls with constant arguments, using the result to replace the call. This also applies to all operators
|
||||||
(which are implemented as functions). For instance, the same example above:
|
(which are implemented as functions). For instance, the same example above:
|
||||||
|
@ -145,9 +145,7 @@ fn main() {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
{
|
{
|
||||||
engine.set_optimization_level(OptimizationLevel::Full);
|
ast = engine.optimize_ast(&scope, r, OptimizationLevel::Full);
|
||||||
ast = engine.optimize_ast(&scope, r);
|
|
||||||
engine.set_optimization_level(OptimizationLevel::None);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "no_optimize")]
|
#[cfg(feature = "no_optimize")]
|
||||||
|
11
src/api.rs
11
src/api.rs
@ -10,7 +10,7 @@ use crate::result::EvalAltResult;
|
|||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
use crate::optimize::optimize_into_ast;
|
use crate::optimize::{optimize_into_ast, OptimizationLevel};
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::{type_name, TypeId},
|
any::{type_name, TypeId},
|
||||||
@ -902,9 +902,14 @@ impl<'e> Engine<'e> {
|
|||||||
/// compiled just once. Before evaluation, constants are passed into the `Engine` via an external scope
|
/// compiled just once. Before evaluation, constants are passed into the `Engine` via an external scope
|
||||||
/// (i.e. with `scope.push_constant(...)`). Then, the `AST is cloned and the copy re-optimized before running.
|
/// (i.e. with `scope.push_constant(...)`). Then, the `AST is cloned and the copy re-optimized before running.
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
pub fn optimize_ast(&self, scope: &Scope, ast: AST) -> AST {
|
pub fn optimize_ast(
|
||||||
|
&self,
|
||||||
|
scope: &Scope,
|
||||||
|
ast: AST,
|
||||||
|
optimization_level: OptimizationLevel,
|
||||||
|
) -> AST {
|
||||||
let fn_lib = ast.1.iter().map(|fn_def| fn_def.as_ref().clone()).collect();
|
let fn_lib = ast.1.iter().map(|fn_def| fn_def.as_ref().clone()).collect();
|
||||||
optimize_into_ast(self, scope, ast.0, fn_lib)
|
optimize_into_ast(self, scope, ast.0, fn_lib, optimization_level)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Override default action of `print` (print to stdout using `println!`)
|
/// Override default action of `print` (print to stdout using `println!`)
|
||||||
|
@ -41,16 +41,23 @@ struct State<'a> {
|
|||||||
engine: &'a Engine<'a>,
|
engine: &'a Engine<'a>,
|
||||||
/// Library of script-defined functions.
|
/// Library of script-defined functions.
|
||||||
fn_lib: &'a [(&'a str, usize)],
|
fn_lib: &'a [(&'a str, usize)],
|
||||||
|
/// Optimization level.
|
||||||
|
optimization_level: OptimizationLevel,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> State<'a> {
|
impl<'a> State<'a> {
|
||||||
/// Create a new State.
|
/// Create a new State.
|
||||||
pub fn new(engine: &'a Engine<'a>, fn_lib: &'a [(&'a str, usize)]) -> Self {
|
pub fn new(
|
||||||
|
engine: &'a Engine<'a>,
|
||||||
|
fn_lib: &'a [(&'a str, usize)],
|
||||||
|
level: OptimizationLevel,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
changed: false,
|
changed: false,
|
||||||
constants: vec![],
|
constants: vec![],
|
||||||
engine,
|
engine,
|
||||||
fn_lib,
|
fn_lib,
|
||||||
|
optimization_level: level,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Reset the state from dirty to clean.
|
/// Reset the state from dirty to clean.
|
||||||
@ -501,7 +508,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
|
|
||||||
// Eagerly call functions
|
// Eagerly call functions
|
||||||
Expr::FunctionCall(id, args, def_value, pos)
|
Expr::FunctionCall(id, args, def_value, pos)
|
||||||
if state.engine.optimization_level == OptimizationLevel::Full // full optimizations
|
if state.optimization_level == OptimizationLevel::Full // full optimizations
|
||||||
&& args.iter().all(|expr| expr.is_constant()) // all arguments are constants
|
&& args.iter().all(|expr| expr.is_constant()) // all arguments are constants
|
||||||
=> {
|
=> {
|
||||||
// First search in script-defined functions (can override built-in)
|
// First search in script-defined functions (can override built-in)
|
||||||
@ -560,14 +567,15 @@ pub(crate) fn optimize<'a>(
|
|||||||
engine: &Engine<'a>,
|
engine: &Engine<'a>,
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
fn_lib: &'a [(&'a str, usize)],
|
fn_lib: &'a [(&'a str, usize)],
|
||||||
|
level: OptimizationLevel,
|
||||||
) -> Vec<Stmt> {
|
) -> Vec<Stmt> {
|
||||||
// If optimization level is None then skip optimizing
|
// If optimization level is None then skip optimizing
|
||||||
if engine.optimization_level == OptimizationLevel::None {
|
if level == OptimizationLevel::None {
|
||||||
return statements;
|
return statements;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the state
|
// Set up the state
|
||||||
let mut state = State::new(engine, fn_lib);
|
let mut state = State::new(engine, fn_lib, level);
|
||||||
|
|
||||||
// Add constants from the scope into the state
|
// Add constants from the scope into the state
|
||||||
scope
|
scope
|
||||||
@ -640,6 +648,7 @@ pub fn optimize_into_ast(
|
|||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
statements: Vec<Stmt>,
|
statements: Vec<Stmt>,
|
||||||
functions: Vec<FnDef>,
|
functions: Vec<FnDef>,
|
||||||
|
level: OptimizationLevel,
|
||||||
) -> AST {
|
) -> AST {
|
||||||
let fn_lib: Vec<_> = functions
|
let fn_lib: Vec<_> = functions
|
||||||
.iter()
|
.iter()
|
||||||
@ -651,11 +660,12 @@ pub fn optimize_into_ast(
|
|||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|mut fn_def| {
|
.map(|mut fn_def| {
|
||||||
if engine.optimization_level != OptimizationLevel::None {
|
if level != OptimizationLevel::None {
|
||||||
let pos = fn_def.body.position();
|
let pos = fn_def.body.position();
|
||||||
|
|
||||||
// Optimize the function body
|
// Optimize the function body
|
||||||
let mut body = optimize(vec![fn_def.body], engine, &Scope::new(), &fn_lib);
|
let mut body =
|
||||||
|
optimize(vec![fn_def.body], engine, &Scope::new(), &fn_lib, level);
|
||||||
|
|
||||||
// {} -> Noop
|
// {} -> Noop
|
||||||
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
|
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
|
||||||
@ -675,10 +685,10 @@ pub fn optimize_into_ast(
|
|||||||
);
|
);
|
||||||
|
|
||||||
AST(
|
AST(
|
||||||
match engine.optimization_level {
|
match level {
|
||||||
OptimizationLevel::None => statements,
|
OptimizationLevel::None => statements,
|
||||||
OptimizationLevel::Simple | OptimizationLevel::Full => {
|
OptimizationLevel::Simple | OptimizationLevel::Full => {
|
||||||
optimize(statements, engine, &scope, &fn_lib)
|
optimize(statements, engine, &scope, &fn_lib, level)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
|
@ -2781,7 +2781,13 @@ pub fn parse_global_expr<'a, 'e>(
|
|||||||
Ok(
|
Ok(
|
||||||
// Optimize AST
|
// Optimize AST
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
optimize_into_ast(engine, scope, vec![Stmt::Expr(Box::new(expr))], vec![]),
|
optimize_into_ast(
|
||||||
|
engine,
|
||||||
|
scope,
|
||||||
|
vec![Stmt::Expr(Box::new(expr))],
|
||||||
|
vec![],
|
||||||
|
engine.optimization_level,
|
||||||
|
),
|
||||||
//
|
//
|
||||||
// Do not optimize AST if `no_optimize`
|
// Do not optimize AST if `no_optimize`
|
||||||
#[cfg(feature = "no_optimize")]
|
#[cfg(feature = "no_optimize")]
|
||||||
@ -2866,7 +2872,13 @@ pub fn parse<'a, 'e>(
|
|||||||
Ok(
|
Ok(
|
||||||
// Optimize AST
|
// Optimize AST
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
optimize_into_ast(engine, scope, statements, functions),
|
optimize_into_ast(
|
||||||
|
engine,
|
||||||
|
scope,
|
||||||
|
statements,
|
||||||
|
functions,
|
||||||
|
engine.optimization_level,
|
||||||
|
),
|
||||||
//
|
//
|
||||||
// Do not optimize AST if `no_optimize`
|
// Do not optimize AST if `no_optimize`
|
||||||
#[cfg(feature = "no_optimize")]
|
#[cfg(feature = "no_optimize")]
|
||||||
|
Loading…
Reference in New Issue
Block a user