mirror of
https://github.com/Noratrieb/brainfuck.git
synced 2026-01-14 13:35:00 +01:00
add mir
This commit is contained in:
parent
d0718adf7f
commit
80b1b0e3f6
4 changed files with 341 additions and 0 deletions
|
|
@ -16,6 +16,7 @@ use crate::parse::ParseError;
|
||||||
|
|
||||||
pub mod hir;
|
pub mod hir;
|
||||||
pub mod lir;
|
pub mod lir;
|
||||||
|
mod mir;
|
||||||
pub mod parse;
|
pub mod parse;
|
||||||
|
|
||||||
#[derive(clap::Parser, Default)]
|
#[derive(clap::Parser, Default)]
|
||||||
|
|
@ -31,6 +32,7 @@ pub struct Args {
|
||||||
pub enum DumpKind {
|
pub enum DumpKind {
|
||||||
Ast,
|
Ast,
|
||||||
Hir,
|
Hir,
|
||||||
|
Mir,
|
||||||
Lir,
|
Lir,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -41,6 +43,7 @@ impl FromStr for DumpKind {
|
||||||
match s {
|
match s {
|
||||||
"ast" => Ok(Self::Ast),
|
"ast" => Ok(Self::Ast),
|
||||||
"hir" => Ok(Self::Hir),
|
"hir" => Ok(Self::Hir),
|
||||||
|
"mir" => Ok(Self::Mir),
|
||||||
"lir" => Ok(Self::Lir),
|
"lir" => Ok(Self::Lir),
|
||||||
other => Err(format!("Invalid IR level: '{other}'")),
|
other => Err(format!("Invalid IR level: '{other}'")),
|
||||||
}
|
}
|
||||||
|
|
@ -80,6 +83,12 @@ where
|
||||||
drop(parsed);
|
drop(parsed);
|
||||||
drop(ast_alloc);
|
drop(ast_alloc);
|
||||||
|
|
||||||
|
if let Some(DumpKind::Mir) = config.dump {
|
||||||
|
let mir_alloc = Bump::new();
|
||||||
|
let mir = mir::optimized_mir(&mir_alloc, &optimized_hir);
|
||||||
|
println!("{mir:#?}");
|
||||||
|
}
|
||||||
|
|
||||||
let cg_alloc = Bump::new();
|
let cg_alloc = Bump::new();
|
||||||
|
|
||||||
let lir = lir::generate(&cg_alloc, &optimized_hir);
|
let lir = lir::generate(&cg_alloc, &optimized_hir);
|
||||||
|
|
|
||||||
94
rust2/src/mir/mod.rs
Normal file
94
rust2/src/mir/mod.rs
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
//! an experimental MIR (mid-level-ir)
|
||||||
|
#![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, StoreInner},
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
enum StmtKind<'mir> {
|
||||||
|
/// Add or sub, the value has the valid range -255..=255
|
||||||
|
AddSub(i32, i16, Store),
|
||||||
|
/// Sets the current cell to 0 and adds that value of the cell to another cell at `offset`
|
||||||
|
MoveAddTo {
|
||||||
|
offset: i32,
|
||||||
|
store_set_null: Store,
|
||||||
|
store_move: Store,
|
||||||
|
},
|
||||||
|
/// Left or Right pointer move (`<>`)
|
||||||
|
PointerMove(i32),
|
||||||
|
Loop(Mir<'mir>),
|
||||||
|
Out,
|
||||||
|
In(Store),
|
||||||
|
SetN(u8, Store),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
|
pub fn optimized_mir<'mir>(alloc: &'mir Bump, hir: &Hir<'_>) -> Mir<'mir> {
|
||||||
|
let mut mir = hir_to_mir(alloc, hir);
|
||||||
|
opts::pass_get_state_info(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, i16::from(n), Store::unknown()),
|
||||||
|
HirStmtKind::Sub(offset, n) => {
|
||||||
|
StmtKind::AddSub(offset, -i16::from(n), Store::unknown())
|
||||||
|
}
|
||||||
|
HirStmtKind::MoveAddTo { offset } => StmtKind::MoveAddTo {
|
||||||
|
offset,
|
||||||
|
store_set_null: Store::unknown(),
|
||||||
|
store_move: Store::unknown(),
|
||||||
|
},
|
||||||
|
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(StoreInner::Unknown.into()),
|
||||||
|
HirStmtKind::SetN(n) => StmtKind::SetN(n, Store::unknown()),
|
||||||
|
};
|
||||||
|
Stmt {
|
||||||
|
kind,
|
||||||
|
span: hir_stmt.span,
|
||||||
|
state: MemoryState::empty(alloc),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
stmts.extend(iter);
|
||||||
|
|
||||||
|
Mir { stmts }
|
||||||
|
}
|
||||||
89
rust2/src/mir/opts.rs
Normal file
89
rust2/src/mir/opts.rs
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
use bumpalo::Bump;
|
||||||
|
|
||||||
|
use crate::mir::{
|
||||||
|
state::{CellState, MemoryState, MemoryStateChange},
|
||||||
|
Mir, StmtKind,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// this pass fills out as much state info for all statements as possible
|
||||||
|
#[tracing::instrument]
|
||||||
|
pub fn pass_get_state_info<'mir>(alloc: &'mir Bump, mir: &mut Mir<'mir>) {
|
||||||
|
let empty_state = MemoryState::empty(alloc);
|
||||||
|
pass_get_state_info_inner(alloc, mir, empty_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
|
fn pass_get_state_info_inner<'mir>(
|
||||||
|
alloc: &'mir Bump,
|
||||||
|
mir: &mut Mir<'mir>,
|
||||||
|
mut outer: MemoryState<'mir>,
|
||||||
|
) {
|
||||||
|
for stmt in &mut mir.stmts {
|
||||||
|
let state = match &mut stmt.kind {
|
||||||
|
StmtKind::AddSub(offset, _, store) => MemoryState::single(
|
||||||
|
alloc,
|
||||||
|
outer,
|
||||||
|
MemoryStateChange::Change {
|
||||||
|
offset: *offset,
|
||||||
|
new_state: CellState::WrittenToUnknown(store.clone()),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
StmtKind::MoveAddTo {
|
||||||
|
offset,
|
||||||
|
store_set_null,
|
||||||
|
store_move,
|
||||||
|
} => MemoryState::double(
|
||||||
|
alloc,
|
||||||
|
outer,
|
||||||
|
MemoryStateChange::Change {
|
||||||
|
offset: 0,
|
||||||
|
new_state: CellState::WrittenToKnown(store_set_null.clone(), 0),
|
||||||
|
},
|
||||||
|
MemoryStateChange::Change {
|
||||||
|
offset: *offset,
|
||||||
|
new_state: CellState::WrittenToUnknown(store_move.clone()),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
StmtKind::PointerMove(n) => {
|
||||||
|
MemoryState::single(alloc, outer, MemoryStateChange::Move(*n))
|
||||||
|
}
|
||||||
|
StmtKind::Loop(body) => {
|
||||||
|
// TODO: we can get a lot smarter here and get huge benefits; we don't yet
|
||||||
|
pass_get_state_info_inner(alloc, body, MemoryState::empty(alloc));
|
||||||
|
MemoryState::double(
|
||||||
|
alloc,
|
||||||
|
outer,
|
||||||
|
// forget all knowledge, the opaque loop might have touched it all
|
||||||
|
MemoryStateChange::Forget,
|
||||||
|
// we certainly know that the current cell is zero, since the loop exited
|
||||||
|
MemoryStateChange::Change {
|
||||||
|
offset: 0,
|
||||||
|
new_state: CellState::LoopNull,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
StmtKind::Out => outer,
|
||||||
|
StmtKind::In(store) => MemoryState::single(
|
||||||
|
alloc,
|
||||||
|
outer,
|
||||||
|
MemoryStateChange::Change {
|
||||||
|
offset: 0,
|
||||||
|
new_state: CellState::WrittenToUnknown(store.clone()),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
StmtKind::SetN(value, store) => MemoryState::single(
|
||||||
|
alloc,
|
||||||
|
outer,
|
||||||
|
MemoryStateChange::Change {
|
||||||
|
offset: 0,
|
||||||
|
new_state: CellState::WrittenToKnown(store.clone(), *value),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
};
|
||||||
|
stmt.state = state.clone();
|
||||||
|
outer = state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
|
fn pass_const_propagation(mir: &mut Mir<'_>) {}
|
||||||
149
rust2/src/mir/state.rs
Normal file
149
rust2/src/mir/state.rs
Normal file
|
|
@ -0,0 +1,149 @@
|
||||||
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
|
fmt::{Debug, Formatter},
|
||||||
|
rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
|
use bumpalo::Bump;
|
||||||
|
|
||||||
|
use crate::BumpVec;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum CellState {
|
||||||
|
Unknown,
|
||||||
|
LoopNull,
|
||||||
|
WrittenToUnknown(Store),
|
||||||
|
WrittenToKnown(Store, u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A change in the known state of the memory caused by a single instruction
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum MemoryStateChange {
|
||||||
|
/// A cell was changed
|
||||||
|
Change { offset: i32, new_state: CellState },
|
||||||
|
/// The pointer was moved
|
||||||
|
Move(i32),
|
||||||
|
/// Forget everything
|
||||||
|
Forget,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct MemoryState<'mir>(Rc<RefCell<MemoryStateInner<'mir>>>);
|
||||||
|
|
||||||
|
impl<'mir> MemoryState<'mir> {
|
||||||
|
pub fn empty(alloc: &'mir Bump) -> Self {
|
||||||
|
Self(Rc::new(RefCell::new(MemoryStateInner {
|
||||||
|
prev: None,
|
||||||
|
deltas: Vec::new_in(alloc),
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn state_for_offset(&self, offset: i32) -> &'mir CellState {
|
||||||
|
self.0.borrow().state_for_offset(offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for MemoryState<'_> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.0
|
||||||
|
.try_borrow()
|
||||||
|
.map(|s| MemoryStateInner::fmt(&*s, f))
|
||||||
|
.unwrap_or_else(|_| f.debug_struct("MemoryState").finish_non_exhaustive())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The known state of memory relative to the pointer
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct MemoryStateInner<'mir> {
|
||||||
|
prev: Option<MemoryState<'mir>>,
|
||||||
|
deltas: BumpVec<'mir, MemoryStateChange>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'mir> MemoryStateInner<'mir> {
|
||||||
|
pub fn state_for_offset(&self, offset: i32) -> &'mir CellState {
|
||||||
|
let mut offset = offset;
|
||||||
|
for delta in &self.deltas {
|
||||||
|
match delta {
|
||||||
|
MemoryStateChange::Change {
|
||||||
|
offset: write_offset,
|
||||||
|
new_state,
|
||||||
|
} if *write_offset == offset => {
|
||||||
|
return new_state;
|
||||||
|
}
|
||||||
|
MemoryStateChange::Move(change) => offset -= change,
|
||||||
|
// we may not access the forbidden knowledge
|
||||||
|
MemoryStateChange::Forget => return &CellState::Unknown,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.prev
|
||||||
|
.map(|state| state.state_for_offset(offset))
|
||||||
|
.unwrap_or(&CellState::Unknown)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'mir> MemoryState<'mir> {
|
||||||
|
pub fn single(
|
||||||
|
alloc: &'mir Bump,
|
||||||
|
prev: MemoryState<'mir>,
|
||||||
|
delta: MemoryStateChange,
|
||||||
|
) -> MemoryState<'mir> {
|
||||||
|
let mut deltas = Vec::new_in(alloc);
|
||||||
|
deltas.push(delta);
|
||||||
|
Self::new(prev, deltas)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn double(
|
||||||
|
alloc: &'mir Bump,
|
||||||
|
prev: MemoryState<'mir>,
|
||||||
|
delta1: MemoryStateChange,
|
||||||
|
delta2: MemoryStateChange,
|
||||||
|
) -> MemoryState<'mir> {
|
||||||
|
let mut deltas = Vec::new_in(alloc);
|
||||||
|
deltas.push(delta1);
|
||||||
|
deltas.push(delta2);
|
||||||
|
Self::new(prev, deltas)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(
|
||||||
|
prev: MemoryState<'mir>,
|
||||||
|
deltas: BumpVec<'mir, MemoryStateChange>,
|
||||||
|
) -> MemoryState<'mir> {
|
||||||
|
Self(Rc::new(RefCell::new(MemoryStateInner {
|
||||||
|
prev: Some(prev),
|
||||||
|
deltas,
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Store(Rc<RefCell<StoreInner>>);
|
||||||
|
|
||||||
|
impl Store {
|
||||||
|
pub fn unknown() -> Self {
|
||||||
|
StoreInner::Unknown.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Store {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.0
|
||||||
|
.try_borrow()
|
||||||
|
.map(|s| StoreInner::fmt(&*s, f))
|
||||||
|
.unwrap_or_else(|_| f.debug_struct("Store").finish_non_exhaustive())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum StoreInner {
|
||||||
|
Unknown,
|
||||||
|
Used(usize),
|
||||||
|
Dead,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<StoreInner> for Store {
|
||||||
|
fn from(inner: StoreInner) -> Self {
|
||||||
|
Self(Rc::new(RefCell::new(inner)))
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue