use crate::arch::x86::{DecodedInsn, Mnemonic, format_intel};
use crate::ast::Stmt;
use super::{Candidate, Pattern, PatternCtx};
pub struct X87Expr;
impl Pattern for X87Expr {
fn name(&self) -> &'static str {
"x87_expr"
}
fn tentative(
&self,
_ctx: &PatternCtx,
insns: &[DecodedInsn],
start: usize,
) -> Option<Candidate> {
let ins = insns.get(start)?;
let m = ins.iced.mnemonic();
let shape = classify(m)?;
let full = format_intel(&ins.iced);
let rest = full.strip_prefix(shape.prefix)?;
let (lhs_raw, rhs_raw) = split_two_ops(rest)?;
let (dst, src) = match shape.kind {
X87Kind::Load => ("st0".to_string(), wrap_cast(shape.cast, &rhs_raw)),
X87Kind::Store => (lhs_raw.clone(), wrap_cast(shape.cast, "st0")),
X87Kind::Binary(op) => {
if lhs_raw == "st" {
("st0".to_string(), format!("st0 {op} {rhs_raw}"))
} else if rhs_raw == "st" {
(lhs_raw.clone(), format!("{lhs_raw} {op} st0"))
} else {
(lhs_raw.clone(), format!("{lhs_raw} {op} {rhs_raw}"))
}
}
};
Some(Candidate {
pattern: self.name(),
start,
consumed: 1,
priority: 28,
stmts: vec![Stmt::Move {
dst,
src,
bytes: ins.original_bytes.clone(),
}],
})
}
}
struct X87Shape {
prefix: &'static str,
kind: X87Kind,
cast: Option<&'static str>,
}
enum X87Kind {
Load,
Store,
Binary(&'static str),
}
fn classify(m: Mnemonic) -> Option<X87Shape> {
Some(match m {
Mnemonic::Fld => X87Shape {
prefix: "fld ",
kind: X87Kind::Load,
cast: None,
},
Mnemonic::Fild => X87Shape {
prefix: "fild ",
kind: X87Kind::Load,
cast: Some("(f64)"),
},
Mnemonic::Fst => X87Shape {
prefix: "fst ",
kind: X87Kind::Store,
cast: None,
},
Mnemonic::Fstp => X87Shape {
prefix: "fstp ",
kind: X87Kind::Store,
cast: None,
},
Mnemonic::Fist => X87Shape {
prefix: "fist ",
kind: X87Kind::Store,
cast: Some("(i32)"),
},
Mnemonic::Fistp => X87Shape {
prefix: "fistp ",
kind: X87Kind::Store,
cast: Some("(i64)"),
},
Mnemonic::Fadd => X87Shape {
prefix: "fadd ",
kind: X87Kind::Binary("+"),
cast: None,
},
Mnemonic::Fsub => X87Shape {
prefix: "fsub ",
kind: X87Kind::Binary("-"),
cast: None,
},
Mnemonic::Fmul => X87Shape {
prefix: "fmul ",
kind: X87Kind::Binary("*"),
cast: None,
},
Mnemonic::Fdiv => X87Shape {
prefix: "fdiv ",
kind: X87Kind::Binary("/"),
cast: None,
},
Mnemonic::Fsubr => X87Shape {
prefix: "fsubr ",
kind: X87Kind::Binary("- /*rev*/"),
cast: None,
},
Mnemonic::Fdivr => X87Shape {
prefix: "fdivr ",
kind: X87Kind::Binary("/ /*rev*/"),
cast: None,
},
_ => return None,
})
}
fn split_two_ops(rest: &str) -> Option<(String, String)> {
let (l, r) = rest.split_once(',')?;
let lhs = normalize_st(l.trim());
let rhs = normalize_st(r.trim());
Some((lhs, rhs))
}
fn normalize_st(s: &str) -> String {
if s == "st" {
return "st0".to_string();
}
if let Some(rest) = s.strip_prefix("st(")
&& let Some(num) = rest.strip_suffix(')')
{
return format!("st{num}");
}
s.to_string()
}
fn wrap_cast(cast: Option<&'static str>, value: &str) -> String {
match cast {
Some(c) => format!("{c} {value}"),
None => value.to_string(),
}
}