Files
curlywas/src/constfold.rs
2021-11-01 22:59:59 +01:00

159 lines
5.8 KiB
Rust

use crate::ast;
pub fn fold_script(script: &mut ast::Script) {
for func in &mut script.functions {
fold_expr(&mut func.body);
}
}
fn fold_mem_location(mem_location: &mut ast::MemoryLocation) {
fold_expr(&mut mem_location.left);
fold_expr(&mut mem_location.right);
}
fn fold_expr(expr: &mut ast::Expression) {
use ast::BinOp::*;
match expr.expr {
ast::Expr::Block { ref mut statements, ref mut final_expression} => {
for stmt in statements {
fold_expr(stmt);
}
if let Some(ref mut expr) = final_expression {
fold_expr(expr);
}
}
ast::Expr::Let { ref mut value, .. } => {
if let Some(ref mut expr) = value {
fold_expr(expr);
}
}
ast::Expr::Poke {
ref mut mem_location,
ref mut value,
..
} => {
fold_mem_location(mem_location);
fold_expr(value);
}
ast::Expr::Peek(ref mut mem_location) => fold_mem_location(mem_location),
ast::Expr::UnaryOp { op, ref mut value } => {
fold_expr(value);
let result = match (op, &value.expr) {
(ast::UnaryOp::Negate, ast::Expr::I32Const(value)) => Some(ast::Expr::I32Const(-*value)),
_ => None
};
if let Some(result) = result {
expr.expr = result;
}
}
ast::Expr::BinOp {
ref mut left,
op,
ref mut right,
..
} => {
fold_expr(left);
fold_expr(right);
match (&left.expr, &right.expr) {
(&ast::Expr::I32Const(left), &ast::Expr::I32Const(right)) => {
let result = match op {
Add => left.wrapping_add(right),
Sub => left.wrapping_sub(right),
Mul => left.wrapping_mul(right),
Div => {
if let Some(result) = left.checked_div(right) {
result
} else {
return;
}
}
Rem => {
if let Some(result) = left.checked_rem(right) {
result
} else {
return;
}
}
And => left & right,
Or => left | right,
Xor => left ^ right,
Eq => (left == right) as i32,
Ne => (left != right) as i32,
Lt => (left < right) as i32,
Le => (left <= right) as i32,
Gt => (left > right) as i32,
Ge => (left >= right) as i32,
Lsl => left << right,
Lsr => ((left as u32) >> right) as i32,
Asr => left >> right
};
expr.expr = ast::Expr::I32Const(result);
}
(&ast::Expr::F32Const(left), &ast::Expr::F32Const(right)) => {
use ast::Expr::*;
expr.expr = match op {
Add => F32Const(left + right),
Sub => F32Const(left - right),
Mul => F32Const(left * right),
Div => F32Const(left / right),
Rem | And | Or | Xor | Lsl | Lsr | Asr => return,
Eq => I32Const((left == right) as i32),
Ne => I32Const((left != right) as i32),
Lt => I32Const((left < right) as i32),
Le => I32Const((left <= right) as i32),
Gt => I32Const((left > right) as i32),
Ge => I32Const((left >= right) as i32),
};
}
_ => (),
}
}
ast::Expr::I32Const(_) | ast::Expr::F32Const(_) | ast::Expr::Variable { .. } => (),
ast::Expr::Assign { ref mut value, .. } => fold_expr(value),
ast::Expr::LocalTee { ref mut value, .. } => fold_expr(value),
ast::Expr::Loop { ref mut block, .. } => fold_expr(block),
ast::Expr::Branch(_) => (),
ast::Expr::BranchIf {
ref mut condition, ..
} => fold_expr(condition),
ast::Expr::Cast { ref mut value, .. } => fold_expr(value),
ast::Expr::FuncCall {
ref name,
ref mut params,
..
} => {
for param in params.iter_mut() {
fold_expr(param);
}
use ast::Expr::*;
let params: Vec<_> = params.iter().map(|e| &e.expr).collect();
expr.expr = match (name.as_str(), params.as_slice()) {
("sqrt", [F32Const(v)]) if *v >= 0.0 => F32Const(v.sqrt()),
_ => return,
};
}
ast::Expr::Select {
ref mut condition,
ref mut if_true,
ref mut if_false,
..
} => {
fold_expr(condition);
fold_expr(if_true);
fold_expr(if_false);
}
ast::Expr::If {
ref mut condition, ref mut if_true, ref mut if_false
} => {
fold_expr(condition);
fold_expr(if_true);
if let Some(ref mut if_false) = if_false {
fold_expr(if_false);
}
}
ast::Expr::Return { value: Some(ref mut value) } => fold_expr(value),
ast::Expr::Return { value: None } => (),
ast::Expr::Error => unreachable!()
}
}