mirror of
https://github.com/Noratrieb/brainfuck.git
synced 2026-01-14 13:35:00 +01:00
start with finding dead stores
This commit is contained in:
parent
039b5ea9c7
commit
d5d80f79b7
8 changed files with 181 additions and 56 deletions
|
|
@ -20,6 +20,9 @@ insta = "1.14.0"
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
opt-level = 3
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "opts"
|
name = "opts"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
++++++++++[>++++++++++<-]>>++++++++++>->>>>>>>>>>>>>>>>-->+++++++[->++\n++++++++<]>[->+>+>+>+<<<<]+++>>+++>>>++++++++[-<
|
|
||||||
++++<++++<++++>>>]++++\n+[-<++++<++++>>]>>-->++++++[->+++++++++++<]>[->+>+>+>+<<<<]+++++>>+>++\n++++>++++++>++++++++[-<+
|
|
||||||
+++<++++<++++>>>]++++++[-<+++<+++<+++>>>]>>-->\n---+[-<+]-<[+[->+]-<<->>>+>[-]++[-->++]-->+++[---++[--<++]---->>-<+>[+\n
|
|
||||||
+++[----<++++]--[>]++[-->++]--<]>++[--+[-<+]->>[-]+++++[---->++++]-->[\n->+<]>>[.>]++[-->++]]-->+++]---+[-<+]->>-[+>>>+[
|
|
||||||
-<+]->>>++++++++++<<[-\n>+>-[>+>>]>[+[-<+>]>+>>]<<<<<<]>>[-]>>>++++++++++<[->-[>+>>]>[+[-<+>]>\n+>>]<<<<<]>[-]>>[>++++++
|
|
||||||
[-<++++++++>]<.<<+>+>[-]]<[<[->-<]++++++[->+++\n+++++<]>.[-]]<<++++++[-<++++++++>]<.[-]<<[-<+>]+[-<+]->>]+[-]<<<.>>>+[\n
|
|
||||||
-<+]-<<]
|
|
||||||
|
|
@ -158,7 +158,18 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fizzbuzz() {
|
fn fizzbuzz() {
|
||||||
let str = include_str!("fizzbuzz.bf");
|
let str = include_str!("../benches/fizzbuzz.bf");
|
||||||
|
let mut stdout = Vec::new();
|
||||||
|
let stdin = [];
|
||||||
|
|
||||||
|
super::run(str, &mut stdout, stdin.as_slice(), &Args::default()).unwrap();
|
||||||
|
|
||||||
|
insta::assert_debug_snapshot!(String::from_utf8(stdout));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mandelbrot() {
|
||||||
|
let str = include_str!("../benches/mandelbrot.bf");
|
||||||
let mut stdout = Vec::new();
|
let mut stdout = Vec::new();
|
||||||
let stdin = [];
|
let stdin = [];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ use bumpalo::Bump;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
hir::{Hir, StmtKind as HirStmtKind},
|
hir::{Hir, StmtKind as HirStmtKind},
|
||||||
mir::state::{Load, MemoryState, Store},
|
mir::state::{MemoryState, Store},
|
||||||
parse::Span,
|
parse::Span,
|
||||||
BumpVec,
|
BumpVec,
|
||||||
};
|
};
|
||||||
|
|
@ -50,20 +50,26 @@ impl Debug for Stmt<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Offset = i32;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum StmtKind<'mir> {
|
enum StmtKind<'mir> {
|
||||||
/// Add or sub, the value has the valid range -255..=255
|
/// Add or sub, the value has the valid range -255..=255
|
||||||
AddSub(i32, i16, Store),
|
AddSub {
|
||||||
|
offset: Offset,
|
||||||
|
n: i16,
|
||||||
|
store: Store,
|
||||||
|
},
|
||||||
/// Sets the current cell to 0 and adds that value of the cell to another cell at `offset`
|
/// Sets the current cell to 0 and adds that value of the cell to another cell at `offset`
|
||||||
MoveAddTo {
|
MoveAddTo {
|
||||||
offset: i32,
|
offset: Offset,
|
||||||
store_set_null: Store,
|
store_set_null: Store,
|
||||||
store_move: Store,
|
store_move: Store,
|
||||||
},
|
},
|
||||||
/// Left or Right pointer move (`<>`)
|
/// Left or Right pointer move (`<>`)
|
||||||
PointerMove(i32),
|
PointerMove(Offset),
|
||||||
Loop(Mir<'mir>, Load),
|
Loop(Mir<'mir>),
|
||||||
Out(Load),
|
Out,
|
||||||
In(Store),
|
In(Store),
|
||||||
SetN(u8, Store),
|
SetN(u8, Store),
|
||||||
}
|
}
|
||||||
|
|
@ -80,21 +86,27 @@ fn hir_to_mir<'mir>(alloc: &'mir Bump, hir: &Hir<'_>) -> Mir<'mir> {
|
||||||
let mut stmts = Vec::new_in(alloc);
|
let mut stmts = Vec::new_in(alloc);
|
||||||
let iter = hir.stmts.iter().map(|hir_stmt| {
|
let iter = hir.stmts.iter().map(|hir_stmt| {
|
||||||
let kind = match *hir_stmt.kind() {
|
let kind = match *hir_stmt.kind() {
|
||||||
HirStmtKind::Add(offset, n) => StmtKind::AddSub(offset, i16::from(n), Store::unknown()),
|
HirStmtKind::Add(offset, n) => StmtKind::AddSub {
|
||||||
HirStmtKind::Sub(offset, n) => {
|
offset,
|
||||||
StmtKind::AddSub(offset, -i16::from(n), Store::unknown())
|
n: i16::from(n),
|
||||||
}
|
store: Store::dead(),
|
||||||
|
},
|
||||||
|
HirStmtKind::Sub(offset, n) => StmtKind::AddSub {
|
||||||
|
offset,
|
||||||
|
n: -i16::from(n),
|
||||||
|
store: Store::dead(),
|
||||||
|
},
|
||||||
HirStmtKind::MoveAddTo { offset } => StmtKind::MoveAddTo {
|
HirStmtKind::MoveAddTo { offset } => StmtKind::MoveAddTo {
|
||||||
offset,
|
offset,
|
||||||
store_set_null: Store::unknown(),
|
store_set_null: Store::dead(),
|
||||||
store_move: Store::unknown(),
|
store_move: Store::dead(),
|
||||||
},
|
},
|
||||||
HirStmtKind::Right(n) => StmtKind::PointerMove(i32::try_from(n).unwrap()),
|
HirStmtKind::Right(n) => StmtKind::PointerMove(i32::try_from(n).unwrap()),
|
||||||
HirStmtKind::Left(n) => StmtKind::PointerMove(-i32::try_from(n).unwrap()),
|
HirStmtKind::Left(n) => StmtKind::PointerMove(-i32::try_from(n).unwrap()),
|
||||||
HirStmtKind::Loop(ref body) => StmtKind::Loop(hir_to_mir(alloc, body), Load::Unknown),
|
HirStmtKind::Loop(ref body) => StmtKind::Loop(hir_to_mir(alloc, body)),
|
||||||
HirStmtKind::Out => StmtKind::Out(Load::Unknown),
|
HirStmtKind::Out => StmtKind::Out,
|
||||||
HirStmtKind::In => StmtKind::In(Store::unknown()),
|
HirStmtKind::In => StmtKind::In(Store::dead()),
|
||||||
HirStmtKind::SetN(n) => StmtKind::SetN(n, Store::unknown()),
|
HirStmtKind::SetN(n) => StmtKind::SetN(n, Store::dead()),
|
||||||
};
|
};
|
||||||
Stmt {
|
Stmt {
|
||||||
kind,
|
kind,
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
|
use std::collections::{hash_map::Entry, HashMap};
|
||||||
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
use crate::mir::{
|
use crate::mir::{
|
||||||
state::{CellState, MemoryState, MemoryStateChange},
|
state::{CellState, MemoryState, MemoryStateChange, Store},
|
||||||
Mir, StmtKind,
|
Mir, Offset, StmtKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// this pass fills out as much state info for all statements as possible
|
/// this pass fills out as much state info for all statements as possible
|
||||||
|
|
@ -11,6 +13,7 @@ use crate::mir::{
|
||||||
pub fn passes<'mir>(alloc: &'mir Bump, mir: &mut Mir<'mir>) {
|
pub fn passes<'mir>(alloc: &'mir Bump, mir: &mut Mir<'mir>) {
|
||||||
pass_fill_state_info(alloc, mir);
|
pass_fill_state_info(alloc, mir);
|
||||||
pass_const_propagation(mir);
|
pass_const_propagation(mir);
|
||||||
|
pass_dead_store_elimination(mir);
|
||||||
}
|
}
|
||||||
/// this pass fills out as much state info for all statements as possible
|
/// this pass fills out as much state info for all statements as possible
|
||||||
#[tracing::instrument(skip(alloc, mir))]
|
#[tracing::instrument(skip(alloc, mir))]
|
||||||
|
|
@ -27,7 +30,7 @@ fn pass_fill_state_info_inner<'mir>(
|
||||||
) {
|
) {
|
||||||
for stmt in &mut mir.stmts {
|
for stmt in &mut mir.stmts {
|
||||||
let state = match &mut stmt.kind {
|
let state = match &mut stmt.kind {
|
||||||
StmtKind::AddSub(offset, n, store) => {
|
StmtKind::AddSub { offset, n, store } => {
|
||||||
let prev_state = outer.state_for_offset(*offset);
|
let prev_state = outer.state_for_offset(*offset);
|
||||||
let new_state = match prev_state {
|
let new_state = match prev_state {
|
||||||
CellState::WrittenToKnown(_, prev_n) => {
|
CellState::WrittenToKnown(_, prev_n) => {
|
||||||
|
|
@ -65,7 +68,7 @@ fn pass_fill_state_info_inner<'mir>(
|
||||||
StmtKind::PointerMove(n) => {
|
StmtKind::PointerMove(n) => {
|
||||||
MemoryState::single(alloc, outer, MemoryStateChange::Move(*n))
|
MemoryState::single(alloc, outer, MemoryStateChange::Move(*n))
|
||||||
}
|
}
|
||||||
StmtKind::Loop(body, _) => {
|
StmtKind::Loop(body) => {
|
||||||
// TODO: we can get a lot smarter here and get huge benefits; we don't yet
|
// TODO: we can get a lot smarter here and get huge benefits; we don't yet
|
||||||
pass_fill_state_info_inner(alloc, body, MemoryState::empty(alloc));
|
pass_fill_state_info_inner(alloc, body, MemoryState::empty(alloc));
|
||||||
MemoryState::double(
|
MemoryState::double(
|
||||||
|
|
@ -80,7 +83,7 @@ fn pass_fill_state_info_inner<'mir>(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
StmtKind::Out(_) => outer,
|
StmtKind::Out => outer,
|
||||||
StmtKind::In(store) => MemoryState::single(
|
StmtKind::In(store) => MemoryState::single(
|
||||||
alloc,
|
alloc,
|
||||||
outer,
|
outer,
|
||||||
|
|
@ -103,6 +106,82 @@ fn pass_fill_state_info_inner<'mir>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This pass eliminates dead stores. It should probably be run multiple times between other passes
|
||||||
|
/// for cleanup
|
||||||
|
#[tracing::instrument(skip(mir))]
|
||||||
|
fn pass_dead_store_elimination(mir: &mut Mir<'_>) {
|
||||||
|
pass_dead_store_elimination_mark_dead_stores(mir)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pass_dead_store_elimination_mark_dead_stores(mir: &mut Mir<'_>) {
|
||||||
|
fn mark_store(
|
||||||
|
potential_dead_stores: &mut HashMap<Offset, Store>,
|
||||||
|
offset: Offset,
|
||||||
|
store: &Store,
|
||||||
|
) {
|
||||||
|
match potential_dead_stores.entry(offset) {
|
||||||
|
Entry::Occupied(mut entry) => {
|
||||||
|
let old = entry.insert(store.clone());
|
||||||
|
if old.is_maybe_dead() {
|
||||||
|
// it's certainly dead
|
||||||
|
info!("We have a dead one!!!");
|
||||||
|
old.mark_dead();
|
||||||
|
} else {
|
||||||
|
// it's alive and well, drop it and keep it marked alive
|
||||||
|
drop(old);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
entry.insert(store.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut potential_dead_stores = HashMap::new();
|
||||||
|
let mut current_offset = 0;
|
||||||
|
|
||||||
|
for stmt in &mir.stmts {
|
||||||
|
match &stmt.kind {
|
||||||
|
StmtKind::AddSub { store, offset, .. } => {
|
||||||
|
mark_store(&mut potential_dead_stores, current_offset + offset, store);
|
||||||
|
}
|
||||||
|
StmtKind::MoveAddTo {
|
||||||
|
offset,
|
||||||
|
store_move,
|
||||||
|
store_set_null,
|
||||||
|
} => {
|
||||||
|
mark_store(&mut potential_dead_stores, current_offset, store_set_null);
|
||||||
|
mark_store(
|
||||||
|
&mut potential_dead_stores,
|
||||||
|
current_offset + offset,
|
||||||
|
store_move,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
StmtKind::PointerMove(offset) => {
|
||||||
|
current_offset -= offset; // ???
|
||||||
|
}
|
||||||
|
StmtKind::Loop(_) | StmtKind::Out => {
|
||||||
|
let store = potential_dead_stores.get(¤t_offset);
|
||||||
|
if let Some(store) = store {
|
||||||
|
store.add_load();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StmtKind::In(store) | StmtKind::SetN(_, store) => {
|
||||||
|
mark_store(&mut potential_dead_stores, current_offset, store);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if stmt.state.has_forget_delta() {
|
||||||
|
// they might all have loads now
|
||||||
|
for store in potential_dead_stores.values_mut() {
|
||||||
|
// TODO STOP: WE MUTATE THE STATE HERE!!! ALL COOL DEAD STORES WILL BE CLOBBERED
|
||||||
|
store.clobber();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// test pass
|
||||||
#[tracing::instrument(skip(mir))]
|
#[tracing::instrument(skip(mir))]
|
||||||
fn pass_const_propagation(mir: &mut Mir<'_>) {
|
fn pass_const_propagation(mir: &mut Mir<'_>) {
|
||||||
pass_const_propagation_inner(mir)
|
pass_const_propagation_inner(mir)
|
||||||
|
|
@ -111,15 +190,13 @@ fn pass_const_propagation(mir: &mut Mir<'_>) {
|
||||||
fn pass_const_propagation_inner(mir: &mut Mir<'_>) {
|
fn pass_const_propagation_inner(mir: &mut Mir<'_>) {
|
||||||
for stmt in &mut mir.stmts {
|
for stmt in &mut mir.stmts {
|
||||||
match &mut stmt.kind {
|
match &mut stmt.kind {
|
||||||
StmtKind::Out(_) => {
|
StmtKind::Out => {
|
||||||
let state = stmt.state.state_for_offset(0);
|
let state = stmt.state.state_for_offset(0);
|
||||||
info!(?state, "We got the state of the output 😳😳😳");
|
|
||||||
// we could now insert a `SetN` before the `Out`, to mark the previous store
|
// we could now insert a `SetN` before the `Out`, to mark the previous store
|
||||||
// as dead.
|
// as dead.
|
||||||
}
|
}
|
||||||
StmtKind::Loop(body, _) => {
|
StmtKind::Loop(body) => {
|
||||||
let state = stmt.state.state_for_offset(0);
|
let state = stmt.state.state_for_offset(0);
|
||||||
info!(?state, "We got the state of the output 😳😳😳");
|
|
||||||
// we could now insert a `SetN` before the `Loop`, to mark the previous store
|
// we could now insert a `SetN` before the `Loop`, to mark the previous store
|
||||||
// as dead.
|
// as dead.
|
||||||
pass_const_propagation_inner(body);
|
pass_const_propagation_inner(body);
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use std::{
|
||||||
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
|
||||||
use crate::BumpVec;
|
use crate::{mir::Offset, BumpVec};
|
||||||
|
|
||||||
/// The known state of a cell in the MIR
|
/// The known state of a cell in the MIR
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -28,16 +28,19 @@ pub enum CellState {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum MemoryStateChange {
|
pub enum MemoryStateChange {
|
||||||
/// A cell value was changed to a new state.
|
/// A cell value was changed to a new state.
|
||||||
Change { offset: i32, new_state: CellState },
|
Change {
|
||||||
|
offset: Offset,
|
||||||
|
new_state: CellState,
|
||||||
|
},
|
||||||
/// The pointer was moved. This affects the `offset` calculations from previous states.
|
/// The pointer was moved. This affects the `offset` calculations from previous states.
|
||||||
Move(i32),
|
Move(Offset),
|
||||||
/// Forget everything about the memory state. This currently happens after each loop, since
|
/// Forget everything about the memory state. This currently happens after each loop, since
|
||||||
/// the loop is opaque and might clobber everything.
|
/// the loop is opaque and might clobber everything.
|
||||||
Forget,
|
Forget,
|
||||||
/// Load a value from memory. This is not a direct change of the memory itself, but it does
|
/// Load a value from memory. This is not a direct change of the memory itself, but it does
|
||||||
/// change the state in that it marks the corresponding store, if any, as alive. Loads should
|
/// change the state in that it marks the corresponding store, if any, as alive. Loads should
|
||||||
/// be eliminated whenever possible, to remove as many dead stores as possible.
|
/// be eliminated whenever possible, to remove as many dead stores as possible.
|
||||||
Load(Option<Store>),
|
Load { offset: Offset },
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The known state of memory at a specific instance in the instruction sequence
|
/// The known state of memory at a specific instance in the instruction sequence
|
||||||
|
|
@ -78,9 +81,17 @@ impl<'mir> MemoryState<'mir> {
|
||||||
Self(Rc::new(RefCell::new(MemoryStateInner { prev, deltas })))
|
Self(Rc::new(RefCell::new(MemoryStateInner { prev, deltas })))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn state_for_offset(&self, offset: i32) -> CellState {
|
pub fn state_for_offset(&self, offset: Offset) -> CellState {
|
||||||
self.0.borrow().state_for_offset(offset)
|
self.0.borrow().state_for_offset(offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_forget_delta(&self) -> bool {
|
||||||
|
self.0
|
||||||
|
.borrow()
|
||||||
|
.deltas
|
||||||
|
.iter()
|
||||||
|
.any(|d| matches!(d, MemoryStateChange::Forget))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for MemoryState<'_> {
|
impl Debug for MemoryState<'_> {
|
||||||
|
|
@ -94,13 +105,13 @@ impl Debug for MemoryState<'_> {
|
||||||
|
|
||||||
/// The known state of memory relative to the pointer
|
/// The known state of memory relative to the pointer
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct MemoryStateInner<'mir> {
|
struct MemoryStateInner<'mir> {
|
||||||
prev: Option<MemoryState<'mir>>,
|
prev: Option<MemoryState<'mir>>,
|
||||||
deltas: BumpVec<'mir, MemoryStateChange>,
|
deltas: BumpVec<'mir, MemoryStateChange>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'mir> MemoryStateInner<'mir> {
|
impl<'mir> MemoryStateInner<'mir> {
|
||||||
pub fn state_for_offset(&self, offset: i32) -> CellState {
|
fn state_for_offset(&self, offset: Offset) -> CellState {
|
||||||
let mut offset = offset;
|
let mut offset = offset;
|
||||||
for delta in &self.deltas {
|
for delta in &self.deltas {
|
||||||
match delta {
|
match delta {
|
||||||
|
|
@ -128,8 +139,8 @@ impl<'mir> MemoryStateInner<'mir> {
|
||||||
pub struct Store(Rc<Cell<StoreInner>>);
|
pub struct Store(Rc<Cell<StoreInner>>);
|
||||||
|
|
||||||
impl Store {
|
impl Store {
|
||||||
pub fn unknown() -> Self {
|
pub fn dead() -> Self {
|
||||||
StoreKind::Unknown.into()
|
StoreKind::Dead.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn id(&self) -> u64 {
|
pub fn id(&self) -> u64 {
|
||||||
|
|
@ -138,18 +149,38 @@ impl Store {
|
||||||
|
|
||||||
pub fn add_load(&self) {
|
pub fn add_load(&self) {
|
||||||
let old = self.inner();
|
let old = self.inner();
|
||||||
let new_kind = match old.kind {
|
let kind = match old.kind {
|
||||||
StoreKind::Unknown => StoreKind::UsedAtLeast(NonZeroU32::new(1).unwrap()),
|
StoreKind::Unknown => StoreKind::UsedAtLeast(NonZeroU32::new(1).unwrap()),
|
||||||
StoreKind::UsedExact(n) => StoreKind::UsedExact(n.checked_add(1).unwrap()),
|
StoreKind::UsedExact(n) => StoreKind::UsedExact(n.checked_add(1).unwrap()),
|
||||||
StoreKind::UsedAtLeast(n) => StoreKind::UsedAtLeast(n.checked_add(1).unwrap()),
|
StoreKind::UsedAtLeast(n) => StoreKind::UsedAtLeast(n.checked_add(1).unwrap()),
|
||||||
StoreKind::Dead => StoreKind::UsedExact(NonZeroU32::new(1).unwrap()),
|
StoreKind::Dead => StoreKind::UsedExact(NonZeroU32::new(1).unwrap()),
|
||||||
};
|
};
|
||||||
|
self.0.set(StoreInner { id: old.id, kind })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_maybe_dead(&self) -> bool {
|
||||||
|
matches!(self.inner().kind, StoreKind::Dead | StoreKind::Unknown)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mark_dead(&self) {
|
||||||
|
let old = self.inner();
|
||||||
self.0.set(StoreInner {
|
self.0.set(StoreInner {
|
||||||
id: old.id,
|
id: old.id,
|
||||||
kind: new_kind,
|
kind: StoreKind::Dead,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn clobber(&self) {
|
||||||
|
let old = self.inner();
|
||||||
|
let kind = match old.kind {
|
||||||
|
StoreKind::Unknown => StoreKind::Unknown,
|
||||||
|
StoreKind::UsedExact(n) => StoreKind::UsedAtLeast(n),
|
||||||
|
StoreKind::UsedAtLeast(n) => StoreKind::UsedAtLeast(n),
|
||||||
|
StoreKind::Dead => StoreKind::Unknown,
|
||||||
|
};
|
||||||
|
self.0.set(StoreInner { id: old.id, kind })
|
||||||
|
}
|
||||||
|
|
||||||
fn inner(&self) -> StoreInner {
|
fn inner(&self) -> StoreInner {
|
||||||
self.0.get()
|
self.0.get()
|
||||||
}
|
}
|
||||||
|
|
@ -157,7 +188,7 @@ impl Store {
|
||||||
|
|
||||||
impl Debug for Store {
|
impl Debug for Store {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
self.inner().fmt(f)
|
self.inner().kind.fmt(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -169,7 +200,7 @@ struct StoreInner {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
enum StoreKind {
|
enum StoreKind {
|
||||||
/// No information is known about uses of the store
|
/// No information is known about uses of the store, it has probably been clobbered
|
||||||
Unknown,
|
Unknown,
|
||||||
/// The exact amount of subsequent loads is known about the store, and it's this
|
/// The exact amount of subsequent loads is known about the store, and it's this
|
||||||
UsedExact(NonZeroU32),
|
UsedExact(NonZeroU32),
|
||||||
|
|
@ -187,12 +218,3 @@ impl From<StoreKind> for Store {
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A load from memory and from which store it was acquired
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum Load {
|
|
||||||
/// It is not known from which `Store` this was loaded
|
|
||||||
Unknown,
|
|
||||||
/// The load was acquired from this `Store`. The `Store` must either be `UsedExact` or `UsedAtLeast`
|
|
||||||
KnownStore(Store),
|
|
||||||
}
|
|
||||||
|
|
|
||||||
7
rust2/src/snapshots/brainfuck__tests__mandelbrot.snap
Normal file
7
rust2/src/snapshots/brainfuck__tests__mandelbrot.snap
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
source: src/lib.rs
|
||||||
|
expression: "String::from_utf8(stdout)"
|
||||||
|
---
|
||||||
|
Ok(
|
||||||
|
"AAAAAAAABBBBBBBBCCCCCCCCCCCCCCCCCCDDDDEFEEDDDCCCCCBBBBBBBBBBBBBBB\nAAAAAAABBBBBBCCCCCCCCCCCCCCCCCDDDDDDEEFIKGGGDDDDDCCCCBBBBBBBBBBBB\nAAAAAABBBBCCCCCCCCCCCCCCCCCDDDDDDDEEEFGHKPIGFEDDDDDCCCCCBBBBBBBBB\nAAAAABBBCCCCCCCCCCCCCCCCCDDDDDDDEEEFGPVT Q[HEEEEDDDCCCCCCBBBBBBB\nAAAABBCCCCCCCCCCCCCCCCDDDDDDDEEFFFGGHK HGFFEEEDDDCCCCCBBBBBB\nAAABBCCCCCCCCCCCCCCCDDDDDEEEFGK MJJ NR YS L HHGIJFDDCCCCCCBBBB\nAAABCCCCCCCCCCCCCDDDEEEEEEFFFHI MGEDDCCCCCCBBB\nAABCCCCCCCCCCCDDEEEEEEEEFFFGY Q MHGEEDCCCCCCCBB\nAACCCCCCDDDDDEEFLHGGHMHGGGHIR QLHEDDCCCCCCCB\nABCCDDDDDDEEEEFGIKU RLJJL IFEDDCCCCCCCB\nACDDDDDDEEEEEGGHOS QR JFEDDDCCCCCCC\nADDDDDEFFFGGHKOPS GEEDDDCCCCCCC\nA PJGFEEDDDCCCCCCC\nADDDDDEFFFGGHKOPS GEEDDDCCCCCCC\nACDDDDDDEEEEEGGHOS QR JFEDDDCCCCCCC\nABCCDDDDDDEEEEFGIKU RLJJL IFEDDCCCCCCCB\nAACCCCCCDDDDDEEFLHGGHMHGGGHIR QLHEDDCCCCCCCB\nAABCCCCCCCCCCCDDEEEEEEEEFFFGY Q MHGEEDCCCCCCCBB\nAAABCCCCCCCCCCCCCDDDEEEEEEFFFHI MGEDDCCCCCCBBB\nAAABBCCCCCCCCCCCCCCCDDDDDEEEFGK MJJ NR YS L HHGIJFDDCCCCCCBBBB\nAAAABBCCCCCCCCCCCCCCCCDDDDDDDEEFFFGGHK HGFFEEEDDDCCCCCBBBBBB\nAAAAABBBCCCCCCCCCCCCCCCCCDDDDDDDEEEFGPVT Q[HEEEEDDDCCCCCCBBBBBBB\nAAAAAABBBBCCCCCCCCCCCCCCCCCDDDDDDDEEEFGHKPIGFEDDDDDCCCCCBBBBBBBBB\nAAAAAAABBBBBBCCCCCCCCCCCCCCCCCDDDDDDEEFIKGGGDDDDDCCCCBBBBBBBBBBBB\n",
|
||||||
|
)
|
||||||
|
|
@ -1 +1 @@
|
||||||
++[<]
|
+><+
|
||||||
Loading…
Add table
Add a link
Reference in a new issue