Skip to content

Instantly share code, notes, and snippets.

@Tosainu
Last active August 30, 2019 12:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Tosainu/367e35aed49a2590d4f78fbca9e805c9 to your computer and use it in GitHub Desktop.
Save Tosainu/367e35aed49a2590d4f78fbca9e805c9 to your computer and use it in GitHub Desktop.
Brainf**k compiler (bf -> LLVM IR) and interpreter.
use std::collections::VecDeque;
use std::io::Read;
struct Emitter {
variable_idx: u32,
label_idx: u32,
loop_stack: VecDeque<u32>,
}
impl Emitter {
fn new() -> Emitter {
Emitter {
variable_idx: 1,
label_idx: 1,
loop_stack: VecDeque::new(),
}
}
fn emit_move_ptr(&mut self, n: i32) {
println!(
r#"
; emit_move_ptr({2})
%{0} = load i32*, i32** %ptr, align 8
%{1} = getelementptr inbounds i32, i32* %{0}, i32 {2}
store i32* %{1}, i32** %ptr, align 8"#,
self.variable_idx,
self.variable_idx + 1,
n
);
self.variable_idx += 2;
}
fn emit_add(&mut self, n: i32) {
println!(
r#"
; emit_add({3})
%{0} = load i32*, i32** %ptr, align 8
%{1} = load i32, i32* %{0}, align 4
%{2} = add nsw i32 %{1}, {3}
store i32 %{2}, i32* %{0}, align 4"#,
self.variable_idx,
self.variable_idx + 1,
self.variable_idx + 2,
n
);
self.variable_idx += 3;
}
fn emit_putchar(&mut self) {
println!(
r#"
; emit_putchar()
%{0} = load i32*, i32** %ptr, align 8
%{1} = load i32, i32* %{0}, align 4
%{2} = call i32 @putchar(i32 %{1})"#,
self.variable_idx,
self.variable_idx + 1,
self.variable_idx + 2,
);
self.variable_idx += 3;
}
fn emit_getchar(&mut self) {
println!(
r#"
; emit_getchar()
%{0} = load i32*, i32** %ptr, align 8
%{1} = call i32 @getchar()
store i32 %{1}, i32* %{0}, align 4"#,
self.variable_idx,
self.variable_idx + 1,
);
self.variable_idx += 2;
}
fn emit_loop_begin(&mut self) {
println!(
r#"
; emit_loop_begin()
br label %loop{3}_cond
loop{3}_cond:
%{0} = load i32*, i32** %ptr, align 8
%{1} = load i32, i32* %{0}, align 4
%{2} = icmp ne i32 %{1}, 0
br i1 %{2}, label %loop{3}_body, label %loop{3}_end
loop{3}_body:"#,
self.variable_idx,
self.variable_idx + 1,
self.variable_idx + 2,
self.label_idx
);
self.loop_stack.push_back(self.label_idx);
self.variable_idx += 3;
self.label_idx += 1;
}
fn emit_loop_end(&mut self) {
if let Some(n) = self.loop_stack.pop_back() {
println!(
r#"
; emit_loop_end()
br label %loop{0}_cond
loop{0}_end:"#,
n
);
}
}
fn emit_header(&self) {
println!("; emit_header()");
println!("define i32 @main() {{");
println!(" %heap_i8 = call i8* @calloc(i64 30000, i64 4)");
println!(" %heap_i32 = bitcast i8* %heap_i8 to i32*");
println!(" %heap = alloca i32*, align 8");
println!(" %ptr = alloca i32*, align 8");
println!(" store i32* %heap_i32, i32** %heap, align 8");
println!(" store i32* %heap_i32, i32** %ptr, align 8");
}
fn emit_footer(&self) {
println!();
println!(" ; emit_footer()");
println!(" %{} = load i32*, i32** %heap, align 8", self.variable_idx);
println!(
" %{} = bitcast i32* %{} to i8*",
self.variable_idx + 1,
self.variable_idx
);
println!(" call void @free(i8* %{})", self.variable_idx + 1);
println!(" ret i32 0");
println!("}}");
println!("declare i8* @calloc(i64, i64)");
println!("declare void @free(i8*)");
println!("declare i32 @getchar()");
println!("declare i32 @putchar(i32)");
}
}
fn main() -> std::io::Result<()> {
let mut src = Vec::new();
std::io::stdin().read_to_end(&mut src)?;
let mut e = Emitter::new();
e.emit_header();
for c in src.iter() {
match c {
b'>' => e.emit_move_ptr(1),
b'<' => e.emit_move_ptr(-1),
b'+' => e.emit_add(1),
b'-' => e.emit_add(-1),
b'.' => e.emit_putchar(),
b',' => e.emit_getchar(),
b'[' => e.emit_loop_begin(),
b']' => e.emit_loop_end(),
_ => (),
}
}
e.emit_footer();
Ok(())
}
use std::collections::VecDeque;
use std::fs::File;
use std::io::{Read, Write};
fn getchar() -> u8 {
std::io::stdin()
.bytes()
.next()
.and_then(|r| r.ok())
.unwrap()
}
fn putchar(c: u8) {
std::io::stdout().write_all(&[c]).unwrap();
}
fn main() {
let src = std::env::args().nth(1).and_then(|filename| {
let mut file = File::open(filename).ok()?;
let mut buf = Vec::new();
file.read_to_end(&mut buf).ok()?;
Some(buf)
}).unwrap_or(
b"++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.".to_vec()
);
let mut stack = VecDeque::new();
let mut heap = vec![0u32; 30000];
let mut ptr = 0;
let mut pc = 0;
let mut eval = true;
while pc < src.len() {
let mut pc_next = None;
match (eval, src[pc]) {
(true, b'>') => ptr += 1,
(true, b'<') => ptr -= 1,
(true, b'+') => heap[ptr] += 1,
(true, b'-') => heap[ptr] -= 1,
(true, b'.') => putchar(heap[ptr] as u8),
(true, b',') => heap[ptr] = u32::from(getchar()),
(_, b'[') => {
stack.push_back((eval, pc));
eval = eval && heap[ptr] > 0;
}
(_, b']') => {
if let Some((e, p)) = stack.pop_back() {
if eval {
pc_next = Some(p);
} else {
eval = e;
}
}
}
_ => (),
}
pc = pc_next.unwrap_or(pc + 1);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment