use crate::ir::{BinOp as B, Expr, Literal, Statement, UnOp};
pub(super) fn decode_val_comparison_constants(expr: Expr) -> Expr {
match expr {
Expr::BinOp { op: op @ (B::Lt | B::Le | B::Gt | B::Ge | B::Eq | B::Ne), left, right } => {
let new_left = decode_large_literal(*left);
let new_right = decode_large_literal(*right);
Expr::BinOp { op, left: Box::new(new_left), right: Box::new(new_right) }
}
other => other,
}
}
fn decode_large_literal(expr: Expr) -> Expr {
match &expr {
Expr::Literal(Literal::I64(v)) => {
let uv = *v as u64;
if uv >= (1u64 << 32) && uv <= (u32::MAX as u64) << 32 | 0xFFFFFFFF {
let decoded = (uv >> 32) as i64;
if decoded >= 0 && decoded <= 1000 {
return Expr::Literal(Literal::I64(decoded));
}
}
}
_ => {}
}
expr
}
pub(super) fn is_user_comparison(expr: &Expr) -> bool {
match expr {
Expr::BinOp { op: B::Lt | B::Le | B::Gt | B::Ge | B::Eq | B::Ne, left, right } => {
(has_named_var(left) || has_named_var(right))
&& !is_type_tag_check_expr(expr)
&& !is_overflow_check_expr(expr)
&& !is_arithmetic_overflow_check(expr)
}
Expr::MethodChain { .. } if has_named_var(expr) => true,
Expr::HostCall { .. } => true,
Expr::UnOp { op: UnOp::Not, operand } => is_user_comparison(operand),
_ => false,
}
}
fn is_arithmetic_overflow_check(expr: &Expr) -> bool {
match expr {
Expr::BinOp { op: B::Lt, left, right } => {
if let Expr::BinOp { op: B::Add, left: add_l, right: add_r } = left.as_ref() {
if format!("{:?}", add_l) == format!("{:?}", right)
|| format!("{:?}", add_r) == format!("{:?}", right) {
return true;
}
}
false
}
_ => false,
}
}
fn has_named_var(expr: &Expr) -> bool {
match expr {
Expr::Var(name) => !name.starts_with("local_") && !name.starts_with("/*"),
Expr::BinOp { left, right, .. } => has_named_var(left) || has_named_var(right),
Expr::UnOp { operand, .. } => has_named_var(operand),
Expr::Ref(inner) => has_named_var(inner),
_ => false,
}
}
fn is_type_tag_check_expr(expr: &Expr) -> bool {
match expr {
Expr::BinOp { op: B::Ne | B::Eq, left, .. } => {
matches!(left.as_ref(),
Expr::BinOp { op: B::BitAnd, right, .. }
if matches!(right.as_ref(),
Expr::Literal(Literal::I64(255))
| Expr::Literal(Literal::I32(255))
)
)
}
_ => false,
}
}
#[allow(dead_code)]
pub(super) fn is_decode_check_expr(expr: &Expr) -> bool {
match expr {
Expr::BinOp { op: B::Eq | B::Ne, left, right } => {
let is_small_const = |e: &Expr| match e {
Expr::Literal(Literal::I32(v)) => v.unsigned_abs() <= 32,
Expr::Literal(Literal::I64(v)) => v.unsigned_abs() <= 32,
_ => false,
};
is_small_const(left) && is_small_const(right)
}
_ => false,
}
}
fn is_overflow_check_expr(expr: &Expr) -> bool {
if let Expr::BinOp { op: B::Lt, right, left, .. } = expr {
let is_zero = matches!(right.as_ref(),
Expr::Literal(Literal::I64(0))
| Expr::Literal(Literal::I32(0))
);
if is_zero && contains_shr63(left) {
return true;
}
}
false
}
fn contains_shr63(expr: &Expr) -> bool {
match expr {
Expr::BinOp { op: B::Shr, right, .. } => {
matches!(right.as_ref(),
Expr::Literal(Literal::I64(63))
| Expr::Literal(Literal::I32(63))
) || contains_shr63(right)
}
Expr::BinOp { left, right, .. } => {
contains_shr63(left) || contains_shr63(right)
}
Expr::UnOp { operand, .. } => contains_shr63(operand),
_ => false,
}
}
pub(super) fn is_vec_iteration_boilerplate(stmt: &Statement) -> bool {
match stmt {
Statement::Let { name, .. } => {
name == "len" || name.starts_with("len_")
}
_ => false,
}
}
pub(super) fn should_emit_return_expr(expr: &Expr) -> bool {
match expr {
Expr::Raw(s) => {
!s.contains("void") && !s.contains("unknown") && !s.contains("computed")
}
Expr::Literal(_) | Expr::Var(_) | Expr::MethodChain { .. }
| Expr::BinOp { .. } | Expr::UnOp { .. } | Expr::HostCall { .. }
| Expr::MacroCall { .. } | Expr::StructLiteral { .. }
| Expr::EnumVariant { .. } | Expr::Ref(_) => true,
}
}