use super::Def;
use crate::intermediate_representation::BinOpType as IrBinOpType;
use crate::intermediate_representation::ByteSize;
use crate::intermediate_representation::CastOpType as IrCastOpType;
use crate::intermediate_representation::Expression as IrExpression;
use crate::intermediate_representation::UnOpType as IrUnOpType;
use crate::intermediate_representation::Variable as IrVariable;
use crate::prelude::*;
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct Variable {
pub name: Option<String>,
pub value: Option<String>,
pub address: Option<String>,
pub size: ByteSize,
pub is_virtual: bool,
}
impl From<Variable> for IrVariable {
fn from(pcode_var: Variable) -> IrVariable {
IrVariable {
name: pcode_var.name.unwrap(),
size: pcode_var.size,
is_temp: pcode_var.is_virtual, }
}
}
impl From<Variable> for IrExpression {
fn from(pcode_var: Variable) -> IrExpression {
match (&pcode_var.name, &pcode_var.value) {
(Some(_name), None) => IrExpression::Var(pcode_var.into()),
(None, Some(_hex_value)) => IrExpression::Const(pcode_var.parse_const_to_bitvector()),
_ => panic!("Conversion failed:\n{pcode_var:?}"),
}
}
}
impl Variable {
pub fn parse_const_to_bitvector(&self) -> Bitvector {
match &self.value {
Some(hex_value) => {
let mut bitvector = Bitvector::from_str_radix(16, hex_value).unwrap();
match bitvector.width().cmp(&self.size.into()) {
std::cmp::Ordering::Greater => bitvector.truncate(self.size).unwrap(),
std::cmp::Ordering::Less => bitvector.zero_extend(self.size).unwrap(),
std::cmp::Ordering::Equal => (),
}
bitvector
}
_ => panic!(),
}
}
pub fn parse_address_to_bitvector(&self, generic_pointer_size: ByteSize) -> Bitvector {
match &self.address {
Some(hex_value) => {
let mut bitvector = Bitvector::from_str_radix(16, hex_value).unwrap();
match bitvector.width().cmp(&generic_pointer_size.into()) {
std::cmp::Ordering::Greater => {
bitvector.truncate(generic_pointer_size).unwrap()
}
std::cmp::Ordering::Less => {
bitvector.zero_extend(generic_pointer_size).unwrap()
}
std::cmp::Ordering::Equal => (),
}
bitvector
}
_ => panic!(),
}
}
pub fn new_virtual(name: impl Into<String>, size: ByteSize) -> Variable {
Variable {
name: Some(name.into()),
value: None,
address: None,
size,
is_virtual: true,
}
}
pub fn new_const(value_string: impl Into<String>, size: ByteSize) -> Variable {
Variable {
name: None,
value: Some(value_string.into()),
address: None,
size,
is_virtual: false,
}
}
pub fn to_load_def(
&self,
target_register_name: impl Into<String>,
generic_pointer_size: ByteSize,
) -> Def {
Def {
lhs: Some(Variable::new_virtual(target_register_name, self.size)),
rhs: Expression {
mnemonic: ExpressionType::LOAD,
input0: None,
input1: Some(Variable::new_const(
self.address.as_ref().unwrap(),
generic_pointer_size,
)),
input2: None,
},
}
}
pub fn parse_to_bytesize(self) -> ByteSize {
match (&self.name, &self.value) {
(None, Some(hex_value)) => {
assert!(u64::from(self.size) <= 8);
let val: u64 = u64::from_str_radix(hex_value, 16).unwrap();
val.into()
}
_ => panic!(),
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct Expression {
pub mnemonic: ExpressionType,
pub input0: Option<Variable>,
pub input1: Option<Variable>,
pub input2: Option<Variable>,
}
impl From<Expression> for IrExpression {
fn from(expr: Expression) -> IrExpression {
use ExpressionType::*;
match expr.mnemonic {
COPY => expr.input0.unwrap().into(),
LOAD | STORE | SUBPIECE => panic!(),
PIECE | INT_EQUAL | INT_NOTEQUAL | INT_LESS | INT_SLESS | INT_LESSEQUAL
| INT_SLESSEQUAL | INT_ADD | INT_SUB | INT_CARRY | INT_SCARRY | INT_SBORROW
| INT_XOR | INT_AND | INT_OR | INT_LEFT | INT_RIGHT | INT_SRIGHT | INT_MULT
| INT_DIV | INT_REM | INT_SDIV | INT_SREM | BOOL_XOR | BOOL_AND | BOOL_OR
| FLOAT_EQUAL | FLOAT_NOTEQUAL | FLOAT_LESS | FLOAT_LESSEQUAL | FLOAT_ADD
| FLOAT_SUB | FLOAT_MULT | FLOAT_DIV => IrExpression::BinOp {
op: expr.mnemonic.into(),
lhs: Box::new(expr.input0.unwrap().into()),
rhs: Box::new(expr.input1.unwrap().into()),
},
INT_NEGATE | INT_2COMP | BOOL_NEGATE | FLOAT_NEG | FLOAT_ABS | FLOAT_SQRT
| FLOAT_CEIL | FLOAT_FLOOR | FLOAT_ROUND | FLOAT_NAN => IrExpression::UnOp {
op: expr.mnemonic.into(),
arg: Box::new(expr.input0.unwrap().into()),
},
INT_ZEXT | INT_SEXT | INT2FLOAT | FLOAT2FLOAT | TRUNC | POPCOUNT | LZCOUNT => panic!(),
}
}
}
#[allow(missing_docs)]
#[allow(non_camel_case_types)]
#[allow(clippy::upper_case_acronyms)]
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum ExpressionType {
COPY,
LOAD,
STORE,
PIECE,
SUBPIECE,
POPCOUNT,
LZCOUNT,
INT_EQUAL,
INT_NOTEQUAL,
INT_LESS,
INT_SLESS,
INT_LESSEQUAL,
INT_SLESSEQUAL,
INT_ADD,
INT_SUB,
INT_CARRY,
INT_SCARRY,
INT_SBORROW,
INT_XOR,
INT_AND,
INT_OR,
INT_LEFT,
INT_RIGHT,
INT_SRIGHT,
INT_MULT,
INT_DIV,
INT_REM,
INT_SDIV,
INT_SREM,
BOOL_XOR,
BOOL_AND,
BOOL_OR,
FLOAT_EQUAL,
FLOAT_NOTEQUAL,
FLOAT_LESS,
FLOAT_LESSEQUAL,
FLOAT_ADD,
FLOAT_SUB,
FLOAT_MULT,
FLOAT_DIV,
INT_NEGATE,
INT_2COMP,
BOOL_NEGATE,
FLOAT_NEG,
FLOAT_ABS,
FLOAT_SQRT,
#[serde(alias = "CEIL")]
FLOAT_CEIL,
#[serde(alias = "FLOOR")]
FLOAT_FLOOR,
#[serde(alias = "ROUND")]
FLOAT_ROUND,
FLOAT_NAN,
INT_ZEXT,
INT_SEXT,
INT2FLOAT,
FLOAT2FLOAT,
TRUNC,
}
impl From<ExpressionType> for IrBinOpType {
fn from(expr_type: ExpressionType) -> IrBinOpType {
use ExpressionType::*;
use IrBinOpType::*;
match expr_type {
PIECE => IrBinOpType::Piece,
INT_EQUAL => IrBinOpType::IntEqual,
INT_NOTEQUAL => IrBinOpType::IntNotEqual,
INT_LESS => IrBinOpType::IntLess,
INT_SLESS => IrBinOpType::IntSLess,
INT_LESSEQUAL => IntLessEqual,
INT_SLESSEQUAL => IntSLessEqual,
INT_ADD => IrBinOpType::IntAdd,
INT_SUB => IrBinOpType::IntSub,
INT_CARRY => IrBinOpType::IntCarry,
INT_SCARRY => IrBinOpType::IntSCarry,
INT_SBORROW => IrBinOpType::IntSBorrow,
INT_XOR => IrBinOpType::IntXOr,
INT_AND => IrBinOpType::IntAnd,
INT_OR => IrBinOpType::IntOr,
INT_LEFT => IrBinOpType::IntLeft,
INT_RIGHT => IrBinOpType::IntRight,
INT_SRIGHT => IrBinOpType::IntSRight,
INT_MULT => IrBinOpType::IntMult,
INT_DIV => IrBinOpType::IntDiv,
INT_REM => IrBinOpType::IntRem,
INT_SDIV => IrBinOpType::IntSDiv,
INT_SREM => IrBinOpType::IntSRem,
BOOL_XOR => IrBinOpType::BoolXOr,
BOOL_AND => IrBinOpType::BoolAnd,
BOOL_OR => IrBinOpType::BoolOr,
FLOAT_EQUAL => IrBinOpType::FloatEqual,
FLOAT_NOTEQUAL => IrBinOpType::FloatNotEqual,
FLOAT_LESS => IrBinOpType::FloatLess,
FLOAT_LESSEQUAL => IrBinOpType::FloatLessEqual,
FLOAT_ADD => IrBinOpType::FloatAdd,
FLOAT_SUB => IrBinOpType::FloatSub,
FLOAT_MULT => IrBinOpType::FloatMult,
FLOAT_DIV => IrBinOpType::FloatDiv,
_ => panic!(),
}
}
}
impl From<ExpressionType> for IrUnOpType {
fn from(expr_type: ExpressionType) -> IrUnOpType {
use ExpressionType::*;
match expr_type {
INT_NEGATE => IrUnOpType::IntNegate,
INT_2COMP => IrUnOpType::Int2Comp,
BOOL_NEGATE => IrUnOpType::BoolNegate,
FLOAT_NEG => IrUnOpType::FloatNegate,
FLOAT_ABS => IrUnOpType::FloatAbs,
FLOAT_SQRT => IrUnOpType::FloatSqrt,
FLOAT_CEIL => IrUnOpType::FloatCeil,
FLOAT_FLOOR => IrUnOpType::FloatFloor,
FLOAT_ROUND => IrUnOpType::FloatRound,
FLOAT_NAN => IrUnOpType::FloatNaN,
_ => panic!(),
}
}
}
impl From<ExpressionType> for IrCastOpType {
fn from(expr_type: ExpressionType) -> IrCastOpType {
use ExpressionType::*;
match expr_type {
INT_ZEXT => IrCastOpType::IntZExt,
INT_SEXT => IrCastOpType::IntSExt,
INT2FLOAT => IrCastOpType::Int2Float,
FLOAT2FLOAT => IrCastOpType::Float2Float,
TRUNC => IrCastOpType::Trunc,
POPCOUNT => IrCastOpType::PopCount,
LZCOUNT => IrCastOpType::LzCount,
_ => panic!(),
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct RegisterProperties {
pub register: String,
pub base_register: String,
pub lsb: ByteSize,
pub size: ByteSize,
}
impl From<&RegisterProperties> for IrVariable {
fn from(register_prop: &RegisterProperties) -> IrVariable {
IrVariable {
name: register_prop.register.clone(),
size: register_prop.size,
is_temp: false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn variable_deserialization() {
let _: Variable = serde_json::from_str(
r#"
{
"name": "RSP",
"size": 8,
"is_virtual": false
}
"#,
)
.unwrap();
}
#[test]
fn expression_deserialization() {
let _: Expression = serde_json::from_str(
r#"
{
"mnemonic": "INT_SLESS",
"input0": {
"name": "EAX",
"size": 4,
"is_virtual": false
},
"input1": {
"value": "00000000",
"size": 4,
"is_virtual": false
}
}
"#,
)
.unwrap();
}
#[test]
fn register_properties_deserialization() {
let _: RegisterProperties = serde_json::from_str(
r#"
{
"register": "AH",
"base_register": "EAX",
"lsb": 2,
"size": 1
}
"#,
)
.unwrap();
}
#[test]
fn parse_to_bitvector() {
let mut var = Variable {
name: None,
value: Some("0".to_string()),
address: None,
size: ByteSize::new(8),
is_virtual: false,
};
assert_eq!(var.parse_const_to_bitvector(), Bitvector::from_u64(0));
var.value = Some("0010f".to_string());
assert_eq!(var.parse_const_to_bitvector(), Bitvector::from_u64(271));
var.value = Some("1ff".to_string());
var.size = ByteSize::new(1);
assert_eq!(var.parse_const_to_bitvector(), Bitvector::from_u8(255));
var.size = ByteSize::new(16);
assert_eq!(var.parse_const_to_bitvector(), Bitvector::from_u128(511));
var.value = Some("00_ffffffffffffffff_ffffffffffffffff".to_string());
var.size = ByteSize::new(16);
assert_eq!(var.parse_const_to_bitvector(), Bitvector::from_i128(-1));
var.size = ByteSize::new(10);
assert_eq!(
var.parse_const_to_bitvector(),
Bitvector::from_i128(-1)
.into_truncate(ByteSize::new(10))
.unwrap()
);
let var = Variable {
name: None,
value: None,
address: Some("000010f".to_string()),
size: ByteSize::new(1), is_virtual: false,
};
assert_eq!(
var.parse_address_to_bitvector(ByteSize::new(8)),
Bitvector::from_u64(271)
);
}
}