#[derive(Debug, Clone, Eq, PartialEq)]
pub struct BlockInfo {
cell_access: Option<Vec<CellAccess>>,
allways_used: bool,
}
impl BlockInfo {
pub fn new_empty() -> BlockInfo {
BlockInfo {
cell_access: None,
allways_used: false,
}
}
pub fn new_access(cell_access: Vec<CellAccess>) -> BlockInfo {
BlockInfo {
cell_access: Some(cell_access),
allways_used: false,
}
}
pub fn has_cell_access(&self) -> bool {
self.cell_access.is_some()
}
pub fn cell_access(&self) -> Option<&Vec<CellAccess>> {
self.cell_access.as_ref()
}
pub fn set_cell_access(&mut self, cell_access: Vec<CellAccess>) {
self.cell_access = Some(cell_access);
}
pub fn always_used(&self) -> bool {
self.allways_used
}
pub fn set_always_used(&mut self, always_used: bool) {
self.allways_used = always_used;
}
pub fn get_access_value(&self, offset: isize) -> Option<Cell> {
if let Some(cell_access) = &self.cell_access {
for cell in cell_access {
if cell.offset == offset {
return Some(cell.value);
}
}
}
None
}
pub fn get_access(&self, offset: isize) -> Option<&CellAccess> {
if let Some(cell_access) = &self.cell_access {
for cell in cell_access {
if cell.offset == offset {
return Some(cell);
}
}
}
None
}
pub fn was_cell_written(&self, offset: isize) -> bool {
if let Some(cell_access) = &self.cell_access {
for cell in cell_access {
if cell.offset == offset {
return cell.value.is_write();
}
}
}
false
}
pub fn was_cell_accessed(&self, offset: isize) -> bool {
if let Some(cell_access) = &self.cell_access {
for cell in cell_access {
if cell.offset == offset {
return true;
}
}
}
false
}
pub fn asm(&self, debug: bool) -> String {
let cell_access = if let Some(cell_access) = self.cell_access() {
if debug {
format!("access: {:?}", cell_access)
} else {
format!("access: {}", cell_access.len())
}
} else {
"access: None".to_owned()
};
if self.allways_used {
format!("always {}", cell_access)
} else {
cell_access
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Cell {
Read,
Write,
NonZero,
Bool,
Value(u8),
Range(u8, u8),
}
impl Cell {
pub fn is_read(&self) -> bool {
matches!(self, Cell::Read)
}
pub fn is_write(&self) -> bool {
!self.is_read()
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct CellAccess {
pub read_before_write: bool,
pub read_after_write: bool,
pub offset: isize,
pub value: Cell,
}
impl CellAccess {
pub fn new_read(offset: isize, read_before_write: bool, read_after_write: bool) -> CellAccess {
CellAccess {
read_before_write,
read_after_write,
offset,
value: Cell::Read,
}
}
pub fn new_write(offset: isize, read_before_write: bool, read_after_write: bool) -> CellAccess {
CellAccess {
read_before_write,
read_after_write,
offset,
value: Cell::Write,
}
}
pub fn new_value(
offset: isize,
value: u8,
read_before_write: bool,
read_after_write: bool,
) -> CellAccess {
CellAccess {
read_before_write,
read_after_write,
offset,
value: Cell::Value(value),
}
}
pub fn has_read(&self) -> bool {
self.read_before_write || self.read_after_write || self.value.is_read()
}
pub fn add(cells: &mut Vec<CellAccess>, offset: isize, value: Cell) {
for exiting_cell in cells.iter_mut() {
if exiting_cell.offset == offset {
if value.is_read() {
if exiting_cell.value.is_write() {
exiting_cell.read_after_write = true;
}
} else {
exiting_cell.value = value;
}
return;
}
}
cells.push(CellAccess {
read_before_write: value.is_read(),
read_after_write: false,
offset,
value,
});
}
pub fn add_with_read(cells: &mut Vec<CellAccess>, offset: isize, value: Cell) {
for exiting_cell in cells.iter_mut() {
if exiting_cell.offset == offset {
if exiting_cell.value.is_write() {
exiting_cell.read_after_write = true;
}
if !value.is_read() {
exiting_cell.value = value;
}
return;
}
}
cells.push(CellAccess {
read_before_write: true,
read_after_write: false,
offset,
value,
});
}
pub fn add_conditional(
cells: &mut Vec<CellAccess>,
offset: isize,
value: Cell,
has_read: bool,
) {
for exiting_cell in cells.iter_mut() {
if exiting_cell.offset == offset {
match (&exiting_cell.value, value) {
(Cell::Value(v1), Cell::Value(v2)) => {
if *v1 != v2 {
if *v1 != 0 && v2 != 0 {
exiting_cell.value = Cell::NonZero
} else if (*v1 == 0 || *v1 == 1) && (v2 == 0 || v2 == 1) {
exiting_cell.value = Cell::Bool
} else {
let min = (*v1).min(v2);
let max = (*v1).max(v2);
if min == 0 && max == 255 {
exiting_cell.value = Cell::Write;
} else {
exiting_cell.value = Cell::Range(min, max);
}
}
}
}
(Cell::NonZero, Cell::Value(v)) => {
if v != 0 {
exiting_cell.value = Cell::NonZero
} else {
exiting_cell.value = Cell::Write
}
}
(Cell::Value(v), Cell::NonZero) => {
if *v != 0 {
exiting_cell.value = Cell::NonZero
} else {
exiting_cell.value = Cell::Write
}
}
(Cell::Bool, Cell::Value(v)) => {
if v == 0 || v == 1 {
exiting_cell.value = Cell::Bool
} else {
exiting_cell.value = Cell::Write
}
}
(Cell::Value(v), Cell::Bool) => {
if *v == 0 || *v == 1 {
exiting_cell.value = Cell::Bool
} else {
exiting_cell.value = Cell::Write
}
}
(Cell::Range(start, end), Cell::Range(start2, end2)) => {
let min = (*start).min(start2);
let max = (*end).max(end2);
if min == 0 && max == 255 {
exiting_cell.value = Cell::Write;
} else {
exiting_cell.value = Cell::Range(min, max);
}
}
(Cell::Range(start, end), Cell::Value(v)) => {
let min = (*start).min(v);
let max = (*end).max(v);
if min == 0 && max == 255 {
exiting_cell.value = Cell::Write;
} else {
exiting_cell.value = Cell::Range(min, max);
}
}
(Cell::Value(v), Cell::Range(start, end)) => {
let min = start.min(*v);
let max = end.max(*v);
if min == 0 && max == 255 {
exiting_cell.value = Cell::Write;
} else {
exiting_cell.value = Cell::Range(min, max);
}
}
(Cell::Range(start, end), Cell::Bool) => {
let min = (*start).min(0);
let max = (*end).max(1);
if min == 0 && max == 255 {
exiting_cell.value = Cell::Write;
} else {
exiting_cell.value = Cell::Range(min, max);
}
}
(Cell::Bool, Cell::Range(start, end)) => {
let min = start.min(0);
let max = end.max(1);
if min == 0 && max == 255 {
exiting_cell.value = Cell::Write;
} else {
exiting_cell.value = Cell::Range(min, max);
}
}
(Cell::Range(start, _), Cell::NonZero) => {
if *start == 0 {
exiting_cell.value = Cell::Write;
} else {
exiting_cell.value = Cell::NonZero;
}
}
(Cell::NonZero, Cell::Range(start, _)) => {
if start == 0 {
exiting_cell.value = Cell::Write;
} else {
exiting_cell.value = Cell::NonZero;
}
}
(Cell::NonZero, Cell::NonZero) | (_, Cell::Read) => {
}
(Cell::Read, new) => exiting_cell.value = new,
_ => exiting_cell.value = Cell::Write,
}
if (has_read || value.is_read()) && exiting_cell.value.is_write() {
exiting_cell.read_after_write = true;
}
return;
}
}
cells.push(CellAccess {
read_before_write: value.is_read() || has_read,
read_after_write: false,
offset,
value,
});
}
pub fn add_backward(cells: &mut Vec<CellAccess>, offset: isize, value: Cell) {
for exiting_cell in cells.iter_mut() {
if exiting_cell.offset == offset && !matches!(exiting_cell.value, Cell::Read) {
return;
}
}
cells.push(CellAccess {
read_before_write: true,
read_after_write: true,
offset,
value,
});
}
pub fn get(cells: &[CellAccess], offset: isize) -> Option<Cell> {
cells.iter().find(|c| c.offset == offset).map(|a| a.value)
}
pub fn was_written(cells: &[CellAccess], offset: isize) -> bool {
for cell in cells {
if cell.offset == offset {
return cell.value.is_write();
}
}
false
}
}