use crate::ir::*; use falcon::il; use serde::{Deserialize, Serialize}; use std::fmt; #[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] pub enum Operation { Assign { dst: Variable, src: Expression, }, Store { index: Expression, src: Expression, }, Load { dst: Variable, index: Expression, }, Branch { target: Expression, }, Call(Call), Intrinsic(il::Intrinsic), Return(Option>), Nop(Option>>), } impl Operation { pub fn from_il(operation: &il::Operation) -> Operation { match operation { il::Operation::Assign { dst, src } => Operation::Assign { dst: dst.clone().into(), src: Expression::from_il(src), }, il::Operation::Store { index, src } => Operation::Store { index: Expression::from_il(index), src: Expression::from_il(src), }, il::Operation::Load { dst, index } => Operation::Load { dst: dst.clone().into(), index: Expression::from_il(index), }, il::Operation::Branch { target } => Operation::Branch { target: Expression::from_il(target), }, il::Operation::Intrinsic { intrinsic } => Operation::Intrinsic(intrinsic.clone()), il::Operation::Nop { placeholder } => Operation::Nop( placeholder .as_ref() .map(|operation| Box::new(Operation::::from_il(operation))), ), } } pub fn is_assign(&self) -> bool { matches!(self, Operation::Call { .. }) } pub fn is_load(&self) -> bool { matches!(self, Operation::Load { .. }) } pub fn is_return(&self) -> bool { matches!(self, Operation::Return(_)) } pub fn is_nop(&self) -> bool { matches!(self, Operation::Nop(_)) } pub fn is_branch(&self) -> bool { matches!(self, Operation::Branch { .. }) } pub fn is_call(&self) -> bool { matches!(self, Operation::Call(_)) } pub fn src(&self) -> Option<&Expression> { match self { Operation::Assign { src, .. } | Operation::Store { src, .. } => Some(src), _ => None, } } pub fn dst(&self) -> Option<&Variable> { match self { Operation::Assign { dst, .. } | Operation::Load { dst, .. } => Some(dst), _ => None, } } pub fn index(&self) -> Option<&Expression> { match self { Operation::Store { index, .. } | Operation::Load { index, .. } => Some(index), _ => None, } } pub fn target(&self) -> Option<&Expression> { match self { Operation::Branch { target } => Some(target), Operation::Call(call) => call.target().expression(), _ => None, } } pub fn call(&self) -> Option<&Call> { match self { Operation::Call(call) => Some(call), _ => None, } } pub fn result(&self) -> Option<&Expression> { match self { Operation::Return(result) => result.as_ref(), _ => None, } } pub fn expressions(&self) -> Vec<&Expression> { match self { Operation::Assign { src, .. } => vec![src], Operation::Store { index, src } => vec![index, src], Operation::Load { index, .. } => vec![index], Operation::Branch { target } => vec![target], Operation::Call(call) => call .target() .expression() .map(|e| vec![e]) .unwrap_or_default(), Operation::Intrinsic(_) | Operation::Return(_) | Operation::Nop(_) => Vec::new(), } } pub fn expressions_mut(&mut self) -> Vec<&mut Expression> { match self { Operation::Assign { src, .. } => vec![src], Operation::Store { index, src } => vec![index, src], Operation::Load { index, .. } => vec![index], Operation::Branch { target } => vec![target], Operation::Call(call) => call .target_mut() .expression_mut() .map(|e| vec![e]) .unwrap_or_default(), Operation::Intrinsic(_) | Operation::Return(_) | Operation::Nop(_) => Vec::new(), } } pub fn variables_written(&self) -> Option> { match self { Operation::Assign { dst, .. } | Operation::Load { dst, .. } => Some(vec![dst]), Operation::Call(call) => call.variables_written().map(|vw| vw.iter().collect()), Operation::Branch { .. } | Operation::Intrinsic(_) => None, Operation::Store { .. } | Operation::Return(_) | Operation::Nop(_) => Some(Vec::new()), } } pub fn variables_read(&self) -> Option> { match self { Operation::Assign { src, .. } => Some(src.variables()), Operation::Store { index, src } => Some( index .variables() .into_iter() .chain(src.variables().into_iter()) .collect(), ), Operation::Load { index, .. } => Some(index.variables()), Operation::Call(call) => call.variables_read(), Operation::Branch { .. } | Operation::Intrinsic(_) => None, Operation::Return(result) => result.as_ref().map(|e| e.variables()), Operation::Nop(_) => Some(Vec::new()), } } pub fn variables(&self) -> Option> { let mut variables = self.variables_written()?; variables.append(&mut self.variables_read()?); variables.sort(); variables.dedup(); Some(variables) } } impl fmt::Display for Operation { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Operation::Assign { dst, src } => write!(f, "{} = {}", dst, src), Operation::Store { index, src } => write!(f, "[{}] = {}", index, src), Operation::Load { dst, index } => write!(f, "{} = [{}]", dst, index), Operation::Branch { target } => write!(f, "branch {}", target), Operation::Call(call) => call.fmt(f), Operation::Intrinsic(intrinsic) => intrinsic.fmt(f), Operation::Return(result) => match result { Some(result) => write!(f, "return {}", result), None => write!(f, "return ???"), }, Operation::Nop(_) => write!(f, "nop"), } } }