Factor tuples into structs to aid in understanding.
This commit is contained in:
148
src/optimizer.rs
148
src/optimizer.rs
@@ -412,26 +412,26 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
match stmt {
|
||||
// var = var op expr => var op= expr
|
||||
Stmt::Assignment(x, _)
|
||||
if x.1.is_none()
|
||||
&& x.0.is_variable_access(true)
|
||||
&& matches!(&x.2, Expr::FnCall(x2, _)
|
||||
if x.0.is_none()
|
||||
&& x.1.lhs.is_variable_access(true)
|
||||
&& matches!(&x.1.rhs, Expr::FnCall(x2, _)
|
||||
if Token::lookup_from_syntax(&x2.name).map(|t| t.has_op_assignment()).unwrap_or(false)
|
||||
&& x2.args.len() == 2
|
||||
&& x2.args[0].get_variable_name(true) == x.0.get_variable_name(true)
|
||||
&& x2.args[0].get_variable_name(true) == x.1.lhs.get_variable_name(true)
|
||||
) =>
|
||||
{
|
||||
match x.2 {
|
||||
match x.1.rhs {
|
||||
Expr::FnCall(ref mut x2, _) => {
|
||||
state.set_dirty();
|
||||
x.1 = Some(OpAssignment::new_from_base(&x2.name));
|
||||
x.0 = Some(OpAssignment::new_from_base(&x2.name));
|
||||
|
||||
let value = mem::take(&mut x2.args[1]);
|
||||
|
||||
if let Expr::Stack(slot, pos) = value {
|
||||
x.2 =
|
||||
x.1.rhs =
|
||||
Expr::from_dynamic(mem::take(x2.constants.get_mut(slot).unwrap()), pos);
|
||||
} else {
|
||||
x.2 = value;
|
||||
x.1.rhs = value;
|
||||
}
|
||||
}
|
||||
ref expr => unreachable!("Expr::FnCall expected but gets {:?}", expr),
|
||||
@@ -439,13 +439,12 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
}
|
||||
|
||||
// expr op= expr
|
||||
Stmt::Assignment(x, _) => match x.0 {
|
||||
Expr::Variable(_, _, _) => optimize_expr(&mut x.2, state, false),
|
||||
_ => {
|
||||
optimize_expr(&mut x.0, state, false);
|
||||
optimize_expr(&mut x.2, state, false);
|
||||
Stmt::Assignment(x, _) => {
|
||||
if !x.1.lhs.is_variable_access(false) {
|
||||
optimize_expr(&mut x.1.lhs, state, false);
|
||||
}
|
||||
},
|
||||
optimize_expr(&mut x.1.rhs, state, false);
|
||||
}
|
||||
|
||||
// if expr {}
|
||||
Stmt::If(condition, x, _) if x.0.is_empty() && x.1.is_empty() => {
|
||||
@@ -502,31 +501,39 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
value.hash(hasher);
|
||||
let hash = hasher.finish();
|
||||
|
||||
let table = &mut x.0;
|
||||
let cases = &mut x.cases;
|
||||
|
||||
// First check hashes
|
||||
if let Some(block) = table.get_mut(&hash) {
|
||||
if let Some(mut condition) = mem::take(&mut block.0) {
|
||||
if let Some(block) = cases.get_mut(&hash) {
|
||||
if let Some(mut condition) = mem::take(&mut block.condition) {
|
||||
// switch const { case if condition => stmt, _ => def } => if condition { stmt } else { def }
|
||||
optimize_expr(&mut condition, state, false);
|
||||
|
||||
let def_stmt =
|
||||
optimize_stmt_block(mem::take(&mut *x.1), state, true, true, false);
|
||||
optimize_stmt_block(mem::take(&mut x.def_case), state, true, true, false);
|
||||
|
||||
*stmt = Stmt::If(
|
||||
condition,
|
||||
Box::new((
|
||||
mem::take(&mut block.1),
|
||||
Stmt::Block(def_stmt.into_boxed_slice(), x.1.position().or_else(*pos))
|
||||
.into(),
|
||||
mem::take(&mut block.statements),
|
||||
Stmt::Block(
|
||||
def_stmt.into_boxed_slice(),
|
||||
x.def_case.position().or_else(*pos),
|
||||
)
|
||||
.into(),
|
||||
)),
|
||||
match_expr.position(),
|
||||
);
|
||||
} else {
|
||||
// Promote the matched case
|
||||
let statements =
|
||||
optimize_stmt_block(mem::take(&mut *block.1), state, true, true, false);
|
||||
*stmt = Stmt::Block(statements.into_boxed_slice(), block.1.position());
|
||||
let statements = optimize_stmt_block(
|
||||
mem::take(&mut block.statements),
|
||||
state,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
*stmt = Stmt::Block(statements.into_boxed_slice(), block.statements.position());
|
||||
}
|
||||
|
||||
state.set_dirty();
|
||||
@@ -534,34 +541,39 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
}
|
||||
|
||||
// Then check ranges
|
||||
let ranges = &mut x.2;
|
||||
let ranges = &mut x.ranges;
|
||||
|
||||
if value.is::<INT>() && !ranges.is_empty() {
|
||||
let value = value.as_int().expect("`INT`");
|
||||
|
||||
// Only one range or all ranges without conditions
|
||||
if ranges.len() == 1 || ranges.iter().all(|(_, _, _, c, _)| c.is_none()) {
|
||||
for (_, _, _, condition, stmt_block) in
|
||||
if ranges.len() == 1 || ranges.iter().all(|(_, _, _, c)| !c.has_condition()) {
|
||||
for (_, _, _, block) in
|
||||
ranges
|
||||
.iter_mut()
|
||||
.filter(|&&mut (start, end, inclusive, _, _)| {
|
||||
.filter(|&&mut (start, end, inclusive, _)| {
|
||||
(!inclusive && (start..end).contains(&value))
|
||||
|| (inclusive && (start..=end).contains(&value))
|
||||
})
|
||||
{
|
||||
if let Some(mut condition) = mem::take(condition) {
|
||||
if let Some(mut condition) = mem::take(&mut block.condition) {
|
||||
// switch const { range if condition => stmt, _ => def } => if condition { stmt } else { def }
|
||||
optimize_expr(&mut condition, state, false);
|
||||
|
||||
let def_stmt =
|
||||
optimize_stmt_block(mem::take(&mut *x.1), state, true, true, false);
|
||||
let def_stmt = optimize_stmt_block(
|
||||
mem::take(&mut x.def_case),
|
||||
state,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
*stmt = Stmt::If(
|
||||
condition,
|
||||
Box::new((
|
||||
mem::take(stmt_block),
|
||||
mem::take(&mut block.statements),
|
||||
Stmt::Block(
|
||||
def_stmt.into_boxed_slice(),
|
||||
x.1.position().or_else(*pos),
|
||||
x.def_case.position().or_else(*pos),
|
||||
)
|
||||
.into(),
|
||||
)),
|
||||
@@ -569,11 +581,13 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
);
|
||||
} else {
|
||||
// Promote the matched case
|
||||
let statements = mem::take(&mut **stmt_block);
|
||||
let statements = mem::take(&mut *block.statements);
|
||||
let statements =
|
||||
optimize_stmt_block(statements, state, true, true, false);
|
||||
*stmt =
|
||||
Stmt::Block(statements.into_boxed_slice(), stmt_block.position());
|
||||
*stmt = Stmt::Block(
|
||||
statements.into_boxed_slice(),
|
||||
block.statements.position(),
|
||||
);
|
||||
}
|
||||
|
||||
state.set_dirty();
|
||||
@@ -581,14 +595,14 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
}
|
||||
} else {
|
||||
// Multiple ranges - clear the table and just keep the right ranges
|
||||
if !table.is_empty() {
|
||||
if !cases.is_empty() {
|
||||
state.set_dirty();
|
||||
table.clear();
|
||||
cases.clear();
|
||||
}
|
||||
|
||||
let old_ranges_len = ranges.len();
|
||||
|
||||
ranges.retain(|&mut (start, end, inclusive, _, _)| {
|
||||
ranges.retain(|&mut (start, end, inclusive, _)| {
|
||||
(!inclusive && (start..end).contains(&value))
|
||||
|| (inclusive && (start..=end).contains(&value))
|
||||
});
|
||||
@@ -597,16 +611,16 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
state.set_dirty();
|
||||
}
|
||||
|
||||
for (_, _, _, condition, stmt_block) in ranges.iter_mut() {
|
||||
let statements = mem::take(&mut **stmt_block);
|
||||
**stmt_block =
|
||||
for (_, _, _, block) in ranges.iter_mut() {
|
||||
let statements = mem::take(&mut *block.statements);
|
||||
*block.statements =
|
||||
optimize_stmt_block(statements, state, preserve_result, true, false);
|
||||
|
||||
if let Some(mut c) = mem::take(condition) {
|
||||
optimize_expr(&mut c, state, false);
|
||||
match c {
|
||||
if let Some(mut condition) = mem::take(&mut block.condition) {
|
||||
optimize_expr(&mut condition, state, false);
|
||||
match condition {
|
||||
Expr::Unit(_) | Expr::BoolConstant(true, _) => state.set_dirty(),
|
||||
_ => *condition = Some(c),
|
||||
_ => block.condition = Some(condition),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -616,35 +630,41 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
|
||||
// Promote the default case
|
||||
state.set_dirty();
|
||||
let def_stmt = optimize_stmt_block(mem::take(&mut *x.1), state, true, true, false);
|
||||
*stmt = Stmt::Block(def_stmt.into_boxed_slice(), x.1.position().or_else(*pos));
|
||||
let def_stmt =
|
||||
optimize_stmt_block(mem::take(&mut x.def_case), state, true, true, false);
|
||||
*stmt = Stmt::Block(
|
||||
def_stmt.into_boxed_slice(),
|
||||
x.def_case.position().or_else(*pos),
|
||||
);
|
||||
}
|
||||
// switch
|
||||
Stmt::Switch(match_expr, x, _) => {
|
||||
optimize_expr(match_expr, state, false);
|
||||
x.0.values_mut().for_each(|block| {
|
||||
let statements = mem::take(&mut *block.1);
|
||||
*block.1 = optimize_stmt_block(statements, state, preserve_result, true, false);
|
||||
x.cases.values_mut().for_each(|block| {
|
||||
let statements = mem::take(&mut *block.statements);
|
||||
*block.statements =
|
||||
optimize_stmt_block(statements, state, preserve_result, true, false);
|
||||
|
||||
if let Some(mut condition) = mem::take(&mut block.0) {
|
||||
if let Some(mut condition) = mem::take(&mut block.condition) {
|
||||
optimize_expr(&mut condition, state, false);
|
||||
match condition {
|
||||
Expr::Unit(_) | Expr::BoolConstant(true, _) => state.set_dirty(),
|
||||
_ => block.0 = Some(condition),
|
||||
_ => block.condition = Some(condition),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Remove false cases
|
||||
while let Some((&key, _)) = x.0.iter().find(|(_, block)| match block.0 {
|
||||
while let Some((&key, _)) = x.cases.iter().find(|(_, block)| match block.condition {
|
||||
Some(Expr::BoolConstant(false, _)) => true,
|
||||
_ => false,
|
||||
}) {
|
||||
state.set_dirty();
|
||||
x.0.remove(&key);
|
||||
x.cases.remove(&key);
|
||||
}
|
||||
|
||||
*x.1 = optimize_stmt_block(mem::take(&mut *x.1), state, preserve_result, true, false);
|
||||
let def_block = mem::take(&mut *x.def_case);
|
||||
*x.def_case = optimize_stmt_block(def_block, state, preserve_result, true, false);
|
||||
}
|
||||
|
||||
// while false { block } -> Noop
|
||||
@@ -727,19 +747,21 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
}
|
||||
}
|
||||
// try { pure try_block } catch ( var ) { catch_block } -> try_block
|
||||
Stmt::TryCatch(x, _) if x.0.iter().all(Stmt::is_pure) => {
|
||||
Stmt::TryCatch(x, _) if x.try_block.iter().all(Stmt::is_pure) => {
|
||||
// If try block is pure, there will never be any exceptions
|
||||
state.set_dirty();
|
||||
*stmt = Stmt::Block(
|
||||
optimize_stmt_block(mem::take(&mut *x.0), state, false, true, false)
|
||||
optimize_stmt_block(mem::take(&mut *x.try_block), state, false, true, false)
|
||||
.into_boxed_slice(),
|
||||
x.0.position(),
|
||||
x.try_block.position(),
|
||||
);
|
||||
}
|
||||
// try { try_block } catch ( var ) { catch_block }
|
||||
Stmt::TryCatch(x, _) => {
|
||||
*x.0 = optimize_stmt_block(mem::take(&mut *x.0), state, false, true, false);
|
||||
*x.2 = optimize_stmt_block(mem::take(&mut *x.2), state, false, true, false);
|
||||
*x.try_block =
|
||||
optimize_stmt_block(mem::take(&mut *x.try_block), state, false, true, false);
|
||||
*x.catch_block =
|
||||
optimize_stmt_block(mem::take(&mut *x.catch_block), state, false, true, false);
|
||||
}
|
||||
// func(...)
|
||||
Stmt::Expr(expr @ Expr::FnCall(_, _)) => {
|
||||
@@ -800,8 +822,8 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Dot(x,_, _) if !_chaining => match (&mut x.lhs, &mut x.rhs) {
|
||||
// map.string
|
||||
(Expr::Map(m, pos), Expr::Property(p)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
|
||||
let prop = p.2.0.as_str();
|
||||
(Expr::Map(m, pos), Expr::Property(p, _)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
|
||||
let prop = p.2.as_str();
|
||||
// Map literal where everything is pure - promote the indexed item.
|
||||
// All other items can be thrown away.
|
||||
state.set_dirty();
|
||||
|
Reference in New Issue
Block a user