Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
288 changes: 166 additions & 122 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion librbf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
name = "librbf"
version = "0.1.0"
authors = ["Luka Dornhecker <luka.dornhecker@gmail.com>"]
edition = "2021"

[dependencies]
combine = "3.4"
chumsky = "0.8.0"
ariadne = "0.1.5"
dynasm = "1.0"
dynasmrt = "1.0"
libc = "0.2.48"
5 changes: 4 additions & 1 deletion librbf/src/ast.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
/// A Vector of [`Instruction`](enum.Instruction.html) representing a Brainfuck program.
pub type Program = Vec<Instruction>;
pub type Program = Vec<Spanned<Instruction>>;

pub type Span = std::ops::Range<usize>;
pub type Spanned<T> = (T, Span);

/// An enum representing all brainfuck instructions.
#[derive(Clone, Debug, PartialEq)]
Expand Down
24 changes: 13 additions & 11 deletions librbf/src/jit.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use ast::{Instruction::*, Program};
use dynasm::dynasm;
use dynasmrt::{DynasmApi, DynasmLabelApi, ExecutableBuffer};
use std::io::Read;
use std::io::{self, Write};
use std::mem;

use dynasm::dynasm;
use dynasmrt::{DynasmApi, DynasmLabelApi, ExecutableBuffer};

use crate::ast::{Instruction::*, Program};

extern "C" fn putchar(c: u8) {
print!("{}", c as char);
}
Expand Down Expand Up @@ -100,43 +102,43 @@ impl Jit {
fn gen(&mut self, program: &Program) {
for ins in program.iter() {
match ins {
&Move(i) => {
&(Move(i), _) => {
dynasm!(self.ops
; add rbx, i as _
);
}
&Add(i) => {
&(Add(i), _) => {
dynasm!(self.ops
; add BYTE [rbx], i as _
);
}
Write => {
(Write, _) => {
dynasm!(self.ops
; movzx rdi, [rbx]
; mov rax, QWORD putchar as _
; call rax
);
}
Read => {
(Read, _) => {
dynasm!(self.ops
; mov rax, QWORD getchar as _
; call rax
; mov [rbx], al
);
}
Set(i) => {
(Set(i), _) => {
dynasm!(self.ops
; mov BYTE [rbx], (i % 0xFF) as _
);
}
&Mul(offset, mul) => {
&(Mul(offset, mul), _) => {
dynasm!(self.ops
; mov al, mul as _
; mul BYTE [rbx]
; add [rbx + offset as _], al
);
}
&Scan(i) => {
&(Scan(i), _) => {
let move_label = self.ops.new_dynamic_label();
let rest_label = self.ops.new_dynamic_label();
dynasm!(self.ops
Expand All @@ -149,7 +151,7 @@ impl Jit {
; =>rest_label
);
}
Loop(body) => {
(Loop(body), _) => {
let body_label = self.ops.new_dynamic_label();
let rest_label = self.ops.new_dynamic_label();
dynasm!(self.ops
Expand Down
4 changes: 2 additions & 2 deletions librbf/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
extern crate dynasm;
extern crate dynasmrt;

#[macro_use]
extern crate combine;
extern crate libc;

mod ast;
mod jit;
mod optimizer;
mod parser;

pub use ast::*;
pub use jit::Jit;
pub use optimizer::optimize;
pub use parser::parse;
107 changes: 107 additions & 0 deletions librbf/src/optimizer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use std::collections::HashMap;

use crate::ast::{Instruction::*, Program, Spanned};

fn opt(program: Program) -> Program {
let mut iter = program.iter().peekable();
let mut prog: Program = vec![];

while let Some(current) = iter.next() {
match (current, iter.peek()) {
((Add(0), _), _) => (),
((Move(0), _), _) => (),

((Add(a), span_a), Some((Add(b), span_b))) => {
iter.next();
prog.push((Add(a + b), span_a.start..span_b.end));
}
((Move(a), span_a), Some((Move(b), span_b))) => {
iter.next();
prog.push((Move(a + b), span_a.start..span_b.end));
}

((Loop(body), span), _) => {
if let Some(optimized_body) = optimize_loop((body.clone(), span.clone())) {
prog.extend(optimized_body);
}
}
((Set(a), span_set), Some((Add(b), span_add))) => {
iter.next();
prog.push((Set(a + b), span_set.start..span_add.end));
}

((Add(_), _), Some(&set @ (Set(_), _))) | ((Set(_), _), Some(&set @ (Set(_), _))) => {
iter.next();
prog.push(set.clone());
}

((Set(0), _), Some((Loop(_), _))) => {
iter.next();
prog.push(current.clone())
}
((Set(0), _), Some((Mul(_, _), _))) => {
prog.push(current.clone());
while let Some((Mul(_, _), _)) = iter.next() {}
}
_ => prog.push(current.clone()),
}
}
prog
}

fn optimize_loop(program: Spanned<Program>) -> Option<Program> {
match &program.0[..] {
[] => None,
[(Set(0), _)] => Some(vec![(Set(0), program.1)]),
[(Add(-1), _)] => Some(vec![(Set(0), program.1)]),
[(Move(n), _)] => Some(vec![(Scan(*n), program.1)]),
_ => Some(optimize_mul((optimize(program.0), program.1))),
}
}

fn optimize_mul(program: Spanned<Program>) -> Program {
let mut muls = HashMap::new();
let mut offset = 0;
let mut is_mul = true;

for ins in program.0.iter() {
match ins {
(Add(i), _) => *muls.entry(offset).or_insert(0) += i,
(Move(i), _) => offset += i,
_ => is_mul = false,
}
if is_mul == false {
break;
}
}

if !is_mul || offset != 0 || muls.get(&0) != Some(&-1) {
return vec![(Loop(program.0), program.1)];
}

let mut result: Vec<_> = muls
.iter()
.map(|(&k, &v)| {
if k == 0 {
None
} else {
Some((Mul(k, v), program.1.clone()))
}
})
.filter_map(|x| x)
.collect();

result.push((Set(0), program.1));
result
}

pub fn optimize(program: Program) -> Program {
let mut opt_a = opt(program);
let mut opt_b = opt(opt_a.clone());

while opt_a != opt_b {
opt_a = opt(opt_b.clone());
opt_b = opt(opt_a.clone());
}
opt_b
}
Loading