Factor tuples into structs to aid in understanding.

This commit is contained in:
Stephen Chung
2022-01-28 10:11:40 +08:00
parent b86dd3f586
commit 20baae71d4
11 changed files with 386 additions and 272 deletions

View File

@@ -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();