mirror of
https://github.com/Noratrieb/brainfuck.git
synced 2026-01-14 13:35:00 +01:00
rename a bunch of stuff
This commit is contained in:
parent
ddf2686049
commit
5fc5c49dff
10 changed files with 252 additions and 215 deletions
3
rust2/.rustfmt.toml
Normal file
3
rust2/.rustfmt.toml
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
imports_granularity = "Crate"
|
||||||
|
newline_style = "Unix"
|
||||||
|
group_imports = "StdExternalCrate"
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use criterion::{black_box, criterion_main, Criterion};
|
use criterion::{black_box, criterion_main, Criterion};
|
||||||
use std::io::{Read, Write};
|
|
||||||
|
|
||||||
struct MockReadWrite;
|
struct MockReadWrite;
|
||||||
|
|
||||||
|
|
@ -24,9 +25,9 @@ impl Write for MockReadWrite {
|
||||||
fn run_bf(bf: &str) {
|
fn run_bf(bf: &str) {
|
||||||
let bump = Bump::new();
|
let bump = Bump::new();
|
||||||
let ast = brainfuck::parse::parse(&bump, bf.bytes().enumerate()).unwrap();
|
let ast = brainfuck::parse::parse(&bump, bf.bytes().enumerate()).unwrap();
|
||||||
let ir = brainfuck::opts::optimize(&bump, &ast);
|
let hir = brainfuck::hir::optimized_hir(&bump, &ast);
|
||||||
let code = brainfuck::codegen::generate(&bump, &ir);
|
let lir = brainfuck::lir::generate(&bump, &hir);
|
||||||
brainfuck::codegen_interpreter::run(&code, MockReadWrite, MockReadWrite, |_| {});
|
brainfuck::lir::interpreter::run(&lir, MockReadWrite, MockReadWrite, |_| {});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn optimized(c: &mut Criterion) {
|
fn optimized(c: &mut Criterion) {
|
||||||
|
|
|
||||||
89
rust2/src/hir/mod.rs
Normal file
89
rust2/src/hir/mod.rs
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
use std::fmt::{Debug, Formatter};
|
||||||
|
|
||||||
|
use bumpalo::Bump;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
parse::{Ast, Instr, Span},
|
||||||
|
BumpVec,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub mod opts;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Hir<'hir> {
|
||||||
|
pub stmts: BumpVec<'hir, Stmt<'hir>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Hir<'_> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.stmts.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Stmt<'hir> {
|
||||||
|
pub kind: StmtKind<'hir>,
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'hir> Stmt<'hir> {
|
||||||
|
fn new(kind: StmtKind<'hir>, span: Span) -> Stmt<'hir> {
|
||||||
|
Self { kind, span }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kind(&self) -> &StmtKind<'hir> {
|
||||||
|
&self.kind
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Stmt<'_> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.kind.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum StmtKind<'hir> {
|
||||||
|
Add(i32, u8),
|
||||||
|
Sub(i32, u8),
|
||||||
|
/// Sets the current cell to 0 and adds that value of the cell to another cell at `offset`
|
||||||
|
MoveAddTo {
|
||||||
|
offset: i32,
|
||||||
|
},
|
||||||
|
Right(usize),
|
||||||
|
Left(usize),
|
||||||
|
Loop(Hir<'hir>),
|
||||||
|
Out,
|
||||||
|
In,
|
||||||
|
SetN(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ast_to_ir<'hir>(alloc: &'hir Bump, ast: &Ast<'_>) -> Hir<'hir> {
|
||||||
|
let mut stmts = Vec::new_in(alloc);
|
||||||
|
|
||||||
|
let stmts_iter = ast.iter().map(|(instr, span)| {
|
||||||
|
let kind = match instr {
|
||||||
|
Instr::Add => StmtKind::Add(0, 1),
|
||||||
|
Instr::Sub => StmtKind::Sub(0, 1),
|
||||||
|
Instr::Right => StmtKind::Right(1),
|
||||||
|
Instr::Left => StmtKind::Left(1),
|
||||||
|
Instr::Out => StmtKind::Out,
|
||||||
|
Instr::In => StmtKind::In,
|
||||||
|
Instr::Loop(body) => {
|
||||||
|
let ir_body = ast_to_ir(alloc, body);
|
||||||
|
StmtKind::Loop(ir_body)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Stmt::new(kind, *span)
|
||||||
|
});
|
||||||
|
|
||||||
|
stmts.extend(stmts_iter);
|
||||||
|
|
||||||
|
Hir { stmts }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn optimized_hir<'hir>(alloc: &'hir Bump, ast: &Ast<'_>) -> Hir<'hir> {
|
||||||
|
let mut hir = ast_to_ir(alloc, ast);
|
||||||
|
opts::optimize(alloc, &mut hir);
|
||||||
|
hir
|
||||||
|
}
|
||||||
|
|
@ -1,111 +1,38 @@
|
||||||
use crate::parse::{Instr, Span};
|
|
||||||
use crate::BumpVec;
|
|
||||||
use bumpalo::Bump;
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::fmt::{Debug, Formatter};
|
|
||||||
|
use bumpalo::Bump;
|
||||||
use tracing::trace;
|
use tracing::trace;
|
||||||
|
|
||||||
#[derive(Clone)]
|
use crate::{
|
||||||
pub struct Ir<'ir> {
|
hir::{Hir, Stmt, StmtKind},
|
||||||
pub stmts: BumpVec<'ir, Stmt<'ir>>,
|
BumpVec,
|
||||||
}
|
};
|
||||||
|
|
||||||
impl Debug for Ir<'_> {
|
pub fn optimize<'hir>(alloc: &'hir Bump, hir: &mut Hir<'hir>) {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
pass_group(alloc, hir);
|
||||||
self.stmts.fmt(f)
|
pass_find_set_null(hir);
|
||||||
}
|
pass_set_n(hir);
|
||||||
}
|
pass_cancel_left_right_add_sub(hir);
|
||||||
|
pass_add_sub_offset(hir);
|
||||||
#[derive(Clone)]
|
pass_move_add_to(hir);
|
||||||
pub struct Stmt<'ir> {
|
|
||||||
pub kind: StmtKind<'ir>,
|
|
||||||
pub span: Span,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ir> Stmt<'ir> {
|
|
||||||
fn new(kind: StmtKind<'ir>, span: Span) -> Stmt<'ir> {
|
|
||||||
Self { kind, span }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn kind(&self) -> &StmtKind<'ir> {
|
|
||||||
&self.kind
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for Stmt<'_> {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
self.kind.fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum StmtKind<'ir> {
|
|
||||||
Add(i32, u8),
|
|
||||||
Sub(i32, u8),
|
|
||||||
/// Sets the current cell to 0 and adds that value of the cell to another cell at `offset`
|
|
||||||
MoveAddTo {
|
|
||||||
offset: i32,
|
|
||||||
},
|
|
||||||
Right(usize),
|
|
||||||
Left(usize),
|
|
||||||
Loop(Ir<'ir>),
|
|
||||||
Out,
|
|
||||||
In,
|
|
||||||
SetN(u8),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn optimize<'ir>(alloc: &'ir Bump, instrs: &[(Instr<'_>, Span)]) -> Ir<'ir> {
|
|
||||||
let mut ir = ast_to_ir(alloc, instrs);
|
|
||||||
pass_group(alloc, &mut ir);
|
|
||||||
pass_find_set_null(&mut ir);
|
|
||||||
pass_set_n(&mut ir);
|
|
||||||
pass_cancel_left_right_add_sub(&mut ir);
|
|
||||||
pass_add_sub_offset(&mut ir);
|
|
||||||
pass_move_add_to(&mut ir);
|
|
||||||
|
|
||||||
ir
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ast_to_ir<'ir>(alloc: &'ir Bump, ast: &[(Instr<'_>, Span)]) -> Ir<'ir> {
|
|
||||||
let mut stmts = Vec::new_in(alloc);
|
|
||||||
|
|
||||||
let stmts_iter = ast.iter().map(|(instr, span)| {
|
|
||||||
let kind = match instr {
|
|
||||||
Instr::Add => StmtKind::Add(0, 1),
|
|
||||||
Instr::Sub => StmtKind::Sub(0, 1),
|
|
||||||
Instr::Right => StmtKind::Right(1),
|
|
||||||
Instr::Left => StmtKind::Left(1),
|
|
||||||
Instr::Out => StmtKind::Out,
|
|
||||||
Instr::In => StmtKind::In,
|
|
||||||
Instr::Loop(body) => {
|
|
||||||
let ir_body = ast_to_ir(alloc, body);
|
|
||||||
StmtKind::Loop(ir_body)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Stmt::new(kind, *span)
|
|
||||||
});
|
|
||||||
|
|
||||||
stmts.extend(stmts_iter);
|
|
||||||
|
|
||||||
Ir { stmts }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// pass that replaces things like `Sub(1) Sub(1)` with `Sub(2)`
|
/// pass that replaces things like `Sub(1) Sub(1)` with `Sub(2)`
|
||||||
// TODO: This pass is really slow, speed it up please
|
// TODO: This pass is really slow, speed it up please
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
fn pass_group<'ir>(alloc: &'ir Bump, ir_param: &mut Ir<'ir>) {
|
fn pass_group<'hir>(alloc: &'hir Bump, ir_param: &mut Hir<'hir>) {
|
||||||
let empty_ir = Ir {
|
let empty_ir = Hir {
|
||||||
stmts: Vec::new_in(alloc),
|
stmts: Vec::new_in(alloc),
|
||||||
};
|
};
|
||||||
|
|
||||||
let ir = std::mem::replace(ir_param, empty_ir);
|
let ir = std::mem::replace(ir_param, empty_ir);
|
||||||
|
|
||||||
let new_stmts = Vec::new_in(alloc);
|
let new_stmts = Vec::new_in(alloc);
|
||||||
let stmts = ir
|
let stmts =
|
||||||
.stmts
|
ir.stmts
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.fold(new_stmts, |mut stmts: BumpVec<'ir, Stmt<'ir>>, next| {
|
.fold(new_stmts, |mut stmts: BumpVec<'hir, Stmt<'hir>>, next| {
|
||||||
let Some(old) = stmts.last_mut() else {
|
let Some(old) = stmts.last_mut() else {
|
||||||
if let StmtKind::Loop(mut body) = next.kind {
|
if let StmtKind::Loop(mut body) = next.kind {
|
||||||
pass_group(alloc, &mut body);
|
pass_group(alloc, &mut body);
|
||||||
stmts.push(Stmt::new(
|
stmts.push(Stmt::new(
|
||||||
|
|
@ -118,48 +45,48 @@ fn pass_group<'ir>(alloc: &'ir Bump, ir_param: &mut Ir<'ir>) {
|
||||||
return stmts;
|
return stmts;
|
||||||
};
|
};
|
||||||
|
|
||||||
match (&mut old.kind, next.kind) {
|
match (&mut old.kind, next.kind) {
|
||||||
(StmtKind::Add(offset_a, a), StmtKind::Add(offset_b, b))
|
(StmtKind::Add(offset_a, a), StmtKind::Add(offset_b, b))
|
||||||
if *a < 255 && *offset_a == offset_b =>
|
if *a < 255 && *offset_a == offset_b =>
|
||||||
{
|
{
|
||||||
old.span = old.span.merge(next.span);
|
old.span = old.span.merge(next.span);
|
||||||
*a += b;
|
*a += b;
|
||||||
|
}
|
||||||
|
(StmtKind::Sub(offset_a, a), StmtKind::Sub(offset_b, b))
|
||||||
|
if *a < 255 && *offset_a == offset_b =>
|
||||||
|
{
|
||||||
|
old.span = old.span.merge(next.span);
|
||||||
|
*a += b;
|
||||||
|
}
|
||||||
|
(StmtKind::Right(a), StmtKind::Right(b)) if *a < 255 => {
|
||||||
|
old.span = old.span.merge(next.span);
|
||||||
|
*a += b;
|
||||||
|
}
|
||||||
|
(StmtKind::Left(a), StmtKind::Left(b)) if *a < 255 => {
|
||||||
|
old.span = old.span.merge(next.span);
|
||||||
|
*a += b;
|
||||||
|
}
|
||||||
|
(_, StmtKind::Loop(mut body)) => {
|
||||||
|
pass_group(alloc, &mut body);
|
||||||
|
stmts.push(Stmt {
|
||||||
|
span: next.span,
|
||||||
|
kind: StmtKind::Loop(body),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
(_, kind) => {
|
||||||
|
stmts.push(Stmt::new(kind, next.span));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
(StmtKind::Sub(offset_a, a), StmtKind::Sub(offset_b, b))
|
|
||||||
if *a < 255 && *offset_a == offset_b =>
|
|
||||||
{
|
|
||||||
old.span = old.span.merge(next.span);
|
|
||||||
*a += b;
|
|
||||||
}
|
|
||||||
(StmtKind::Right(a), StmtKind::Right(b)) if *a < 255 => {
|
|
||||||
old.span = old.span.merge(next.span);
|
|
||||||
*a += b;
|
|
||||||
}
|
|
||||||
(StmtKind::Left(a), StmtKind::Left(b)) if *a < 255 => {
|
|
||||||
old.span = old.span.merge(next.span);
|
|
||||||
*a += b;
|
|
||||||
}
|
|
||||||
(_, StmtKind::Loop(mut body)) => {
|
|
||||||
pass_group(alloc, &mut body);
|
|
||||||
stmts.push(Stmt {
|
|
||||||
span: next.span,
|
|
||||||
kind: StmtKind::Loop(body),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
(_, kind) => {
|
|
||||||
stmts.push(Stmt::new(kind, next.span));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stmts
|
stmts
|
||||||
});
|
});
|
||||||
|
|
||||||
*ir_param = Ir { stmts };
|
*ir_param = Hir { stmts };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// pass that replaces `Loop([Sub(_)])` to `SetNull`
|
/// pass that replaces `Loop([Sub(_)])` to `SetNull`
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
fn pass_find_set_null(ir: &mut Ir<'_>) {
|
fn pass_find_set_null(ir: &mut Hir<'_>) {
|
||||||
for stmt in &mut ir.stmts {
|
for stmt in &mut ir.stmts {
|
||||||
if let Stmt {
|
if let Stmt {
|
||||||
kind: StmtKind::Loop(body),
|
kind: StmtKind::Loop(body),
|
||||||
|
|
@ -182,7 +109,7 @@ fn pass_find_set_null(ir: &mut Ir<'_>) {
|
||||||
|
|
||||||
/// pass that replaces `SetN(n) Add(m)` with `SetN(n + m)`
|
/// pass that replaces `SetN(n) Add(m)` with `SetN(n + m)`
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
fn pass_set_n(ir: &mut Ir<'_>) {
|
fn pass_set_n(ir: &mut Hir<'_>) {
|
||||||
window_pass(ir, pass_set_n, |[a, b]| {
|
window_pass(ir, pass_set_n, |[a, b]| {
|
||||||
if let StmtKind::SetN(before) = a.kind() {
|
if let StmtKind::SetN(before) = a.kind() {
|
||||||
let new = match b.kind() {
|
let new = match b.kind() {
|
||||||
|
|
@ -200,7 +127,7 @@ fn pass_set_n(ir: &mut Ir<'_>) {
|
||||||
|
|
||||||
/// pass that replaces `Left(5) Right(3)` with `Left(2)`
|
/// pass that replaces `Left(5) Right(3)` with `Left(2)`
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
fn pass_cancel_left_right_add_sub(ir: &mut Ir<'_>) {
|
fn pass_cancel_left_right_add_sub(ir: &mut Hir<'_>) {
|
||||||
window_pass(ir, pass_cancel_left_right_add_sub, |[a, b]| {
|
window_pass(ir, pass_cancel_left_right_add_sub, |[a, b]| {
|
||||||
match (a.kind(), b.kind()) {
|
match (a.kind(), b.kind()) {
|
||||||
(StmtKind::Right(r), StmtKind::Left(l)) | (StmtKind::Left(l), StmtKind::Right(r)) => {
|
(StmtKind::Right(r), StmtKind::Left(l)) | (StmtKind::Left(l), StmtKind::Right(r)) => {
|
||||||
|
|
@ -233,7 +160,7 @@ fn pass_cancel_left_right_add_sub(ir: &mut Ir<'_>) {
|
||||||
|
|
||||||
/// pass that replaces `Right(9) Add(5) Left(9)` with `AddOffset(9, 5)`
|
/// pass that replaces `Right(9) Add(5) Left(9)` with `AddOffset(9, 5)`
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
fn pass_add_sub_offset(ir: &mut Ir<'_>) {
|
fn pass_add_sub_offset(ir: &mut Hir<'_>) {
|
||||||
window_pass(ir, pass_add_sub_offset, |[a, b, c]| {
|
window_pass(ir, pass_add_sub_offset, |[a, b, c]| {
|
||||||
match (a.kind(), b.kind(), c.kind()) {
|
match (a.kind(), b.kind(), c.kind()) {
|
||||||
(StmtKind::Right(r), StmtKind::Add(0, n), StmtKind::Left(l)) if r == l => {
|
(StmtKind::Right(r), StmtKind::Add(0, n), StmtKind::Left(l)) if r == l => {
|
||||||
|
|
@ -255,7 +182,7 @@ fn pass_add_sub_offset(ir: &mut Ir<'_>) {
|
||||||
|
|
||||||
/// pass that replaces `Loop([Sub(1) AddOffset(o, 1)])` with `MoveAddTo(o)`
|
/// pass that replaces `Loop([Sub(1) AddOffset(o, 1)])` with `MoveAddTo(o)`
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
fn pass_move_add_to(ir: &mut Ir<'_>) {
|
fn pass_move_add_to(ir: &mut Hir<'_>) {
|
||||||
for stmt in &mut ir.stmts {
|
for stmt in &mut ir.stmts {
|
||||||
if let Stmt {
|
if let Stmt {
|
||||||
kind: StmtKind::Loop(body),
|
kind: StmtKind::Loop(body),
|
||||||
|
|
@ -286,16 +213,16 @@ fn pass_move_add_to(ir: &mut Ir<'_>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum WindowPassAction<'ir> {
|
enum WindowPassAction<'hir> {
|
||||||
None,
|
None,
|
||||||
Merge(StmtKind<'ir>),
|
Merge(StmtKind<'hir>),
|
||||||
RemoveAll,
|
RemoveAll,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window_pass<'ir, P, F, const N: usize>(ir: &mut Ir<'ir>, pass_recur: P, action: F)
|
fn window_pass<'hir, P, F, const N: usize>(ir: &mut Hir<'hir>, pass_recur: P, action: F)
|
||||||
where
|
where
|
||||||
P: Fn(&mut Ir<'ir>),
|
P: Fn(&mut Hir<'hir>),
|
||||||
F: Fn([&Stmt<'ir>; N]) -> WindowPassAction<'ir>,
|
F: Fn([&Stmt<'hir>; N]) -> WindowPassAction<'hir>,
|
||||||
{
|
{
|
||||||
assert!(N > 0);
|
assert!(N > 0);
|
||||||
|
|
||||||
|
|
@ -2,17 +2,20 @@
|
||||||
#![deny(unsafe_op_in_unsafe_fn)]
|
#![deny(unsafe_op_in_unsafe_fn)]
|
||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
|
|
||||||
use crate::parse::ParseError;
|
use std::{
|
||||||
|
fmt::Display,
|
||||||
|
io::{Read, Write},
|
||||||
|
path::PathBuf,
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use owo_colors::OwoColorize;
|
use owo_colors::OwoColorize;
|
||||||
use std::fmt::Display;
|
|
||||||
use std::io::{Read, Write};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
pub mod codegen;
|
use crate::parse::ParseError;
|
||||||
pub mod codegen_interpreter;
|
|
||||||
pub mod opts;
|
pub mod hir;
|
||||||
|
pub mod lir;
|
||||||
pub mod parse;
|
pub mod parse;
|
||||||
|
|
||||||
#[derive(clap::Parser, Default)]
|
#[derive(clap::Parser, Default)]
|
||||||
|
|
@ -27,8 +30,8 @@ pub struct Args {
|
||||||
|
|
||||||
pub enum DumpKind {
|
pub enum DumpKind {
|
||||||
Ast,
|
Ast,
|
||||||
Ir,
|
Hir,
|
||||||
Code,
|
Lir,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for DumpKind {
|
impl FromStr for DumpKind {
|
||||||
|
|
@ -37,8 +40,8 @@ impl FromStr for DumpKind {
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
match s {
|
match s {
|
||||||
"ast" => Ok(Self::Ast),
|
"ast" => Ok(Self::Ast),
|
||||||
"ir" => Ok(Self::Ir),
|
"hir" => Ok(Self::Hir),
|
||||||
"code" => Ok(Self::Code),
|
"lir" => Ok(Self::Lir),
|
||||||
other => Err(format!("Invalid IR level: '{other}'")),
|
other => Err(format!("Invalid IR level: '{other}'")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -65,12 +68,12 @@ where
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let ir_alloc = Bump::new();
|
let hir_alloc = Bump::new();
|
||||||
|
|
||||||
let optimized_ir = opts::optimize(&ir_alloc, &parsed);
|
let optimized_hir = hir::optimized_hir(&hir_alloc, &parsed);
|
||||||
|
|
||||||
if let Some(DumpKind::Ir) = config.dump {
|
if let Some(DumpKind::Hir) = config.dump {
|
||||||
println!("{optimized_ir:#?}");
|
println!("{optimized_hir:#?}");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,27 +82,27 @@ where
|
||||||
|
|
||||||
let cg_alloc = Bump::new();
|
let cg_alloc = Bump::new();
|
||||||
|
|
||||||
let code = codegen::generate(&cg_alloc, &optimized_ir);
|
let lir = lir::generate(&cg_alloc, &optimized_hir);
|
||||||
|
|
||||||
if let Some(DumpKind::Code) = config.dump {
|
if let Some(DumpKind::Lir) = config.dump {
|
||||||
println!("{code:#?}");
|
println!("{lir:#?}");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
drop(optimized_ir);
|
drop(optimized_hir);
|
||||||
drop(ir_alloc);
|
drop(hir_alloc);
|
||||||
|
|
||||||
match config.profile {
|
match config.profile {
|
||||||
true => {
|
true => {
|
||||||
let mut code_profile_count = vec![0; code.debug().len()];
|
let mut code_profile_count = vec![0; lir.debug().len()];
|
||||||
|
|
||||||
codegen_interpreter::run(&code, stdout, stdin, |ip| unsafe {
|
lir::interpreter::run(&lir, stdout, stdin, |ip| unsafe {
|
||||||
*code_profile_count.get_unchecked_mut(ip) += 1;
|
*code_profile_count.get_unchecked_mut(ip) += 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut src_profile_count = vec![0u64; src.len()];
|
let mut src_profile_count = vec![0u64; src.len()];
|
||||||
|
|
||||||
for (stmt_span, stmt_count) in code.debug().iter().zip(&code_profile_count) {
|
for (stmt_span, stmt_count) in lir.debug().iter().zip(&code_profile_count) {
|
||||||
for i in &mut src_profile_count[stmt_span.start()..stmt_span.end()] {
|
for i in &mut src_profile_count[stmt_span.start()..stmt_span.end()] {
|
||||||
*i += stmt_count;
|
*i += stmt_count;
|
||||||
}
|
}
|
||||||
|
|
@ -112,7 +115,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false => {
|
false => {
|
||||||
codegen_interpreter::run(&code, stdout, stdin, |_| {});
|
lir::interpreter::run(&lir, stdout, stdin, |_| {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
use crate::codegen::{Code, Stmt};
|
use std::{
|
||||||
use std::io::{Read, Write};
|
io::{Read, Write},
|
||||||
use std::num::Wrapping;
|
num::Wrapping,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::lir::{Lir, Stmt};
|
||||||
|
|
||||||
const MEM_SIZE: usize = 32_000;
|
const MEM_SIZE: usize = 32_000;
|
||||||
|
|
||||||
|
|
@ -10,7 +13,7 @@ type Memory = [Wrapping<u8>; MEM_SIZE];
|
||||||
// maybe useless, but seems to give tiny wins
|
// maybe useless, but seems to give tiny wins
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct Interpreter<'c, W, R, P> {
|
struct Interpreter<'c, W, R, P> {
|
||||||
code: &'c Code<'c>,
|
code: &'c Lir<'c>,
|
||||||
profile_collector: P,
|
profile_collector: P,
|
||||||
ip: usize,
|
ip: usize,
|
||||||
ptr: usize,
|
ptr: usize,
|
||||||
|
|
@ -19,7 +22,7 @@ struct Interpreter<'c, W, R, P> {
|
||||||
stdin: R,
|
stdin: R,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run<W, R, P>(code: &Code<'_>, stdout: W, stdin: R, profile_collector: P)
|
pub fn run<W, R, P>(code: &Lir<'_>, stdout: W, stdin: R, profile_collector: P)
|
||||||
where
|
where
|
||||||
W: Write,
|
W: Write,
|
||||||
R: Read,
|
R: Read,
|
||||||
|
|
@ -35,7 +38,7 @@ where
|
||||||
profile_collector,
|
profile_collector,
|
||||||
};
|
};
|
||||||
|
|
||||||
// SAFETY: `Code` can only be produced by the `crate::codegen` module, which is trusted to not
|
// SAFETY: `Lir` can only be produced by the `crate::lir` module, which is trusted to not
|
||||||
// produce out of bounds jumps and put the `End` at the end
|
// produce out of bounds jumps and put the `End` at the end
|
||||||
unsafe {
|
unsafe {
|
||||||
interpreter.execute();
|
interpreter.execute();
|
||||||
|
|
@ -17,12 +17,18 @@
|
||||||
//! this module must not produce out of bounds jumps and always put the `End` instruction at the
|
//! this module must not produce out of bounds jumps and always put the `End` instruction at the
|
||||||
//! end
|
//! end
|
||||||
|
|
||||||
use crate::opts::{Ir, Stmt as IrStmt, StmtKind};
|
pub mod interpreter;
|
||||||
use crate::parse::Span;
|
|
||||||
use crate::BumpVec;
|
|
||||||
use bumpalo::Bump;
|
|
||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::{Debug, Formatter};
|
||||||
|
|
||||||
|
use bumpalo::Bump;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
hir::{Hir, Stmt as HirStmt, StmtKind as HirStmtKind},
|
||||||
|
parse::Span,
|
||||||
|
BumpVec,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum Stmt {
|
pub enum Stmt {
|
||||||
Add(u8),
|
Add(u8),
|
||||||
|
|
@ -43,18 +49,18 @@ pub enum Stmt {
|
||||||
const _: [(); 8] = [(); std::mem::size_of::<Stmt>()];
|
const _: [(); 8] = [(); std::mem::size_of::<Stmt>()];
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Code<'c> {
|
pub struct Lir<'c> {
|
||||||
stmts: BumpVec<'c, Stmt>,
|
stmts: BumpVec<'c, Stmt>,
|
||||||
debug: BumpVec<'c, Span>,
|
debug: BumpVec<'c, Span>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Code<'_> {
|
impl Debug for Lir<'_> {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
self.stmts.fmt(f)
|
self.stmts.fmt(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Code<'_> {
|
impl Lir<'_> {
|
||||||
pub fn stmts(&self) -> &[Stmt] {
|
pub fn stmts(&self) -> &[Stmt] {
|
||||||
&self.stmts
|
&self.stmts
|
||||||
}
|
}
|
||||||
|
|
@ -64,68 +70,68 @@ impl Code<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate<'c>(alloc: &'c Bump, ir: &Ir<'_>) -> Code<'c> {
|
pub fn generate<'c>(alloc: &'c Bump, ir: &Hir<'_>) -> Lir<'c> {
|
||||||
let stmts = Vec::new_in(alloc);
|
let stmts = Vec::new_in(alloc);
|
||||||
let debug = Vec::new_in(alloc);
|
let debug = Vec::new_in(alloc);
|
||||||
let mut code = Code { stmts, debug };
|
let mut lir = Lir { stmts, debug };
|
||||||
|
|
||||||
generate_stmts(&mut code, &ir.stmts);
|
generate_stmts(&mut lir, &ir.stmts);
|
||||||
code.stmts.push(Stmt::End);
|
lir.stmts.push(Stmt::End);
|
||||||
code.debug.push(Span::default());
|
lir.debug.push(Span::default());
|
||||||
|
|
||||||
assert_eq!(code.stmts.len(), code.debug.len());
|
assert_eq!(lir.stmts.len(), lir.debug.len());
|
||||||
|
|
||||||
code
|
lir
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_stmts<'c>(code: &mut Code<'c>, ir: &[IrStmt<'_>]) {
|
fn generate_stmts<'c>(lir: &mut Lir<'c>, ir: &[HirStmt<'_>]) {
|
||||||
for ir_stmt in ir {
|
for ir_stmt in ir {
|
||||||
ir_to_stmt(code, ir_stmt);
|
ir_to_stmt(lir, ir_stmt);
|
||||||
}
|
}
|
||||||
debug_assert_eq!(code.stmts.len(), code.debug.len());
|
debug_assert_eq!(lir.stmts.len(), lir.debug.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ir_to_stmt<'c>(code: &mut Code<'c>, ir_stmt: &IrStmt<'_>) {
|
fn ir_to_stmt<'c>(lir: &mut Lir<'c>, ir_stmt: &HirStmt<'_>) {
|
||||||
let stmt = match &ir_stmt.kind {
|
let stmt = match &ir_stmt.kind {
|
||||||
StmtKind::Add(0, n) => Stmt::Add(*n),
|
HirStmtKind::Add(0, n) => Stmt::Add(*n),
|
||||||
StmtKind::Sub(0, n) => Stmt::Sub(*n),
|
HirStmtKind::Sub(0, n) => Stmt::Sub(*n),
|
||||||
StmtKind::Add(offset, n) => Stmt::AddOffset {
|
HirStmtKind::Add(offset, n) => Stmt::AddOffset {
|
||||||
offset: *offset,
|
offset: *offset,
|
||||||
n: *n,
|
n: *n,
|
||||||
},
|
},
|
||||||
StmtKind::Sub(offset, n) => Stmt::SubOffset {
|
HirStmtKind::Sub(offset, n) => Stmt::SubOffset {
|
||||||
offset: *offset,
|
offset: *offset,
|
||||||
n: *n,
|
n: *n,
|
||||||
},
|
},
|
||||||
StmtKind::MoveAddTo { offset } => Stmt::MoveAddTo { offset: *offset },
|
HirStmtKind::MoveAddTo { offset } => Stmt::MoveAddTo { offset: *offset },
|
||||||
StmtKind::Right(n) => Stmt::Right(u32::try_from(*n).unwrap()),
|
HirStmtKind::Right(n) => Stmt::Right(u32::try_from(*n).unwrap()),
|
||||||
StmtKind::Left(n) => Stmt::Left(u32::try_from(*n).unwrap()),
|
HirStmtKind::Left(n) => Stmt::Left(u32::try_from(*n).unwrap()),
|
||||||
StmtKind::Out => Stmt::Out,
|
HirStmtKind::Out => Stmt::Out,
|
||||||
StmtKind::In => Stmt::In,
|
HirStmtKind::In => Stmt::In,
|
||||||
StmtKind::SetN(n) => Stmt::SetN(*n),
|
HirStmtKind::SetN(n) => Stmt::SetN(*n),
|
||||||
StmtKind::Loop(instr) => {
|
HirStmtKind::Loop(instr) => {
|
||||||
let skip_jmp_idx = code.stmts.len();
|
let skip_jmp_idx = lir.stmts.len();
|
||||||
code.stmts.push(Stmt::JmpIfZero(0)); // placeholder
|
lir.stmts.push(Stmt::JmpIfZero(0)); // placeholder
|
||||||
code.debug.push(ir_stmt.span);
|
lir.debug.push(ir_stmt.span);
|
||||||
|
|
||||||
// compile the loop body now
|
// compile the loop body now
|
||||||
generate_stmts(code, &instr.stmts);
|
generate_stmts(lir, &instr.stmts);
|
||||||
// if the loop body is empty, we jmp to ourselves, which is an infinite loop - as expected
|
// if the loop body is empty, we jmp to ourselves, which is an infinite loop - as expected
|
||||||
let first_loop_body_idx = skip_jmp_idx + 1;
|
let first_loop_body_idx = skip_jmp_idx + 1;
|
||||||
code.stmts
|
lir.stmts
|
||||||
.push(Stmt::JmpIfNonZero(first_loop_body_idx.try_into().unwrap()));
|
.push(Stmt::JmpIfNonZero(first_loop_body_idx.try_into().unwrap()));
|
||||||
code.debug.push(ir_stmt.span);
|
lir.debug.push(ir_stmt.span);
|
||||||
|
|
||||||
// there will always at least be an `End` instruction after the loop
|
// there will always at least be an `End` instruction after the loop
|
||||||
let after_loop_idx = code.stmts.len();
|
let after_loop_idx = lir.stmts.len();
|
||||||
|
|
||||||
// fix the placeholder with the actual index
|
// fix the placeholder with the actual index
|
||||||
code.stmts[skip_jmp_idx] = Stmt::JmpIfZero(after_loop_idx.try_into().unwrap());
|
lir.stmts[skip_jmp_idx] = Stmt::JmpIfZero(after_loop_idx.try_into().unwrap());
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
code.stmts.push(stmt);
|
lir.stmts.push(stmt);
|
||||||
code.debug.push(ir_stmt.span);
|
lir.debug.push(ir_stmt.span);
|
||||||
}
|
}
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
#![feature(allocator_api, let_else)]
|
#![feature(allocator_api, let_else)]
|
||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
|
|
||||||
|
use std::{fs, io, process};
|
||||||
|
|
||||||
use brainfuck::Args;
|
use brainfuck::Args;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use std::{fs, io, process};
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let stdout = io::stdout();
|
let stdout = io::stdout();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
|
use std::{cmp, fmt::Debug};
|
||||||
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use std::cmp;
|
|
||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||||
pub struct Span {
|
pub struct Span {
|
||||||
|
|
|
||||||
|
|
@ -1 +1,5 @@
|
||||||
[->>>>>>>>>+<<<<<<<<<]
|
[<+++>-
|
||||||
|
>>>>>
|
||||||
|
+++[->+++++<]>[-]<
|
||||||
|
<<<<<
|
||||||
|
]
|
||||||
Loading…
Add table
Add a link
Reference in a new issue