mirror of
https://github.com/Noratrieb/brainfuck.git
synced 2026-01-14 13:35:00 +01:00
120 lines
3.6 KiB
Rust
120 lines
3.6 KiB
Rust
//! an experimental MIR (mid-level-ir)
|
|
//!
|
|
//! The MIR consists of two parts. First, there are instructions (`Stmt`). These instructions
|
|
//! can be seen as an extended version of the default brainfuck instruction set `+-<>,.[]`.
|
|
//! These instructions modify the classic tape. What MIR does is that it attaches an abstract
|
|
//! `MemoryState` to *each* statement. This state contains all facts known about the state of the
|
|
//! tape at the point of execution of the statement.
|
|
//!
|
|
//! For example, for the code `++.`, the `MemoryState` for the `.` instruction contains a single
|
|
//! fact: "The current cell was written to, by the instruction before and with the value 2". MIR
|
|
//! tracks as much of the reads/writes to determine their dependencies and eliminate as many
|
|
//! of them as possible.
|
|
//!
|
|
//! Note that MIR is always pessimized, so if it can't determine for sure that something is true,
|
|
//! it will not act on it.
|
|
#![allow(dead_code)]
|
|
|
|
mod opts;
|
|
mod state;
|
|
|
|
use std::fmt::{Debug, Formatter};
|
|
|
|
use bumpalo::Bump;
|
|
|
|
use crate::{
|
|
hir::{Hir, StmtKind as HirStmtKind},
|
|
mir::state::{MemoryState, Store},
|
|
parse::Span,
|
|
BumpVec,
|
|
};
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Mir<'mir> {
|
|
stmts: BumpVec<'mir, Stmt<'mir>>,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
struct Stmt<'mir> {
|
|
kind: StmtKind<'mir>,
|
|
state: MemoryState<'mir>,
|
|
span: Span,
|
|
}
|
|
|
|
impl Debug for Stmt<'_> {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_struct("Stmt")
|
|
.field("kind", &self.kind)
|
|
.field("state", &self.state)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
type Offset = i32;
|
|
|
|
#[derive(Debug, Clone)]
|
|
enum StmtKind<'mir> {
|
|
/// Add or sub, the value has the valid range -255..=255
|
|
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`
|
|
MoveAddTo {
|
|
offset: Offset,
|
|
store_set_null: Store,
|
|
store_move: Store,
|
|
},
|
|
/// Left or Right pointer move (`<>`)
|
|
PointerMove(Offset),
|
|
Loop(Mir<'mir>),
|
|
Out,
|
|
In(Store),
|
|
SetN(u8, Store),
|
|
}
|
|
|
|
#[tracing::instrument(skip(alloc, hir))]
|
|
pub fn optimized_mir<'mir>(alloc: &'mir Bump, hir: &Hir<'_>) -> Mir<'mir> {
|
|
let mut mir = hir_to_mir(alloc, hir);
|
|
opts::passes(alloc, &mut mir);
|
|
mir
|
|
}
|
|
|
|
/// compiles hir down to a minimal mir
|
|
fn hir_to_mir<'mir>(alloc: &'mir Bump, hir: &Hir<'_>) -> Mir<'mir> {
|
|
let mut stmts = Vec::new_in(alloc);
|
|
let iter = hir.stmts.iter().map(|hir_stmt| {
|
|
let kind = match *hir_stmt.kind() {
|
|
HirStmtKind::Add(offset, n) => StmtKind::AddSub {
|
|
offset,
|
|
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 {
|
|
offset,
|
|
store_set_null: Store::dead(),
|
|
store_move: Store::dead(),
|
|
},
|
|
HirStmtKind::Right(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)),
|
|
HirStmtKind::Out => StmtKind::Out,
|
|
HirStmtKind::In => StmtKind::In(Store::dead()),
|
|
HirStmtKind::SetN(n) => StmtKind::SetN(n, Store::dead()),
|
|
};
|
|
Stmt {
|
|
kind,
|
|
span: hir_stmt.span,
|
|
state: MemoryState::empty(alloc),
|
|
}
|
|
});
|
|
stmts.extend(iter);
|
|
|
|
Mir { stmts }
|
|
}
|