├── programs ├── cat.bf ├── 1-to-5.bf ├── factor.bf └── mandelbrot.bf ├── .gitignore ├── object-example ├── write_lib.c ├── write.rs ├── Cargo.toml ├── write.as ├── Makefile ├── hello.as └── src │ └── main.rs ├── Cargo.toml ├── optimized-jit ├── Cargo.toml └── src │ └── main.rs ├── singlepass-jit ├── Cargo.toml └── src │ └── main.rs ├── interpreter ├── Cargo.toml └── src │ └── main.rs ├── optimized ├── Cargo.toml └── src │ └── main.rs ├── singlepass-compiler ├── Cargo.toml ├── Makefile ├── bf_lib.rs └── src │ └── main.rs ├── cranelift-jit ├── Cargo.toml └── src │ └── main.rs ├── cranelift-example ├── Cargo.toml └── src │ └── main.rs ├── README.md ├── mean.py ├── plot.py ├── bench-all.py ├── benchmark.adoc └── Cargo.lock /programs/cat.bf: -------------------------------------------------------------------------------- 1 | +[,.] 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | times 3 | bench_programs 4 | plots 5 | -------------------------------------------------------------------------------- /object-example/write_lib.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int my_write(char* x, int len) { 4 | printf("len: %d", len); 5 | printf(x); 6 | } 7 | -------------------------------------------------------------------------------- /programs/1-to-5.bf: -------------------------------------------------------------------------------- 1 | set cell 0 to 48 ('0' in ASCII) 2 | ++++++++++ ++++++++++ ++++++++++ ++++++++++ ++++++++ 3 | set cell 1 to 5 4 | >+++++ 5 | increment cell 0; print it; decrement cell 1; loop while cell 1 is not 0 6 | [<+.>-] 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "interpreter", 4 | "optimized", 5 | "singlepass-jit", 6 | "optimized-jit", 7 | "cranelift-jit", 8 | "cranelift-example", 9 | "object-example", 10 | "singlepass-compiler", 11 | ] 12 | -------------------------------------------------------------------------------- /optimized-jit/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bf-optimized-jit" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | dynasmrt = "1.2.3" 10 | -------------------------------------------------------------------------------- /singlepass-jit/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bf-singlepass-jit" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | dynasmrt = "1.2.3" 10 | -------------------------------------------------------------------------------- /object-example/write.rs: -------------------------------------------------------------------------------- 1 | #[no_mangle] 2 | pub extern "sysv64" fn my_write(address: *const u8, len: usize) { 3 | let string = unsafe { std::slice::from_raw_parts(address, len) }; 4 | let string = std::str::from_utf8(string).unwrap(); 5 | print!("{}", string); 6 | } 7 | -------------------------------------------------------------------------------- /interpreter/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bf-interpreter" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [features] 9 | profile = [] 10 | 11 | [dependencies] 12 | -------------------------------------------------------------------------------- /optimized/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bf-optimized" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [features] 9 | 10 | profile = [] 11 | 12 | [dependencies] 13 | -------------------------------------------------------------------------------- /singlepass-compiler/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "singlepass-compiler" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | dynasmrt = "1.2.3" 10 | object = { version = "0.30.0", features = ["write"] } 11 | -------------------------------------------------------------------------------- /cranelift-jit/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bf-cranelift-jit" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | cranelift = "0.89.2" 10 | libc = "0.2.137" 11 | memmap2 = "0.5.8" 12 | target-lexicon = "0.12.5" 13 | -------------------------------------------------------------------------------- /object-example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "object-example" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | dynasmrt = "1.2.3" 10 | memmap2 = "0.5.8" 11 | object = { version = "0.30.0", features = ["write"] } 12 | -------------------------------------------------------------------------------- /object-example/write.as: -------------------------------------------------------------------------------- 1 | section .text 2 | global my_write 3 | 4 | my_write: 5 | push rbp 6 | mov rbp, rsp 7 | mov rdx, rsi ; length of string to write 8 | mov rsi, rdi ; string to write 9 | mov rax, 1 ; 'write' system call = 4 10 | mov rdi, 1 ; file descriptor 1 = STDOUT 11 | syscall ; call the kernel 12 | pop rbp 13 | ret 14 | -------------------------------------------------------------------------------- /cranelift-example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cranelift-example" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | cranelift = "0.89.2" 10 | cranelift-codegen = { version = "0.89.2" } 11 | memmap2 = "0.5.8" 12 | target-lexicon = "0.12.5" 13 | -------------------------------------------------------------------------------- /object-example/Makefile: -------------------------------------------------------------------------------- 1 | 2 | rust: 3 | cargo run -p object-example -- obj 4 | rustc --crate-type staticlib -Cpanic=abort -Clto -Cdebuginfo=0 -Coverflow-checks=no write_lib.rs 5 | gcc -o out -nostartfiles out.elf libwrite_lib.a -pthread -ldl 6 | 7 | c: 8 | nasm -o hello.o hello.as -f elf64 9 | gcc -c -o write_lib.o write_lib.c 10 | gcc -o out -nostartfiles hello.o write_lib.o 11 | 12 | clean: 13 | rm write_lib.o out out.elf libwrite_lib.a hello.o 14 | -------------------------------------------------------------------------------- /object-example/hello.as: -------------------------------------------------------------------------------- 1 | section .text 2 | global _start 3 | extern my_write 4 | 5 | _start: 6 | lea rdi, [rel hello] ; string to write 7 | mov rsi, helloLen ; length of string to write 8 | call my_write 9 | 10 | ; Terminate program 11 | mov rax, 60 ; 'exit' system call = 60 12 | mov rdi, 0 ; exit with error code 0 13 | syscall ; call the kernel 14 | hello: db 'Hello world!',10 15 | helloLen: equ $-hello 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Interpreted, Optimized, JITed and Compiled Brainfuck implementations 2 | 3 | This repo contains a series of [brainfuck] implementations based on [Eli 4 | Bendersky blog post series about Brainfuck and JIT compilation][eli], but 5 | rewritten in Rust. 6 | 7 | These implementations were developed as I was writing my [Compiling Brainfuck 8 | Series][my_series]. 9 | 10 | 11 | [eli]: https://eli.thegreenplace.net/2017/adventures-in-jit-compilation-part-1-an-interpreter. 12 | [brainfuck]: https://esolangs.org/wiki/Brainfuck 13 | [my_series]: https://rodrigodd.github.io/2022/10/21/bf_compiler-part1.html 14 | 15 | ## Benchmarks 16 | 17 | Benchmarks result can be found in [benchmark.adoc](benchmark.adoc). 18 | -------------------------------------------------------------------------------- /singlepass-compiler/Makefile: -------------------------------------------------------------------------------- 1 | 2 | build: 3 | rustc --crate-type staticlib bf_lib.rs -o bf_lib.a 4 | cargo run -p singlepass-compiler -- ../programs/mandelbrot.bf -o 5 | gcc -o mandelbrot -nostartfiles mandelbrot.o bf_lib.a -pthread -ldl 6 | 7 | windows: 8 | rustc --crate-type staticlib bf_lib.rs --target=x86_64-pc-windows-msvc -Copt-level=2 -Clto -Cpanic=abort 9 | cargo run -p singlepass-compiler -- ../programs/factor.bf -o 10 | link /subsystem:console /entry:WinMain advapi32.lib advapi32.lib userenv.lib kernel32.lib kernel32.lib ws2_32.lib bcrypt.lib msvcrt.lib vcruntime.lib factor.o bf_lib.lib 11 | 12 | clean: 13 | rm -f bf_lib.a bf_lib.lib factor.o factor factor.exe mandelbrot.o mandelbrot mandelbrot.exe 14 | -------------------------------------------------------------------------------- /singlepass-compiler/bf_lib.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Read, Write}; 2 | 3 | #[no_mangle] 4 | pub extern "sysv64" fn bf_write(value: u8) { 5 | // Writing a non-UTF-8 byte sequence on Windows error out. 6 | if cfg!(target_os = "windows") && value >= 128 { 7 | return; 8 | } 9 | 10 | let mut stdout = std::io::stdout().lock(); 11 | 12 | let result = stdout.write_all(&[value]).and_then(|_| stdout.flush()); 13 | 14 | if let Err(err) = result { 15 | eprintln!("IO error: {}", err); 16 | std::process::exit(1); 17 | } 18 | } 19 | 20 | #[no_mangle] 21 | pub unsafe extern "sysv64" fn bf_read(buf: *mut u8) { 22 | let mut stdin = std::io::stdin().lock(); 23 | loop { 24 | let mut value = 0; 25 | let err = stdin.read_exact(std::slice::from_mut(&mut value)); 26 | 27 | if let Err(err) = err { 28 | if err.kind() != std::io::ErrorKind::UnexpectedEof { 29 | eprintln!("IO error: {}", err); 30 | std::process::exit(1); 31 | } 32 | value = 0; 33 | } 34 | 35 | // ignore CR from Window's CRLF 36 | if cfg!(target_os = "windows") && value == b'\r' { 37 | continue; 38 | } 39 | 40 | *buf = value; 41 | break; 42 | } 43 | } 44 | 45 | #[no_mangle] 46 | pub unsafe extern "sysv64" fn bf_exit() { 47 | std::process::exit(0); 48 | } 49 | -------------------------------------------------------------------------------- /mean.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import numpy as np 3 | import csv 4 | import io 5 | import math 6 | 7 | 8 | with open(f"times/factor.csv", newline='') as csv_file: 9 | header = list(csv.reader(csv_file))[0] 10 | 11 | # tables will be a list of "commit, factor_time, mandelbrot_time, factor_impr, mandelbrot_impr" 12 | table = [[commit, [0,0], [0,0], [0,0], [0,0]] for commit in header[1:]] 13 | 14 | # compute each mean and standart deviation of the mean 15 | for i, name in enumerate(["factor", "mandelbrot"]): 16 | with open(f"times/{name}.csv", newline='') as csv_file: 17 | times = list(csv.reader(csv_file)) 18 | times = np.array(times[1:]) # remove header 19 | times = times.astype(float) 20 | 21 | # each collumn is a commit 22 | for col in range(1,len(times[0])): 23 | values = times[:, col] 24 | mean = np.mean(values) 25 | stderr = np.std(values, ddof=1) / math.sqrt(len(values)) 26 | 27 | table[col-1][i+1] = [mean, stderr] 28 | 29 | # because of the way that I collect the data, calculing the mean of the 30 | # improvements, is better than the improvent of the mean 31 | for col in range(2,len(times[0])): 32 | prev = times[:, col - 1] 33 | curr = times[:, col] 34 | 35 | ratio = curr/prev 36 | mean = np.mean(ratio) 37 | stderr = np.std(ratio, ddof=1) / math.sqrt(len(ratio)) 38 | 39 | # convert to relative gain 40 | impr = (mean - 1) * 100.0 41 | impr_err = stderr * 100.0 42 | 43 | table[col-1][i+3] = [impr, impr_err] 44 | 45 | # transform [mean, err] to "mean±err" 46 | for i in range(0, len(table)): 47 | print(table[i]) 48 | for j in [1,2,3,4]: 49 | mean = table[i][j][0] 50 | std = table[i][j][1] 51 | p = 0 52 | if std > 0: 53 | p = math.ceil(-math.log10(std)) + 1 54 | if p > 16: 55 | p = 16 56 | table[i][j] = f"{mean:.{p}f}±{std:.{p}f}" 57 | 58 | table.insert(0, ['commit','factor.bf','mandelbrot.bf','delta','delta']) 59 | with open('times/benchmark.csv', 'w') as f: 60 | csv.writer(f).writerows(table) 61 | 62 | -------------------------------------------------------------------------------- /plot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import matplotlib.pyplot as plt 3 | import matplotlib as mpl 4 | import numpy as np 5 | import csv 6 | import os 7 | 8 | sets = [ 9 | [1, 2], 10 | [1, 2, 3], 11 | [1, 2, 3, 4], 12 | [1, 2, 4, 5], 13 | [1, 2, 4, 5, 6], 14 | [1, 2, 4, 5, 6, 7], 15 | [1, 2, 4, 7], 16 | [1,2,3,4,5,6,7], 17 | 18 | [1, 7, 8], 19 | [7, 8, 9], 20 | [7, 9, 10], 21 | [1, 2, 4, 7, 9, 10], 22 | 23 | [9, 10, 11], 24 | [9, 10, 11, 12], 25 | [9, 10, 11, 12, 13], 26 | [1, 2, 4, 7, 9, 10, 13], 27 | 28 | [1,2,3,4,5,6,7,8,9,10,11,12,13], 29 | ] 30 | 31 | plt.rcParams['svg.fonttype'] = 'none' 32 | plt.rcParams["font.family"] = "Verdana" 33 | plt.rcParams['svg.hashsalt'] = '' 34 | mpl.use("SVG") 35 | 36 | with plt.style.context('ggplot'), open('times/benchmark.csv', newline='') as csvfile: 37 | 38 | table = list(csv.reader(csvfile)) 39 | table = np.array(table) 40 | 41 | # swap factor and mandel 42 | table[:, [2, 1]] = table[:, [1, 2]] 43 | 44 | labels = [table[0][1], table[0][2]] 45 | 46 | 47 | for index, choosed in enumerate(sets): 48 | bars = [table[i] for i in choosed] 49 | 50 | x = np.arange(len(labels)) # the label locations 51 | width = 0.9 # the width of the bars 52 | 53 | fig, ax = plt.subplots() 54 | 55 | rects = [] 56 | for i, (label, factor, mandel, _, _) in enumerate(bars): 57 | factor = factor.split("±")[0] 58 | mandel = mandel.split("±")[0] 59 | bar = [float(factor), float(mandel)] 60 | w = width/len(bars) 61 | dx = w*i 62 | rects.append(ax.bar(x - width/2 + dx, bar, w, label=label, color=f'C{choosed[i]-1}')) 63 | 64 | # Add some text for labels, title and custom x-axis tick labels, etc. 65 | ax.set_ylabel('seconds') 66 | # ax.set_title('Execution time by change') 67 | ax.set_xticks(x, labels) 68 | ax.legend() 69 | 70 | for rect in rects: 71 | ax.bar_label(rect, padding=3, fmt='%.3g') 72 | 73 | fig.tight_layout() 74 | 75 | if not os.path.exists('plots'): 76 | os.mkdir('plots') 77 | plt.savefig(f'plots/plot{index}.svg', metadata={'Date': None}) 78 | # plt.show() 79 | -------------------------------------------------------------------------------- /cranelift-example/src/main.rs: -------------------------------------------------------------------------------- 1 | use cranelift::{ 2 | codegen::{ 3 | ir::{types::I32, AbiParam, Function, InstBuilder, Signature, UserFuncName}, 4 | isa::{self, CallConv}, 5 | settings, verify_function, Context, 6 | }, 7 | frontend::{FunctionBuilder, FunctionBuilderContext}, 8 | prelude::Configurable, 9 | }; 10 | use target_lexicon::Triple; 11 | 12 | fn main() { 13 | let mut builder = settings::builder(); 14 | builder.set("opt_level", "speed").unwrap(); 15 | let flags = settings::Flags::new(builder); 16 | 17 | let isa = match isa::lookup(Triple::host()) { 18 | Err(err) => panic!("Error looking up target: {}", err), 19 | Ok(isa_builder) => isa_builder.finish(flags).unwrap(), 20 | }; 21 | 22 | let mut sig = Signature::new(CallConv::SystemV); 23 | sig.params.push(AbiParam::new(I32)); 24 | sig.returns.push(AbiParam::new(I32)); 25 | 26 | let mut func = Function::with_name_signature(UserFuncName::default(), sig); 27 | 28 | let mut func_ctx = FunctionBuilderContext::new(); 29 | let mut builder = FunctionBuilder::new(&mut func, &mut func_ctx); 30 | 31 | let block = builder.create_block(); 32 | builder.seal_block(block); 33 | 34 | builder.append_block_params_for_function_params(block); 35 | builder.switch_to_block(block); 36 | 37 | let arg = builder.block_params(block)[0]; 38 | let one = builder.ins().iconst(I32, 1); 39 | let plus_one = builder.ins().iadd(arg, one); 40 | let one = builder.ins().iconst(I32, 1); 41 | let plus_two = builder.ins().iadd(plus_one, one); 42 | builder.ins().return_(&[plus_two]); 43 | 44 | builder.finalize(); 45 | 46 | verify_function(&func, &*isa).unwrap(); 47 | 48 | println!("{}", func.display()); 49 | 50 | let mut ctx = Context::for_function(func); 51 | ctx.set_disasm(true); 52 | let code = ctx.compile(&*isa).unwrap(); 53 | 54 | println!("{}", code.disasm.as_ref().unwrap()); 55 | std::fs::write("dump.bin", code.code_buffer()).unwrap(); 56 | 57 | println!("{}", ctx.func.display()); 58 | 59 | // let mut buffer = memmap2::MmapOptions::new() 60 | // .len(code.code_buffer().len()) 61 | // .map_anon() 62 | // .unwrap(); 63 | 64 | // buffer.copy_from_slice(code.code_buffer()); 65 | 66 | // let buffer = buffer.make_exec().unwrap(); 67 | 68 | // let x = unsafe { 69 | // let code_fn: unsafe extern "sysv64" fn(usize) -> usize = 70 | // std::mem::transmute(buffer.as_ptr()); 71 | 72 | // code_fn(1) 73 | // }; 74 | 75 | // println!("out: {}", x); 76 | } 77 | -------------------------------------------------------------------------------- /bench-all.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | import sys 4 | import subprocess 5 | import re 6 | 7 | steps = [ 8 | ("bf-optimized", "basic", "689bc2a"), 9 | ("bf-optimized", "precomputejumps", "0be6f60"), 10 | ("bf-optimized", "Add", "1ed437e"), 11 | ("bf-optimized", "Move", "cbd8ba5"), 12 | ("bf-optimized", "Clear", "bb4ff06"), 13 | ("bf-optimized", "AddTo", "81d659a"), 14 | ("bf-optimized", "MoveUntil", "d6b2b23"), 15 | 16 | ("bf-singlepass-jit", "singlepass-jit", "bc3e2aa"), 17 | ("bf-singlepass-jit", "dynasm-jit", "f5a15d4"), 18 | ("bf-optimized-jit", "optimized-jit", "872c0c8"), 19 | 20 | ("bf-cranelift-jit", "cranelift-jit", "6ed28d7"), 21 | ("bf-cranelift-jit", "repeat-cranelift-jit", "9d6664f"), 22 | ("bf-cranelift-jit", "optimized-cranelift-jit", "eaf72a5"), 23 | ] 24 | 25 | p = re.compile(b"real\t(\d+)m(\d+.\d+)s") 26 | 27 | def time(command): 28 | out = subprocess.run(['bash', '-c', 29 | f"time ( {command} ) "], 30 | stderr=subprocess.PIPE) 31 | 32 | m = p.search(out.stderr) 33 | if m == None: 34 | print("didn't found real time in output of " + name) 35 | print(out.stderr) 36 | exit(1) 37 | min, secs = m.groups() 38 | 39 | t = float(min) * 60.0 + float(secs) 40 | 41 | return t 42 | 43 | def compile(): 44 | # compile each commit 45 | os.system('rm -rf bench_programs') 46 | os.system('mkdir bench_programs') 47 | for package, name, commit in steps: 48 | if os.system(f'git checkout {commit}') != 0: 49 | raise('git checkout failed') 50 | os.system(f'cargo build -p {package} --release') 51 | os.system(f'mv ./target/release/{package} ./bench_programs/{name}') 52 | os.system("git checkout master") 53 | 54 | def run(): 55 | programs = [ 56 | ('mandelbrot','./bench_programs/{name} ./programs/mandelbrot.bf'), 57 | ('factor', 'echo 179424691 | ./bench_programs/{name} ./programs/factor.bf'), 58 | ] 59 | 60 | 61 | os.system('mkdir times -p') 62 | for name, command in programs: 63 | csv_file = open(f'times/{name}.csv', 'w') 64 | 65 | # header 66 | header = "interation" 67 | for _, name, _ in steps: 68 | header = header + "," + name 69 | 70 | csv_file.write(header + '\n') 71 | csv_file.flush() 72 | 73 | # data 74 | for interation in range(0,20): 75 | print(f'interation {interation}') 76 | 77 | text = f"{interation}" 78 | for _, name, commit in steps: 79 | print(f'running for {name}') 80 | 81 | t = time(command.format(name = name)) 82 | print(f'{interation:2} {name}: {t}') 83 | 84 | text = text + "," + str(t) 85 | 86 | csv_file.write(text + '\n') 87 | csv_file.flush() 88 | 89 | csv_file.close() 90 | 91 | if __name__ == "__main__": 92 | if len(sys.argv) != 2: 93 | print('expected 1 argument') 94 | exit(1) 95 | 96 | arg = sys.argv[1] 97 | if arg == '-c' or arg == '--compile': 98 | compile() 99 | elif arg == '-r' or arg == '--run': 100 | run() 101 | else: 102 | print('unkown argument: expected --compile (-c) or --run (-r)') 103 | exit(2) 104 | -------------------------------------------------------------------------------- /benchmark.adoc: -------------------------------------------------------------------------------- 1 | = Execution times 2 | 3 | :f: programs/factor.bf 4 | :m: programs/mandelbrot.bf 5 | 6 | These are the execution time for the link:{f}[factor.bf] (with input 179424691) 7 | and link:{m}[mandel.bf] programs. 8 | 9 | Ran on a Windows 10 notebook, under WSL, with a Intel Core CPU i5-4200U 10 | 1.60GHZ. 11 | 12 | [options="header"] 13 | [cols="1,>1,>1,>1,>1"] 14 | |===================================================================================== 15 | | commit ^| factor.bf (s) ^| delta (%) ^| mandelbrot.bf (s) ^| delta (%) 16 | | basic | 27.68±0.11 ^| - | 96.1±1.7 ^| - 17 | | precompute jumps | 19.74±0.12 | -28.67±0.28 | 72.33±0.74 | -24.59±0.59 18 | | Add | 19.688±0.044 | -0.23±0.46 | 72.09±0.65 | -0.28±0.57 19 | | Move | 6.413±0.065 | -67.43±0.30 | 17.90±0.30 | -75.20±0.25 20 | | Clear | 5.885±0.095 | -8.2±1.2 | 17.78±0.39 | -0.7±1.1 21 | | AddTo | 4.227±0.059 | -28.13±0.29 | 17.31±0.37 | -2.56±0.52 22 | | MoveUntil | 4.068±0.062 | -3.79±0.23 | 14.08±0.29 | -18.61±0.50 23 | | singlepass-jit | 3.888±0.023 | -4.16±1.00 | 14.56±0.13 | 3.9±1.3 24 | | dynasm-jit | 3.877±0.023 | -0.26±0.42 | 14.55±0.12 | -0.03±0.34 25 | | optimized-jit | 0.8500±0.0068 | -78.079±0.065 | 3.565±0.039 | -75.51±0.10 26 | | cranelift-jit | 3.714±0.011 | - | 13.999±0.086 | - 27 | | repeat-cranelift-jit | 1.1386±0.0026 | -69.344±0.040 | 3.740±0.029 | -73.29±0.11 28 | | optimized-cranelift-jit | 0.7478±0.0010 | -34.31±0.20 | 3.389±0.036 | -9.41±0.38 29 | |===================================================================================== 30 | 31 | These values are generate by running the python script `bench-all.py` followed 32 | by `mean.py`, under a linux enviorment. The results are outputed in the file 33 | `time/benchmark.csv`. 34 | 35 | The times are measure with the command `time`. Each commit was run 20 times, and 36 | then computed the mean and standart error. 37 | 38 | = Instructions count 39 | 40 | Manually generated by running the binary given by `cargo build -p bf-optimized 41 | --release --features=profile`. 42 | 43 | [options="header"] 44 | |=========================================================================================================================================================================================== 45 | | 2+| Basic 2+| Add and Move 2+| Clear 2+| AddTo 2+| MoveUntil 46 | | Instruction | mandelbrot.bf | factor.bf | mandelbrot.bf | factor.bf | mandelbrot.bf | factor.bf | mandelbrot.bf | factor.bf | mandelbrot.bf | factor.bf 47 | | + | 179.053.599 | 212.428.900 | 351.460.869 | 396.481.127 | 339.422.378 | 367.942.750 | 222.485.960 | 218.853.566 | 222.485.960 | 218.853.566 48 | | - | 1.776.230.223 | 212.328.376 | - | - | - | - | - | - | | 49 | | > | 4.453.036.023 | 1.220.387.724 | 1.408.648.727 | 484.719.480 | 1.408.648.727 | 484.719.480 | 1.291.712.309 | 335.630.296 | 804.007.203 | 330.602.348 50 | | < | 4.453.036.013 | 1.220.387.704 | - | - | - | - | - | - | | 51 | | [ | 422.534.152 | 118.341.127 | 422.534.152 | 118.341.127 | 375.540.657 | 102.848.457 | 130.270.554 | 79.773.560 | 105.793.470 | 79.261.003 52 | | ] | 835.818.921 | 242.695.606 | 835.818.921 | 242.695.606 | 823.780.430 | 214.157.229 | 765.312.221 | 139.612.637 | 277.607.115 | 134.584.689 53 | | . | 6.240 | 21 | 6.240 | 21 | 6.240 | 21 | 6.240 | 21 | 6.240 | 21 54 | | , | 0 | 10 | 0 | 10 | 0 | 10 | 0 | 10 | 0 | 10 55 | | clear | | | | | 46.993.495 | 15.492.670 | 46.993.495 | 15.492.670 | 46.993.495 | 15.492.670 56 | | addto | | | | | | | 245.270.103 | 23.074.897 | 245.270.103 | 23.074.897 57 | | moveuntil | | | | | | | | | 24.477.084 | 512.557 58 | | Total | 12.119.715.171 | 3.226.569.468 | 3.018.468.909 | 1.242.237.371 | 2.994.391.927 | 1.185.160.617 | 2.702.050.882 | 812.437.657 | 1.726.640.670 | 802.381.761 59 | | Decrease to previous | | | -75,09% | -61,50% | -0,80% | -4,59% | -9,76% | -31,45% | -36,10% | -1,24% 60 | |=========================================================================================================================================================================================== 61 | 62 | -------------------------------------------------------------------------------- /object-example/src/main.rs: -------------------------------------------------------------------------------- 1 | use dynasmrt::x64::X64Relocation; 2 | use dynasmrt::{dynasm, DynasmApi, DynasmLabelApi, VecAssembler}; 3 | 4 | use object::{ 5 | elf::{ 6 | ELFOSABI_SYSV, EM_X86_64, ET_EXEC, PF_R, PF_X, PT_LOAD, SHF_ALLOC, SHF_EXECINSTR, 7 | SHT_PROGBITS, 8 | }, 9 | write::{ 10 | elf::{FileHeader, ProgramHeader}, 11 | Relocation, Symbol, 12 | }, 13 | SymbolFlags, 14 | }; 15 | 16 | fn main() { 17 | let mut code: VecAssembler = VecAssembler::new(0); 18 | 19 | let hello_str = b"Hello world!\n"; 20 | let len = hello_str.len() as i32; 21 | 22 | let relocation_offset; 23 | dynasm!(code 24 | ; lea rdi, [>hello] // string to write 25 | ; mov rsi, DWORD len // length of string to write 26 | ; call DWORD 0 27 | ;; relocation_offset = code.offset().0 as u64 - 4 28 | 29 | // Terminate program 30 | ; mov eax,60 // 'exit' system call 31 | ; mov edi,0 // exit with error code 0 32 | ; syscall // call the kernel 33 | ; hello: 34 | ; .bytes hello_str 35 | ); 36 | 37 | let code = code.finalize().unwrap(); 38 | 39 | let mut buffer = memmap2::MmapOptions::new() 40 | .len(code.len()) 41 | .map_anon() 42 | .unwrap(); 43 | 44 | buffer.copy_from_slice(code.as_slice()); 45 | 46 | let buffer = buffer.make_exec().unwrap(); 47 | 48 | let args: Vec = std::env::args().collect(); 49 | if args.len() != 2 { 50 | eprintln!("expected 1 argument"); 51 | return; 52 | } 53 | match args[1].as_str() { 54 | "run" => unsafe { 55 | let hello: unsafe extern "C" fn() -> ! = std::mem::transmute(buffer.as_ptr()); 56 | hello() 57 | }, 58 | "obj" => { 59 | let mut obj = object::write::Object::new( 60 | object::BinaryFormat::Elf, 61 | object::Architecture::X86_64, 62 | object::Endianness::Little, 63 | ); 64 | 65 | let start = obj.add_symbol(Symbol { 66 | name: b"_start".to_vec(), 67 | kind: object::SymbolKind::Text, 68 | scope: object::SymbolScope::Linkage, 69 | weak: false, 70 | flags: SymbolFlags::None, 71 | 72 | value: 0, 73 | size: 0, 74 | section: object::write::SymbolSection::Undefined, 75 | }); 76 | let my_write = obj.add_symbol(Symbol { 77 | name: b"my_write".to_vec(), 78 | kind: object::SymbolKind::Text, 79 | scope: object::SymbolScope::Linkage, 80 | weak: false, 81 | flags: SymbolFlags::None, 82 | 83 | value: 0, 84 | size: 0, 85 | section: object::write::SymbolSection::Undefined, 86 | }); 87 | 88 | let text = obj.section_id(object::write::StandardSection::Text); 89 | obj.add_relocation( 90 | text, 91 | Relocation { 92 | offset: relocation_offset, 93 | size: 32, 94 | kind: object::RelocationKind::Relative, 95 | encoding: object::RelocationEncoding::Generic, 96 | symbol: my_write, 97 | addend: -4, 98 | }, 99 | ) 100 | .unwrap(); 101 | 102 | obj.add_symbol_data(start, text, &code, 1); 103 | 104 | let mut out = Vec::new(); 105 | obj.emit(&mut out).unwrap(); 106 | 107 | std::fs::write("hello.o", out).unwrap(); 108 | } 109 | "exe" => { 110 | let mut out = Vec::new(); 111 | let mut writer = 112 | object::write::elf::Writer::new(object::Endianness::Little, true, &mut out); 113 | 114 | let text_name = writer.add_section_name(b".text"); 115 | let _text_section = writer.reserve_section_index(); 116 | 117 | writer.reserve_file_header(); 118 | 119 | writer.reserve_program_headers(1); 120 | 121 | writer.reserve_strtab_section_index(); 122 | writer.reserve_strtab(); 123 | 124 | writer.reserve_shstrtab_section_index(); 125 | writer.reserve_shstrtab(); 126 | 127 | writer.reserve_section_headers(); 128 | 129 | let text_offset = writer.reserve(code.len(), 16); 130 | 131 | const PAGE_SIZE: u64 = 0x1000; 132 | 133 | writer 134 | .write_file_header(&FileHeader { 135 | os_abi: ELFOSABI_SYSV, 136 | abi_version: 0, 137 | e_type: ET_EXEC, 138 | e_machine: EM_X86_64, 139 | e_entry: 0x400000 + (text_offset as u64 % PAGE_SIZE), 140 | e_flags: 0, 141 | }) 142 | .unwrap(); 143 | 144 | writer.write_align_program_headers(); 145 | writer.write_program_header(&ProgramHeader { 146 | p_type: PT_LOAD, 147 | p_flags: PF_R | PF_X, 148 | p_offset: text_offset as u64, 149 | p_vaddr: 0x400000 + (text_offset as u64 % PAGE_SIZE), 150 | p_paddr: 0, 151 | p_filesz: code.len() as u64, 152 | p_memsz: code.len() as u64, 153 | p_align: PAGE_SIZE, 154 | }); 155 | 156 | writer.write_strtab(); 157 | 158 | writer.write_shstrtab(); 159 | 160 | writer.write_null_section_header(); 161 | 162 | writer.write_section_header(&object::write::elf::SectionHeader { 163 | name: Some(text_name), 164 | sh_type: SHT_PROGBITS, 165 | sh_flags: (SHF_ALLOC | SHF_EXECINSTR) as u64, 166 | sh_addr: 0x400000, 167 | sh_offset: text_offset as u64, 168 | sh_size: code.len() as u64, 169 | sh_link: 0, 170 | sh_info: 0, 171 | sh_addralign: 16, 172 | sh_entsize: 0, 173 | }); 174 | 175 | writer.write_strtab_section_header(); 176 | writer.write_shstrtab_section_header(); 177 | 178 | writer.write_align(16); 179 | writer.write(&code); 180 | 181 | assert_eq!(writer.reserved_len(), writer.len()); 182 | 183 | std::fs::write("out.exe", out).unwrap(); 184 | } 185 | _ => { 186 | eprintln!("unkown argument"); 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /programs/factor.bf: -------------------------------------------------------------------------------- 1 | [ 2 | Takes an integer from stdin and emits its factors to stdout 3 | 4 | Factor an arbitrarily large positive integer 5 | 6 | Copyright (C) 1999 by Brian Raiter 7 | under the GNU General Public License 8 | ] 9 | 10 | >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>- 11 | 12 | * 13 | * read in the number 14 | * 15 | 16 | <<<<<<<<<+ 17 | [-[>>>>>>>>>>][-]<<<<<<<<<<[[->>>>>>>>>>+<<<<<<<<<<]<<<<<<<<<<] 18 | >>>>>>>>>>,----------] 19 | >>>>>>>>>>[------------------------------------->>>>>>>>>->] 20 | <[+>[>>>>>>>>>+>]<-<<<<<<<<<<]- 21 | 22 | # 23 | 24 | * 25 | * display the number and initialize the loop variable to two 26 | * 27 | 28 | [>++++++++++++++++++++++++++++++++++++++++++++++++. 29 | ------------------------------------------------<<<<<<<<<<<] 30 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++. 31 | --------------------------.[-] 32 | >>>>>>>>>>>>++<<<<+ 33 | 34 | * 35 | * the main loop 36 | * 37 | 38 | [ [-]>> 39 | 40 | * 41 | * make copies of the number and the loop variable 42 | * 43 | 44 | [>>>>[-]>[-]>[-]>[-] 45 | >[-]>[-] 46 | <<<<<<<[->>>+>+<<<<]>>>>>>>>] 47 | <<<<<<<<<<[>>>>>>[-<<<<+>>>>]<<<<<<<<<<<<<<<<]>>>>>>>>>> 48 | [>[->>>+>>+<<<<<]>>>>>>>>>] 49 | <<<<<<<<<<[>>>>>>[-<<<<<+>>>>>]<<<<<<<<<<<<<<<<]>>>>>>>>>> 50 | 51 | * 52 | * divide the number by the loop variable 53 | * 54 | 55 | [>>>[-]>>>[-]>[-]>>>] initialize 56 | <<<<<<<<<<[<<<<<<<<<<] 57 | >>>>>>>>>[-]>>>>>>>+<<<<<<<<[+]+pattern 58 | [ ->> double divisor until above dividend 59 | [>>>>>>[->++<]>>>>]<<<<<<<<<< 60 | [>>>>>>>>[-]>[-] 61 | <<<<[->>>++<<<]<<<<<<<<<<<<<<<]>>>>>>>>>> 62 | [>>>>>>>>[->+<[->+<[->+<[->+<[->+<[->+<[->+<[->+<[->+< 63 | [->--------->>>>>>>>>+<<<<<<<<<<[->+<]]]]]]]]]]]>>] 64 | <<<<<<<<<<[>>>>>>>>>[-<+<<<+>>>>]<<<<<<<<<<<<<<<<<<<]>>>>>>>>>> 65 | [>>>>>>>[-<+>[-<+>[-<+>[-<+>[-<+>[-<+>[-<+>[-<+>[-<+> 66 | [-<--------->>>>>>>>>>>+<<<<<<<<<<[-<+>]]]]]]]]]]]>>>] 67 | <<<<<<<<<< 68 | [>>>>[->>>+>>+<<<<<]<<<<<<<<<<<<<<] 69 | >>>>>>>>>>[>>>>>>>[-<<<+>>>]>>>]<<<<<<<<<< 70 | [>>>>>>>>[->-<]> 71 | [<<<<<<<<<[<[-]>>>>>>>>>>[-<<<<<<<<<<+>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<] 72 | >>>>>>>>>>>>>>>>>>>] 73 | <<<<<<<<<<<<<<<<<<<] 74 | >>>>>>>>>[+[+[+[+[+[+[+[+[+[+[[-]<+>]]]]]]]]]]]< 75 | ] 76 | >>>>>>>> 77 | [ subtract divisor from dividend 78 | <<<<<< 79 | [>>>>>>>>[-]>[-]<<<<<[->>>+>+<<<<]>>>>>>]<<<<<<<<<< 80 | [>>>>>>>>[-<<<<+>>>>]<<<[->>>+>+<<<<]<<<<<<<<<<<<<<<]>>>>>>>>>> 81 | [>>>>>>>>>[-<<<<+>>>>]>]<<<<<<<<<< 82 | [>>>>>>>>[-<->]<<<<<<<<<<<<<<<<<<]>>>>>>>>>> 83 | [>>>>>>>[->+<[->+<[->+<[->+<[->+<[->+<[->+<[->+<[->+<[->+< 84 | [++++++++++[+>-<]>>>>>>>>>>-<<<<<<<<<<]]]]]]]]]]]>>>] 85 | >>>>>>>+ 86 | [ if difference is nonnegative then 87 | [-]<<<<<<<<<<<<<<<<< replace dividend and increment quotient 88 | [>>>>[-]>>>>[-<<<<+>>>>]<<[->>+<<]<<<<<<<<<<<<<<<<]>>>>>>>>>> 89 | [>>>>>>>>[->+<<<+>>]>>]<<<<<<<<<< 90 | [>>>[->>>>>>+<<<<<<]<<<<<<<<<<<<<]>>>>>>>>>> 91 | [>>>>>>>>>[-<<<<<<+>>>>>>[-<<<<<<+>>>>>> 92 | [-<<<<<<+>>>>>>[-<<<<<<+>>>>>> 93 | [-<<<<<<+>>>>>>[-<<<<<<+>>>>>> 94 | [-<<<<<<+>>>>>>[-<<<<<<+>>>>>> 95 | [-<<<<<<+>>>>>>[-<<<<<<--------->>>>>>>>>>>>>>>>+<<<<<<<<<< 96 | [-<<<<<<+>>>>>>]]]]]]]]]]]>] 97 | >>>>>>> 98 | ] halve divisor and loop until zero 99 | <<<<<<<<<<<<<<<<<[<<<<<<<<<<]>>>>>>>>>> 100 | [>>>>>>>>[-]<<[->+<]<[->>>+<<<]>>>>>]<<<<<<<<<< 101 | [+>>>>>>>[-<<<<<<<+>>>>>>>[-<<<<<<<->>>>>>+> 102 | [-<<<<<<<+>>>>>>>[-<<<<<<<->>>>>>+> 103 | [-<<<<<<<+>>>>>>>[-<<<<<<<->>>>>>+> 104 | [-<<<<<<<+>>>>>>>[-<<<<<<<->>>>>>+> 105 | [-<<<<<<<+>>>>>>>]]]]]]]]]<<<<<<< 106 | [->>>>>>>+<<<<<<<]-<<<<<<<<<<] 107 | >>>>>>> 108 | [-<<<<<<<<<<<+>>>>>>>>>>>] 109 | >>>[>>>>>>>[-<<<<<<<<<<<+++++>>>>>>>>>>>]>>>]<<<<<<<<<< 110 | [+>>>>>>>>[-<<<<<<<<+>>>>>>>>[-<<<<<<<<->>>>>+>>> 111 | [-<<<<<<<<+>>>>>>>>[-<<<<<<<<->>>>>+>>> 112 | [-<<<<<<<<+>>>>>>>>[-<<<<<<<<->>>>>+>>> 113 | [-<<<<<<<<+>>>>>>>>[-<<<<<<<<->>>>>+>>> 114 | [-<<<<<<<<+>>>>>>>>]]]]]]]]]<<<<<<<< 115 | [->>>>>>>>+<<<<<<<<]-<<<<<<<<<<] 116 | >>>>>>>>[-<<<<<<<<<<<<<+>>>>>>>>>>>>>]>> 117 | [>>>>>>>>[-<<<<<<<<<<<<<+++++>>>>>>>>>>>>>]>>]<<<<<<<<<< 118 | [<<<<<<<<<<]>>>>>>>>>> 119 | >>>>>> 120 | ] 121 | <<<<<< 122 | 123 | * 124 | * make copies of the loop variable and the quotient 125 | * 126 | 127 | [>>>[->>>>+>+<<<<<]>>>>>>>] 128 | <<<<<<<<<< 129 | [>>>>>>>[-<<<<+>>>>]<<<<<[->>>>>+>>+<<<<<<<]<<<<<<<<<<<<] 130 | >>>>>>>>>>[>>>>>>>[-<<<<<+>>>>>]>>>]<<<<<<<<<< 131 | 132 | * 133 | * break out of the loop if the quotient is larger than the loop variable 134 | * 135 | 136 | [>>>>>>>>>[-<->]< 137 | [<<<<<<<< 138 | [<<[-]>>>>>>>>>>[-<<<<<<<<<<+>>>>>>>>>>]<<<<<<<<<<<<<<<<<<] 139 | >>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<] 140 | >>>>>>>>[>-<[+[+[+[+[+[+[+[+[+[[-]>+<]]]]]]]]]]]>+ 141 | 142 | [ [-] 143 | 144 | * 145 | * partially increment the loop variable 146 | * 147 | 148 | <[-]+>>>>+>>>>>>>>[>>>>>>>>>>]<<<<<<<<<< 149 | 150 | * 151 | * examine the remainder for nonzero digits 152 | * 153 | 154 | [<<<<<<[<<<<[<<<<<<<<<<]>>>>+<<<<<<<<<<]<<<<] 155 | >>>>>>>>>>>>>>>>>>>>[>>>>>>>>>>]<<<<<<<<<<[<<<<<<<<<<] 156 | >>>>- 157 | 158 | [ [+] 159 | 160 | * 161 | * decrement the loop variable and replace the number with the quotient 162 | * 163 | 164 | >>>>>>>>-<<[>[-]>>[-<<+>>]>>>>>>>]<<<<<<<<<< 165 | 166 | * 167 | * display the loop variable 168 | * 169 | 170 | [+>>[>>>>>>>>+>>]<<-<<<<<<<<<<]- 171 | [>>++++++++++++++++++++++++++++++++++++++++++++++++. 172 | ------------------------------------------------<<<<<<<<<<<<] 173 | ++++++++++++++++++++++++++++++++.[-]>>>> 174 | 175 | ] 176 | 177 | * 178 | * normalize the loop variable 179 | * 180 | 181 | >>>>>> 182 | [>>[->>>>>+<<<<<[->>>>>+<<<<< 183 | [->>>>>+<<<<<[->>>>>+<<<<< 184 | [->>>>>+<<<<<[->>>>>+<<<<< 185 | [->>>>>+<<<<<[->>>>>+<<<<< 186 | [->>>>>+<<<<<[->>>>>--------->>>>>+<<<<<<<<<< 187 | [->>>>>+<<<<<]]]]]]]]]]]>>>>>>>>] 188 | <<<<<<<<<<[>>>>>>>[-<<<<<+>>>>>]<<<<<<<<<<<<<<<<<] 189 | >>>>>>>>> 190 | 191 | ]< 192 | 193 | ]>> 194 | 195 | * 196 | * display the number and end 197 | * 198 | 199 | [>>>>>>>>>>]<<<<<<<<<<[+>[>>>>>>>>>+>]<-<<<<<<<<<<]- 200 | [>++++++++++++++++++++++++++++++++++++++++++++++++.<<<<<<<<<<<] 201 | ++++++++++. 202 | -------------------------------------------------------------------------------- /interpreter/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::{Read, Write}, 3 | process::ExitCode, 4 | }; 5 | 6 | #[derive(PartialEq, Eq, Clone, Copy)] 7 | enum Instruction { 8 | Increase, 9 | Decrease, 10 | MoveRight, 11 | MoveLeft, 12 | Input, 13 | Output, 14 | JumpRight, 15 | JumpLeft, 16 | } 17 | 18 | #[derive(Default, Debug)] 19 | #[cfg(feature = "profile")] 20 | struct Profile { 21 | inc: u64, 22 | dec: u64, 23 | movr: u64, 24 | movl: u64, 25 | jr: u64, 26 | jl: u64, 27 | inp: u64, 28 | out: u64, 29 | } 30 | 31 | struct Program { 32 | program_counter: usize, 33 | pointer: usize, 34 | instructions: Vec, 35 | memory: [u8; 30_000], 36 | #[cfg(feature = "profile")] 37 | profile: Profile, 38 | } 39 | impl Program { 40 | fn new(source: &[u8]) -> Program { 41 | let instructions: Vec = source 42 | .iter() 43 | .filter_map(|b| match b { 44 | b'+' => Some(Instruction::Increase), 45 | b'-' => Some(Instruction::Decrease), 46 | b'.' => Some(Instruction::Output), 47 | b',' => Some(Instruction::Input), 48 | b'>' => Some(Instruction::MoveRight), 49 | b'<' => Some(Instruction::MoveLeft), 50 | b'[' => Some(Instruction::JumpRight), 51 | b']' => Some(Instruction::JumpLeft), 52 | _ => None, 53 | }) 54 | .collect(); 55 | 56 | Program { 57 | program_counter: 0, 58 | pointer: 0, 59 | instructions, 60 | memory: [0; 30_000], 61 | #[cfg(feature = "profile")] 62 | profile: Profile::default(), 63 | } 64 | } 65 | 66 | fn run(&mut self) -> std::io::Result<()> { 67 | let mut stdout = std::io::stdout().lock(); 68 | let mut stdin = std::io::stdin().lock(); 69 | 'program: loop { 70 | use Instruction::*; 71 | 72 | #[cfg(feature = "profile")] 73 | { 74 | match self.instructions[self.program_counter] { 75 | Increase => self.profile.inc += 1, 76 | Decrease => self.profile.dec += 1, 77 | Output => self.profile.out += 1, 78 | Input => self.profile.inp += 1, 79 | MoveRight => self.profile.movr += 1, 80 | MoveLeft => self.profile.movl += 1, 81 | JumpRight => self.profile.jr += 1, 82 | JumpLeft => self.profile.jl += 1, 83 | } 84 | } 85 | 86 | match self.instructions[self.program_counter] { 87 | Increase => self.memory[self.pointer] = self.memory[self.pointer].wrapping_add(1), 88 | Decrease => self.memory[self.pointer] = self.memory[self.pointer].wrapping_sub(1), 89 | Output => { 90 | let value = self.memory[self.pointer]; 91 | // Writing a non-UTF-8 byte sequence on Windows error out. 92 | if !cfg!(target_os = "windows") || value < 128 { 93 | stdout.write_all(&[value])?; 94 | stdout.flush()?; 95 | } 96 | } 97 | Input => loop { 98 | let err = stdin.read_exact(&mut self.memory[self.pointer..self.pointer + 1]); 99 | match err.as_ref().map_err(|e| e.kind()) { 100 | Err(std::io::ErrorKind::UnexpectedEof) => { 101 | self.memory[self.pointer] = 0; 102 | } 103 | _ => err?, 104 | } 105 | if cfg!(target_os = "windows") && self.memory[self.pointer] == b'\r' { 106 | continue; 107 | } 108 | break; 109 | }, 110 | MoveRight => self.pointer = (self.pointer + 1) % self.memory.len(), 111 | MoveLeft => { 112 | self.pointer = (self.pointer + self.memory.len() - 1) % self.memory.len() 113 | } 114 | JumpRight => { 115 | if self.memory[self.pointer] == 0 { 116 | let mut deep = 1; 117 | loop { 118 | if self.program_counter + 1 == self.instructions.len() { 119 | break 'program; 120 | } 121 | self.program_counter += 1; 122 | if self.instructions[self.program_counter] == JumpRight { 123 | deep += 1; 124 | } 125 | if self.instructions[self.program_counter] == JumpLeft { 126 | deep -= 1; 127 | } 128 | if deep == 0 { 129 | break; 130 | } 131 | } 132 | } 133 | } 134 | JumpLeft => { 135 | if self.memory[self.pointer] != 0 { 136 | let mut deep = 1; 137 | loop { 138 | if self.program_counter == 0 { 139 | break 'program; 140 | } 141 | self.program_counter -= 1; 142 | if self.instructions[self.program_counter] == JumpLeft { 143 | deep += 1; 144 | } 145 | if self.instructions[self.program_counter] == JumpRight { 146 | deep -= 1; 147 | } 148 | if deep == 0 { 149 | break; 150 | } 151 | } 152 | } 153 | } 154 | } 155 | self.program_counter += 1; 156 | 157 | if self.instructions.len() == self.program_counter { 158 | break 'program; 159 | } 160 | } 161 | Ok(()) 162 | } 163 | } 164 | 165 | fn main() -> ExitCode { 166 | let mut args = std::env::args(); 167 | if args.len() != 2 { 168 | eprintln!("expected a single file path as argument"); 169 | return ExitCode::from(1); 170 | } 171 | 172 | let file_name = args.nth(1).unwrap(); 173 | let source = match std::fs::read(&file_name) { 174 | Ok(x) => x, 175 | Err(err) => { 176 | eprintln!("Error reading '{}': {}", file_name, err); 177 | return ExitCode::from(2); 178 | } 179 | }; 180 | 181 | let mut program = Program::new(&source); 182 | 183 | if let Err(err) = program.run() { 184 | eprintln!("IO error: {}", err); 185 | } 186 | 187 | #[cfg(feature = "profile")] 188 | { 189 | dbg!(program.profile); 190 | } 191 | 192 | ExitCode::from(0) 193 | } 194 | -------------------------------------------------------------------------------- /singlepass-jit/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Read, Write}; 2 | use std::process::ExitCode; 3 | 4 | use dynasmrt::mmap::MutableBuffer; 5 | use dynasmrt::{dynasm, x64::X64Relocation, DynasmApi, DynasmLabelApi, VecAssembler}; 6 | 7 | struct UnbalancedBrackets(char, usize); 8 | 9 | struct Program { 10 | code: Vec, 11 | memory: [u8; 30_000], 12 | } 13 | impl Program { 14 | fn new(source: &[u8]) -> Result { 15 | let mut code: VecAssembler = VecAssembler::new(0); 16 | 17 | // r12 will be the adress of `memory` 18 | // r13 will be the value of `pointer` 19 | // r12 is got from argument 1 in `rdi` 20 | // r13 is set to 0 21 | dynasm! { code 22 | ; .arch x64 23 | ; push rbp 24 | ; mov rbp, rsp 25 | ; push r12 26 | ; push r13 27 | ; mov r12, rdi 28 | ; xor r13, r13 29 | }; 30 | 31 | let mut bracket_stack = Vec::new(); 32 | 33 | for b in source { 34 | match b { 35 | b'+' => dynasm! { code 36 | ; .arch x64 37 | ; add BYTE [r12 + r13], 1 38 | }, 39 | b'-' => dynasm! { code 40 | ; .arch x64 41 | ; add BYTE [r12 + r13], -1 42 | }, 43 | b'.' => { 44 | dynasm! { code 45 | ; .arch x64 46 | ; mov rax, QWORD write as *const () as i64 47 | ; mov rdi, [r12 + r13] // cell value 48 | ; call rax 49 | ; cmp rax, 0 50 | ; jne ->exit 51 | } 52 | } 53 | b',' => { 54 | dynasm! { code 55 | ; .arch x64 56 | ; mov rax, QWORD read as *const () as i64 57 | ; lea rdi, [r12 + r13] // cell address 58 | ; call rax 59 | ; cmp rax, 0 60 | ; jne ->exit 61 | } 62 | } 63 | b'<' => dynasm! { code 64 | ; .arch x64 65 | ; sub r13, 1 66 | ; mov eax, 29999 67 | ; cmovb r13, rax 68 | }, 69 | b'>' => dynasm! { code 70 | ; .arch x64 71 | ; add r13, 1 72 | ; xor eax, eax 73 | ; cmp r13, 30000 74 | ; cmove r13, rax 75 | }, 76 | b'[' => { 77 | let start_label = code.new_dynamic_label(); 78 | let end_label = code.new_dynamic_label(); 79 | dynasm! { code 80 | ; .arch x64 81 | ; cmp BYTE [r12+r13], 0 82 | ; je =>end_label 83 | ; =>start_label 84 | }; 85 | 86 | bracket_stack.push((start_label, end_label)); 87 | } 88 | b']' => { 89 | let (start_label, end_label) = match bracket_stack.pop() { 90 | Some(x) => x, 91 | None => return Err(UnbalancedBrackets(']', code.offset().0)), 92 | }; 93 | 94 | dynasm! { code 95 | ; .arch x64 96 | ; cmp BYTE [r12 + r13], 0 97 | ; jne =>start_label 98 | ; => end_label 99 | }; 100 | } 101 | _ => continue, 102 | } 103 | } 104 | 105 | if !bracket_stack.is_empty() { 106 | return Err(UnbalancedBrackets(']', code.offset().0)); 107 | } 108 | 109 | // when we push to the stack, we need to remeber 110 | // to pop them in the opossite order. 111 | dynasm! { code 112 | ; .arch x64 113 | ; xor rax, rax 114 | ; ->exit: 115 | ; pop r13 116 | ; pop r12 117 | ; pop rbp 118 | ; ret 119 | } 120 | 121 | Ok(Program { 122 | code: code.finalize().unwrap(), 123 | memory: [0; 30_000], 124 | }) 125 | } 126 | 127 | fn run(&mut self) -> std::io::Result<()> { 128 | let mut buffer = MutableBuffer::new(self.code.len()).unwrap(); 129 | buffer.set_len(self.code.len()); 130 | 131 | buffer.copy_from_slice(&self.code); 132 | 133 | let buffer = buffer.make_exec().unwrap(); 134 | 135 | unsafe { 136 | let code_fn: unsafe extern "sysv64" fn(*mut u8) -> *mut std::io::Error = 137 | std::mem::transmute(buffer.as_ptr()); 138 | 139 | let error = code_fn(self.memory.as_mut_ptr()); 140 | 141 | if !error.is_null() { 142 | return Err(*Box::from_raw(error)); 143 | } 144 | } 145 | 146 | Ok(()) 147 | } 148 | } 149 | 150 | extern "sysv64" fn write(value: u8) -> *mut std::io::Error { 151 | // Writing a non-UTF-8 byte sequence on Windows error out. 152 | if cfg!(target_os = "windows") && value >= 128 { 153 | return std::ptr::null_mut(); 154 | } 155 | 156 | let mut stdout = std::io::stdout().lock(); 157 | 158 | let result = stdout.write_all(&[value]).and_then(|_| stdout.flush()); 159 | 160 | match result { 161 | Err(err) => Box::into_raw(Box::new(err)), 162 | _ => std::ptr::null_mut(), 163 | } 164 | } 165 | 166 | unsafe extern "sysv64" fn read(buf: *mut u8) -> *mut std::io::Error { 167 | let mut stdin = std::io::stdin().lock(); 168 | loop { 169 | let mut value = 0; 170 | let err = stdin.read_exact(std::slice::from_mut(&mut value)); 171 | 172 | if let Err(err) = err { 173 | if err.kind() != std::io::ErrorKind::UnexpectedEof { 174 | return Box::into_raw(Box::new(err)); 175 | } 176 | value = 0; 177 | } 178 | 179 | // ignore CR from Window's CRLF 180 | if cfg!(target_os = "windows") && value == b'\r' { 181 | continue; 182 | } 183 | 184 | *buf = value; 185 | 186 | return std::ptr::null_mut(); 187 | } 188 | } 189 | 190 | fn main() -> ExitCode { 191 | let mut args = std::env::args(); 192 | if args.len() != 2 { 193 | eprintln!("expected a single file path as argument"); 194 | return ExitCode::from(1); 195 | } 196 | 197 | let file_name = args.nth(1).unwrap(); 198 | let source = match std::fs::read(&file_name) { 199 | Ok(x) => x, 200 | Err(err) => { 201 | eprintln!("Error reading '{}': {}", file_name, err); 202 | return ExitCode::from(2); 203 | } 204 | }; 205 | 206 | let mut program = match Program::new(&source) { 207 | Ok(x) => x, 208 | Err(UnbalancedBrackets(c, address)) => { 209 | eprintln!( 210 | "Error parsing file: didn't found pair for `{}` at instruction index {}", 211 | c, address 212 | ); 213 | return ExitCode::from(3); 214 | } 215 | }; 216 | 217 | if let Err(err) = program.run() { 218 | eprintln!("IO error: {}", err); 219 | } 220 | 221 | ExitCode::from(0) 222 | } 223 | -------------------------------------------------------------------------------- /singlepass-compiler/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::process::ExitCode; 2 | 3 | use dynasmrt::{dynasm, x64::X64Relocation, DynasmApi, DynasmLabelApi, VecAssembler}; 4 | 5 | use object::{ 6 | write::{Relocation, Symbol}, 7 | SymbolFlags, 8 | }; 9 | 10 | struct UnbalancedBrackets(char, usize); 11 | 12 | struct Program { 13 | code: Vec, 14 | write_relocations: Vec, 15 | read_relocations: Vec, 16 | exit_relocation: usize, 17 | } 18 | impl Program { 19 | fn new(source: &[u8]) -> Result { 20 | let mut code: VecAssembler = VecAssembler::new(0); 21 | 22 | // r12 will be the adress of `memory` 23 | // r13 will be the value of `pointer` 24 | // r13 is set to 0 25 | dynasm! { code 26 | ; .arch x64 27 | ; push rbp 28 | ; mov rbp, rsp 29 | ; xor r13, r13 30 | 31 | // allocate 30_0000 bytes on stack for the memory 32 | ; sub rsp, 30_000 33 | ; mov r12, rsp 34 | 35 | // zero the memory 36 | ; xor eax, eax 37 | ; mov r11, rbp 38 | ; loop_: 39 | ; add r11, -8 40 | ; mov QWORD [r11], rax 41 | ; cmp r11, r12 42 | ; jne dynasm! { code 53 | ; .arch x64 54 | ; add BYTE [r12 + r13], 1 55 | }, 56 | b'-' => dynasm! { code 57 | ; .arch x64 58 | ; add BYTE [r12 + r13], -1 59 | }, 60 | b'.' => dynasm! { code 61 | ; .arch x64 62 | ; mov rdi, [r12 + r13] // cell value 63 | ; call DWORD 0 64 | ;; write_relocations.push(code.offset().0 - 4) 65 | }, 66 | b',' => dynasm! { code 67 | ; .arch x64 68 | ; lea rdi, [r12 + r13] // cell address 69 | ; call DWORD 0 70 | ;; read_relocations.push(code.offset().0 - 4) 71 | }, 72 | 73 | b'<' => dynasm! { code 74 | ; .arch x64 75 | ; sub r13, 1 76 | ; mov eax, 29999 77 | ; cmovb r13, rax 78 | }, 79 | b'>' => dynasm! { code 80 | ; .arch x64 81 | ; add r13, 1 82 | ; xor eax, eax 83 | ; cmp r13, 30000 84 | ; cmove r13, rax 85 | }, 86 | b'[' => { 87 | let start_label = code.new_dynamic_label(); 88 | let end_label = code.new_dynamic_label(); 89 | dynasm! { code 90 | ; .arch x64 91 | ; cmp BYTE [r12+r13], 0 92 | ; je =>end_label 93 | ; =>start_label 94 | }; 95 | 96 | bracket_stack.push((start_label, end_label)); 97 | } 98 | b']' => { 99 | let (start_label, end_label) = match bracket_stack.pop() { 100 | Some(x) => x, 101 | None => return Err(UnbalancedBrackets(']', code.offset().0)), 102 | }; 103 | 104 | dynasm! { code 105 | ; .arch x64 106 | ; cmp BYTE [r12 + r13], 0 107 | ; jne =>start_label 108 | ; => end_label 109 | }; 110 | } 111 | _ => continue, 112 | } 113 | } 114 | 115 | if !bracket_stack.is_empty() { 116 | return Err(UnbalancedBrackets(']', code.offset().0)); 117 | } 118 | 119 | let exit_relocation; 120 | 121 | dynasm! { code 122 | ; .arch x64 123 | ; call DWORD 0 124 | ;; exit_relocation = code.offset().0 - 4 125 | } 126 | 127 | Ok(Program { 128 | code: code.finalize().unwrap(), 129 | write_relocations, 130 | read_relocations, 131 | exit_relocation, 132 | }) 133 | } 134 | 135 | fn to_elf_object(&self) -> Vec { 136 | let (format, entry_name) = if cfg!(target_os = "windows") { 137 | (object::BinaryFormat::Coff, "WinMain") 138 | } else if cfg!(target_os = "linux") { 139 | (object::BinaryFormat::Elf, "_start") 140 | } else { 141 | unimplemented!("Only Linux and Windows are implemented") 142 | }; 143 | let entry_name = entry_name.as_bytes(); 144 | 145 | let mut obj = object::write::Object::new( 146 | format, 147 | object::Architecture::X86_64, 148 | object::Endianness::Little, 149 | ); 150 | 151 | let mut add_symbol = |name: &[u8]| { 152 | obj.add_symbol(Symbol { 153 | name: name.to_vec(), 154 | value: 0, 155 | size: 0, 156 | kind: object::SymbolKind::Text, 157 | scope: object::SymbolScope::Linkage, 158 | weak: false, 159 | section: object::write::SymbolSection::Undefined, 160 | flags: SymbolFlags::None, 161 | }) 162 | }; 163 | 164 | let start = add_symbol(entry_name); 165 | let bf_write = add_symbol(b"bf_write"); 166 | let bf_read = add_symbol(b"bf_read"); 167 | let bf_exit = add_symbol(b"bf_exit"); 168 | 169 | let text = obj.section_id(object::write::StandardSection::Text); 170 | obj.add_symbol_data(start, text, &self.code, 16); 171 | 172 | let mut add_call_reloc = |offset, symbol| { 173 | obj.add_relocation( 174 | text, 175 | Relocation { 176 | offset: offset as u64, 177 | symbol, 178 | size: 32, 179 | kind: object::RelocationKind::Relative, 180 | encoding: object::RelocationEncoding::Generic, 181 | addend: -4, 182 | }, 183 | ) 184 | .unwrap(); 185 | }; 186 | 187 | for offset in self.read_relocations.iter().copied() { 188 | add_call_reloc(offset, bf_read); 189 | } 190 | for offset in self.write_relocations.iter().copied() { 191 | add_call_reloc(offset, bf_write); 192 | } 193 | add_call_reloc(self.exit_relocation, bf_exit); 194 | 195 | let mut out = Vec::new(); 196 | obj.emit(&mut out).unwrap(); 197 | 198 | out 199 | } 200 | } 201 | 202 | fn main() -> ExitCode { 203 | let mut args = std::env::args(); 204 | 205 | let file_name = args.nth(1).unwrap(); 206 | let source = match std::fs::read(&file_name) { 207 | Ok(x) => x, 208 | Err(err) => { 209 | eprintln!("Error reading '{}': {}", file_name, err); 210 | return ExitCode::from(2); 211 | } 212 | }; 213 | 214 | let program = match Program::new(&source) { 215 | Ok(x) => x, 216 | Err(UnbalancedBrackets(c, address)) => { 217 | eprintln!( 218 | "Error parsing file: didn't found pair for `{}` at instruction index {}", 219 | c, address 220 | ); 221 | return ExitCode::from(3); 222 | } 223 | }; 224 | 225 | let option = args.next(); 226 | let output_name = args.next().unwrap_or_else(|| { 227 | std::path::Path::new(&file_name) 228 | .file_stem() 229 | .unwrap() 230 | .to_string_lossy() 231 | .to_string() 232 | }); 233 | match option.unwrap().as_str() { 234 | "-o" => { 235 | let output_name = std::path::Path::new(&output_name).with_extension("o"); 236 | let obj = program.to_elf_object(); 237 | std::fs::write(output_name, obj).unwrap(); 238 | } 239 | arg => panic!("unknown arg {arg}"), 240 | } 241 | 242 | ExitCode::from(0) 243 | } 244 | -------------------------------------------------------------------------------- /programs/mandelbrot.bf: -------------------------------------------------------------------------------- 1 | A mandelbrot set fractal viewer in brainf*** written by Erik Bosman 2 | +++++++++++++[->++>>>+++++>++>+<<<<<<]>>>>>++++++>--->>>>>>>>>>+++++++++++++++[[ 3 | >>>>>>>>>]+[<<<<<<<<<]>>>>>>>>>-]+[>>>>>>>>[-]>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>[-]+ 4 | <<<<<<<+++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>>>>+>>>>>>>>>>>>>>>>>>>>>>>>>> 5 | >+<<<<<<<<<<<<<<<<<[<<<<<<<<<]>>>[-]+[>>>>>>[>>>>>>>[-]>>]<<<<<<<<<[<<<<<<<<<]>> 6 | >>>>>[-]+<<<<<<++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>>>+<<<<<<+++++++[-[->>> 7 | >>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>>>+<<<<<<<<<<<<<<<<[<<<<<<<<<]>>>[[-]>>>>>>[>>>>> 8 | >>[-<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<+<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>> 9 | [>>>>>>>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<+<<<+<<]>>>>>>>>]<<<<<<<<<[<<<<<<< 10 | <<]>>>>>>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<+<<<<<]>>>>>>>>>+++++++++++++++[[ 11 | >>>>>>>>>]+>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+[ 12 | >+>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>[-<<<<+>>>>]<<<<[->>>>+<<<<<[->>[ 13 | -<<+>>]<<[->>+>>+<<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>>>>]<<<<<<< 14 | <<[>[->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<]>[->>>>>>>>>+<<<<<<<<<]<+>>>>>>>>]<<<<<<<<< 15 | [>[-]<->>>>[-<<<<+>[<->-<<<<<<+>>>>>>]<[->+<]>>>>]<<<[->>>+<<<]<+<<<<<<<<<]>>>>> 16 | >>>>[>+>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>>[-<<<<<+>>>>>]<<<<<[->>>>>+ 17 | <<<<<<[->>>[-<<<+>>>]<<<[->>>+>+<<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>> 18 | >>>>>>>]<<<<<<<<<[>>[->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<<]>>[->>>>>>>>>+<<<<<<<<<]<< 19 | +>>>>>>>>]<<<<<<<<<[>[-]<->>>>[-<<<<+>[<->-<<<<<<+>>>>>>]<[->+<]>>>>]<<<[->>>+<< 20 | <]<+<<<<<<<<<]>>>>>>>>>[>>>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>> 21 | >>>>>>>>>>>>>>>>>>>>>>>]>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>+++++++++++++++[[>>>> 22 | >>>>>]<<<<<<<<<-<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+>>>>>>>>>>>>>>>>>>>>>+<<<[<<<<<< 23 | <<<]>>>>>>>>>[>>>[-<<<->>>]+<<<[->>>->[-<<<<+>>>>]<<<<[->>>>+<<<<<<<<<<<<<[<<<<< 24 | <<<<]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>>>[-<<<<->>>>]+<<<<[->>>>-<[-<<<+>>>]<<<[-> 25 | >>+<<<<<<<<<<<<[<<<<<<<<<]>>>[-]+>>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>>>>>>>>]<<<<<< 26 | <<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]<<<<<<<[->+>>>-<<<<]>>>>>>>>>+++++++++++++++++++ 27 | +++++++>>[-<<<<+>>>>]<<<<[->>>>+<<[-]<<]>>[<<<<<<<+<[-<+>>>>+<<[-]]>[-<<[->+>>>- 28 | <<<<]>>>]>>>>>>>>>>>>>[>>[-]>[-]>[-]>>>>>]<<<<<<<<<[<<<<<<<<<]>>>[-]>>>>>>[>>>>> 29 | [-<<<<+>>>>]<<<<[->>>>+<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>[-<<<<<<<< 30 | <+>>>>>>>>>]>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>+++++++++++++++[[>>>>>>>>>]+>[- 31 | ]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+[>+>>>>>>>>]<<< 32 | <<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>>[-<<<<<+>>>>>]<<<<<[->>>>>+<<<<<<[->>[-<<+>>]< 33 | <[->>+>+<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>>>>]<<<<<<<<<[>[->>>> 34 | >>>>>+<<<<<<<<<]<<<<<<<<<<]>[->>>>>>>>>+<<<<<<<<<]<+>>>>>>>>]<<<<<<<<<[>[-]<->>> 35 | [-<<<+>[<->-<<<<<<<+>>>>>>>]<[->+<]>>>]<<[->>+<<]<+<<<<<<<<<]>>>>>>>>>[>>>>>>[-< 36 | <<<<+>>>>>]<<<<<[->>>>>+<<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>+>>>>>>>> 37 | ]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>>[-<<<<<+>>>>>]<<<<<[->>>>>+<<<<<<[->>[-<<+ 38 | >>]<<[->>+>>+<<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>>>>]<<<<<<<<<[> 39 | [->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<]>[->>>>>>>>>+<<<<<<<<<]<+>>>>>>>>]<<<<<<<<<[>[- 40 | ]<->>>>[-<<<<+>[<->-<<<<<<+>>>>>>]<[->+<]>>>>]<<<[->>>+<<<]<+<<<<<<<<<]>>>>>>>>> 41 | [>>>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 42 | ]>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+> 43 | >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>++++++++ 44 | +++++++[[>>>>>>>>>]<<<<<<<<<-<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+[>>>>>>>>[-<<<<<<<+ 45 | >>>>>>>]<<<<<<<[->>>>>>>+<<<<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>>>>[ 46 | -]>>>]<<<<<<<<<[<<<<<<<<<]>>>>+>[-<-<<<<+>>>>>]>[-<<<<<<[->>>>>+<++<<<<]>>>>>[-< 47 | <<<<+>>>>>]<->+>]<[->+<]<<<<<[->>>>>+<<<<<]>>>>>>[-]<<<<<<+>>>>[-<<<<->>>>]+<<<< 48 | [->>>>->>>>>[>>[-<<->>]+<<[->>->[-<<<+>>>]<<<[->>>+<<<<<<<<<<<<[<<<<<<<<<]>>>[-] 49 | +>>>>>>[>>>>>>>>>]>+<]]+>>>[-<<<->>>]+<<<[->>>-<[-<<+>>]<<[->>+<<<<<<<<<<<[<<<<< 50 | <<<<]>>>>[-]+>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>>>>>>>>]<<<<<<<<]>>>>>>>>]<<<<<<<<< 51 | [<<<<<<<<<]>>>>[-<<<<+>>>>]<<<<[->>>>+>>>>>[>+>>[-<<->>]<<[->>+<<]>>>>>>>>]<<<<< 52 | <<<+<[>[->>>>>+<<<<[->>>>-<<<<<<<<<<<<<<+>>>>>>>>>>>[->>>+<<<]<]>[->>>-<<<<<<<<< 53 | <<<<<+>>>>>>>>>>>]<<]>[->>>>+<<<[->>>-<<<<<<<<<<<<<<+>>>>>>>>>>>]<]>[->>>+<<<]<< 54 | <<<<<<<<<<]>>>>[-]<<<<]>>>[-<<<+>>>]<<<[->>>+>>>>>>[>+>[-<->]<[->+<]>>>>>>>>]<<< 55 | <<<<<+<[>[->>>>>+<<<[->>>-<<<<<<<<<<<<<<+>>>>>>>>>>[->>>>+<<<<]>]<[->>>>-<<<<<<< 56 | <<<<<<<+>>>>>>>>>>]<]>>[->>>+<<<<[->>>>-<<<<<<<<<<<<<<+>>>>>>>>>>]>]<[->>>>+<<<< 57 | ]<<<<<<<<<<<]>>>>>>+<<<<<<]]>>>>[-<<<<+>>>>]<<<<[->>>>+>>>>>[>>>>>>>>>]<<<<<<<<< 58 | [>[->>>>>+<<<<[->>>>-<<<<<<<<<<<<<<+>>>>>>>>>>>[->>>+<<<]<]>[->>>-<<<<<<<<<<<<<< 59 | +>>>>>>>>>>>]<<]>[->>>>+<<<[->>>-<<<<<<<<<<<<<<+>>>>>>>>>>>]<]>[->>>+<<<]<<<<<<< 60 | <<<<<]]>[-]>>[-]>[-]>>>>>[>>[-]>[-]>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>>>[-< 61 | <<<+>>>>]<<<<[->>>>+<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>+++++++++++++++[ 62 | [>>>>>>>>>]+>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+ 63 | [>+>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>[-<<<<+>>>>]<<<<[->>>>+<<<<<[->> 64 | [-<<+>>]<<[->>+>+<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>>>>]<<<<<<<< 65 | <[>[->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<]>[->>>>>>>>>+<<<<<<<<<]<+>>>>>>>>]<<<<<<<<<[ 66 | >[-]<->>>[-<<<+>[<->-<<<<<<<+>>>>>>>]<[->+<]>>>]<<[->>+<<]<+<<<<<<<<<]>>>>>>>>>[ 67 | >>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]> 68 | >>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>[-]>>>>+++++++++++++++[[>>>>>>>>>]<<<<<<<<<-<<<<< 69 | <<<<[<<<<<<<<<]>>>>>>>>>-]+[>>>[-<<<->>>]+<<<[->>>->[-<<<<+>>>>]<<<<[->>>>+<<<<< 70 | <<<<<<<<[<<<<<<<<<]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>>>[-<<<<->>>>]+<<<<[->>>>-<[- 71 | <<<+>>>]<<<[->>>+<<<<<<<<<<<<[<<<<<<<<<]>>>[-]+>>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>> 72 | >>>>>>>]<<<<<<<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>[-<<<+>>>]<<<[->>>+>>>>>>[>+>>> 73 | [-<<<->>>]<<<[->>>+<<<]>>>>>>>>]<<<<<<<<+<[>[->+>[-<-<<<<<<<<<<+>>>>>>>>>>>>[-<< 74 | +>>]<]>[-<<-<<<<<<<<<<+>>>>>>>>>>>>]<<<]>>[-<+>>[-<<-<<<<<<<<<<+>>>>>>>>>>>>]<]> 75 | [-<<+>>]<<<<<<<<<<<<<]]>>>>[-<<<<+>>>>]<<<<[->>>>+>>>>>[>+>>[-<<->>]<<[->>+<<]>> 76 | >>>>>>]<<<<<<<<+<[>[->+>>[-<<-<<<<<<<<<<+>>>>>>>>>>>[-<+>]>]<[-<-<<<<<<<<<<+>>>> 77 | >>>>>>>]<<]>>>[-<<+>[-<-<<<<<<<<<<+>>>>>>>>>>>]>]<[-<+>]<<<<<<<<<<<<]>>>>>+<<<<< 78 | ]>>>>>>>>>[>>>[-]>[-]>[-]>>>>]<<<<<<<<<[<<<<<<<<<]>>>[-]>[-]>>>>>[>>>>>>>[-<<<<< 79 | <+>>>>>>]<<<<<<[->>>>>>+<<<<+<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>+>[-<-<<<<+>>>> 80 | >]>>[-<<<<<<<[->>>>>+<++<<<<]>>>>>[-<<<<<+>>>>>]<->+>>]<<[->>+<<]<<<<<[->>>>>+<< 81 | <<<]+>>>>[-<<<<->>>>]+<<<<[->>>>->>>>>[>>>[-<<<->>>]+<<<[->>>-<[-<<+>>]<<[->>+<< 82 | <<<<<<<<<[<<<<<<<<<]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>[-<<->>]+<<[->>->[-<<<+>>>]< 83 | <<[->>>+<<<<<<<<<<<<[<<<<<<<<<]>>>[-]+>>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>>>>>>>>]< 84 | <<<<<<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>[-<<<+>>>]<<<[->>>+>>>>>>[>+>[-<->]<[->+ 85 | <]>>>>>>>>]<<<<<<<<+<[>[->>>>+<<[->>-<<<<<<<<<<<<<+>>>>>>>>>>[->>>+<<<]>]<[->>>- 86 | <<<<<<<<<<<<<+>>>>>>>>>>]<]>>[->>+<<<[->>>-<<<<<<<<<<<<<+>>>>>>>>>>]>]<[->>>+<<< 87 | ]<<<<<<<<<<<]>>>>>[-]>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<+<<<<<]]>>>>[-<<<<+> 88 | >>>]<<<<[->>>>+>>>>>[>+>>[-<<->>]<<[->>+<<]>>>>>>>>]<<<<<<<<+<[>[->>>>+<<<[->>>- 89 | <<<<<<<<<<<<<+>>>>>>>>>>>[->>+<<]<]>[->>-<<<<<<<<<<<<<+>>>>>>>>>>>]<<]>[->>>+<<[ 90 | ->>-<<<<<<<<<<<<<+>>>>>>>>>>>]<]>[->>+<<]<<<<<<<<<<<<]]>>>>[-]<<<<]>>>>[-<<<<+>> 91 | >>]<<<<[->>>>+>[-]>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<+<<<<<]>>>>>>>>>[>>>>>> 92 | >>>]<<<<<<<<<[>[->>>>+<<<[->>>-<<<<<<<<<<<<<+>>>>>>>>>>>[->>+<<]<]>[->>-<<<<<<<< 93 | <<<<<+>>>>>>>>>>>]<<]>[->>>+<<[->>-<<<<<<<<<<<<<+>>>>>>>>>>>]<]>[->>+<<]<<<<<<<< 94 | <<<<]]>>>>>>>>>[>>[-]>[-]>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>[-]>[-]>>>>>[>>>>>[-<<<<+ 95 | >>>>]<<<<[->>>>+<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>>>>[-<<<<<+>>>>> 96 | ]<<<<<[->>>>>+<<<+<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>+++++++++++++++[[>>>> 97 | >>>>>]+>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+[>+>> 98 | >>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>[-<<<<+>>>>]<<<<[->>>>+<<<<<[->>[-<<+ 99 | >>]<<[->>+>>+<<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>>>>]<<<<<<<<<[> 100 | [->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<]>[->>>>>>>>>+<<<<<<<<<]<+>>>>>>>>]<<<<<<<<<[>[- 101 | ]<->>>>[-<<<<+>[<->-<<<<<<+>>>>>>]<[->+<]>>>>]<<<[->>>+<<<]<+<<<<<<<<<]>>>>>>>>> 102 | [>+>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>>[-<<<<<+>>>>>]<<<<<[->>>>>+<<<< 103 | <<[->>>[-<<<+>>>]<<<[->>>+>+<<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>> 104 | >>>]<<<<<<<<<[>>[->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<<]>>[->>>>>>>>>+<<<<<<<<<]<<+>>> 105 | >>>>>]<<<<<<<<<[>[-]<->>>>[-<<<<+>[<->-<<<<<<+>>>>>>]<[->+<]>>>>]<<<[->>>+<<<]<+ 106 | <<<<<<<<<]>>>>>>>>>[>>>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>> 107 | >>>>>>>>>>>>>>>>>>>]>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>+++++++++++++++[[>>>>>>>> 108 | >]<<<<<<<<<-<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+>>>>>>>>>>>>>>>>>>>>>+<<<[<<<<<<<<<] 109 | >>>>>>>>>[>>>[-<<<->>>]+<<<[->>>->[-<<<<+>>>>]<<<<[->>>>+<<<<<<<<<<<<<[<<<<<<<<< 110 | ]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>>>[-<<<<->>>>]+<<<<[->>>>-<[-<<<+>>>]<<<[->>>+< 111 | <<<<<<<<<<<[<<<<<<<<<]>>>[-]+>>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>>>>>>>>]<<<<<<<<]> 112 | >>>>>>>]<<<<<<<<<[<<<<<<<<<]>>->>[-<<<<+>>>>]<<<<[->>>>+<<[-]<<]>>]<<+>>>>[-<<<< 113 | ->>>>]+<<<<[->>>>-<<<<<<.>>]>>>>[-<<<<<<<.>>>>>>>]<<<[-]>[-]>[-]>[-]>[-]>[-]>>>[ 114 | >[-]>[-]>[-]>[-]>[-]>[-]>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>>>[-]>>>>]<<<<<<<<< 115 | [<<<<<<<<<]>+++++++++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>+>>>>>>>>>+<<<<<<<< 116 | <<<<<<[<<<<<<<<<]>>>>>>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+[-]>>[>>>>>>>>>]<<<<< 117 | <<<<[>>>>>>>[-<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<<<<<<[<<<<<<<<<]>>>>>>>[-]+>>>]<<<< 118 | <<<<<<]]>>>>>>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+>>[>+>>>>[-<<<<->>>>]<<<<[->>> 119 | >+<<<<]>>>>>>>>]<<+<<<<<<<[>>>>>[->>+<<]<<<<<<<<<<<<<<]>>>>>>>>>[>>>>>>>>>]<<<<< 120 | <<<<[>[-]<->>>>>>>[-<<<<<<<+>[<->-<<<+>>>]<[->+<]>>>>>>>]<<<<<<[->>>>>>+<<<<<<]< 121 | +<<<<<<<<<]>>>>>>>-<<<<[-]+<<<]+>>>>>>>[-<<<<<<<->>>>>>>]+<<<<<<<[->>>>>>>->>[>> 122 | >>>[->>+<<]>>>>]<<<<<<<<<[>[-]<->>>>>>>[-<<<<<<<+>[<->-<<<+>>>]<[->+<]>>>>>>>]<< 123 | <<<<[->>>>>>+<<<<<<]<+<<<<<<<<<]>+++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>+<<< 124 | <<[<<<<<<<<<]>>>>>>>>>[>>>>>[-<<<<<->>>>>]+<<<<<[->>>>>->>[-<<<<<<<+>>>>>>>]<<<< 125 | <<<[->>>>>>>+<<<<<<<<<<<<<<<<[<<<<<<<<<]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>>>>>>[-< 126 | <<<<<<->>>>>>>]+<<<<<<<[->>>>>>>-<<[-<<<<<+>>>>>]<<<<<[->>>>>+<<<<<<<<<<<<<<[<<< 127 | <<<<<<]>>>[-]+>>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>>>>>>>>]<<<<<<<<]>>>>>>>>]<<<<<<< 128 | <<[<<<<<<<<<]>>>>[-]<<<+++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>-<<<<<[<<<<<<< 129 | <<]]>>>]<<<<.>>>>>>>>>>[>>>>>>[-]>>>]<<<<<<<<<[<<<<<<<<<]>++++++++++[-[->>>>>>>> 130 | >+<<<<<<<<<]>>>>>>>>>]>>>>>+>>>>>>>>>+<<<<<<<<<<<<<<<[<<<<<<<<<]>>>>>>>>[-<<<<<< 131 | <<+>>>>>>>>]<<<<<<<<[->>>>>>>>+[-]>[>>>>>>>>>]<<<<<<<<<[>>>>>>>>[-<<<<<<<+>>>>>> 132 | >]<<<<<<<[->>>>>>>+<<<<<<<<[<<<<<<<<<]>>>>>>>>[-]+>>]<<<<<<<<<<]]>>>>>>>>[-<<<<< 133 | <<<+>>>>>>>>]<<<<<<<<[->>>>>>>>+>[>+>>>>>[-<<<<<->>>>>]<<<<<[->>>>>+<<<<<]>>>>>> 134 | >>]<+<<<<<<<<[>>>>>>[->>+<<]<<<<<<<<<<<<<<<]>>>>>>>>>[>>>>>>>>>]<<<<<<<<<[>[-]<- 135 | >>>>>>>>[-<<<<<<<<+>[<->-<<+>>]<[->+<]>>>>>>>>]<<<<<<<[->>>>>>>+<<<<<<<]<+<<<<<< 136 | <<<]>>>>>>>>-<<<<<[-]+<<<]+>>>>>>>>[-<<<<<<<<->>>>>>>>]+<<<<<<<<[->>>>>>>>->[>>> 137 | >>>[->>+<<]>>>]<<<<<<<<<[>[-]<->>>>>>>>[-<<<<<<<<+>[<->-<<+>>]<[->+<]>>>>>>>>]<< 138 | <<<<<[->>>>>>>+<<<<<<<]<+<<<<<<<<<]>+++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>> 139 | +>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>>>>[-<<<<<<->>>>>>]+< 140 | <<<<<[->>>>>>->>[-<<<<<<<<+>>>>>>>>]<<<<<<<<[->>>>>>>>+<<<<<<<<<<<<<<<<<[<<<<<<< 141 | <<]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>>>>>>>[-<<<<<<<<->>>>>>>>]+<<<<<<<<[->>>>>>>> 142 | -<<[-<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<<<<<<<<<<<<<<[<<<<<<<<<]>>>[-]+>>>>>>[>>>>>> 143 | >>>]>[-]+<]]+>[-<[>>>>>>>>>]<<<<<<<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>[-]<<<++++ 144 | +[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>>->>>>>>>>>>>>>>>>>>>>>>>>>>>-<<<<<<[<<<< 145 | <<<<<]]>>>] 146 | -------------------------------------------------------------------------------- /optimized-jit/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Read, Write}; 2 | use std::process::ExitCode; 3 | 4 | use dynasmrt::mmap::MutableBuffer; 5 | use dynasmrt::{dynasm, x64::X64Relocation, DynasmApi, DynasmLabelApi, VecAssembler}; 6 | 7 | #[derive(PartialEq, Eq, Clone, Copy)] 8 | enum Instruction { 9 | Add(i8), 10 | Move(i32), 11 | Input, 12 | Output, 13 | JumpRight, 14 | JumpLeft, 15 | Clear, 16 | AddTo(i32), 17 | MoveUntil(i32), 18 | } 19 | 20 | struct UnbalancedBrackets(char, usize); 21 | 22 | struct Program { 23 | code: Vec, 24 | memory: [u8; 30_000], 25 | } 26 | impl Program { 27 | fn new(source: &[u8]) -> Result { 28 | let mut code: VecAssembler = VecAssembler::new(0); 29 | 30 | let mut instructions = Vec::new(); 31 | 32 | for b in source { 33 | let instr = match b { 34 | b'+' | b'-' => { 35 | let inc = if *b == b'+' { 1 } else { -1 }; 36 | if let Some(Instruction::Add(value)) = instructions.last_mut() { 37 | *value = value.wrapping_add(inc); 38 | continue; 39 | } 40 | Instruction::Add(inc) 41 | } 42 | b'.' => Instruction::Output, 43 | b',' => Instruction::Input, 44 | b'>' | b'<' => { 45 | let inc = if *b == b'>' { 1 } else { -1 }; 46 | if let Some(Instruction::Move(value)) = instructions.last_mut() { 47 | *value += inc; 48 | continue; 49 | } 50 | Instruction::Move(inc) 51 | } 52 | b'[' => Instruction::JumpRight, 53 | b']' => { 54 | use Instruction::*; 55 | match instructions.as_slice() { 56 | // could enter a infinite loop if n is even. 57 | [.., JumpRight, Add(n)] if n % 2 == 1 => { 58 | let len = instructions.len(); 59 | instructions.drain(len - 2..); 60 | Instruction::Clear 61 | } 62 | &[.., JumpRight, Add(-1), Move(x), Add(1), Move(y)] if x == -y => { 63 | let len = instructions.len(); 64 | instructions.drain(len - 5..); 65 | Instruction::AddTo(x) 66 | } 67 | &[.., JumpRight, Move(n)] => { 68 | let len = instructions.len(); 69 | instructions.drain(len - 2..); 70 | Instruction::MoveUntil(n) 71 | } 72 | _ => Instruction::JumpLeft, 73 | } 74 | } 75 | _ => continue, 76 | }; 77 | instructions.push(instr); 78 | } 79 | 80 | // r12 will be the adress of `memory` 81 | // r13 will be the value of `pointer` 82 | // r12 is got from argument 1 in `rdi` 83 | // r13 is set to 0 84 | dynasm! { code 85 | ; .arch x64 86 | ; push rbp 87 | ; mov rbp, rsp 88 | ; push r12 89 | ; push r13 90 | ; mov r12, rdi 91 | ; xor r13, r13 92 | }; 93 | 94 | let mut bracket_stack = Vec::new(); 95 | 96 | for instr in instructions.into_iter() { 97 | match instr { 98 | Instruction::Add(n) => dynasm! { code 99 | ; .arch x64 100 | ; add BYTE [r12 + r13], BYTE n as i8 101 | }, 102 | Instruction::Move(n) => { 103 | if n > 0 { 104 | dynasm! { code 105 | ; lea eax, [r13 + n] 106 | ; add r13, -(30000 - n) 107 | ; cmp eax, 30000 108 | ; cmovl r13d, eax 109 | } 110 | } else { 111 | dynasm! { code 112 | ; lea eax, [r13 + n] 113 | ; add r13d, 30000 + n 114 | ; test eax, eax 115 | ; cmovns r13d, eax 116 | } 117 | } 118 | } 119 | Instruction::Input => { 120 | dynasm! { code 121 | ; .arch x64 122 | ; mov rax, QWORD read as *const () as i64 123 | ; lea rdi, [r12 + r13] // cell address 124 | ; call rax 125 | ; cmp rax, 0 126 | ; jne ->exit 127 | } 128 | } 129 | Instruction::Output => { 130 | dynasm! { code 131 | ; .arch x64 132 | ; mov rax, QWORD write as *const () as i64 133 | ; mov rdi, [r12 + r13] // cell value 134 | ; call rax 135 | ; cmp rax, 0 136 | ; jne ->exit 137 | } 138 | } 139 | Instruction::JumpRight => { 140 | let start_label = code.new_dynamic_label(); 141 | let end_label = code.new_dynamic_label(); 142 | dynasm! { code 143 | ; .arch x64 144 | ; cmp BYTE [r12+r13], 0 145 | ; je =>end_label 146 | ; =>start_label 147 | }; 148 | 149 | bracket_stack.push((start_label, end_label)); 150 | } 151 | Instruction::JumpLeft => { 152 | let (start_label, end_label) = match bracket_stack.pop() { 153 | Some(x) => x, 154 | None => return Err(UnbalancedBrackets(']', code.offset().0)), 155 | }; 156 | 157 | dynasm! { code 158 | ; .arch x64 159 | ; cmp BYTE [r12 + r13], 0 160 | ; jne =>start_label 161 | ; => end_label 162 | }; 163 | } 164 | Instruction::Clear => dynasm! { code 165 | ; .arch x64 166 | ; mov BYTE [r12 + r13], 0 167 | }, 168 | Instruction::AddTo(n) => dynasm! { code 169 | ; .arch x64 170 | // rax = cell to add to 171 | ;; 172 | if n > 0 { 173 | dynasm! { code 174 | ; lea ecx, [r13 + n] 175 | ; lea eax, [r13 + n - 30000] 176 | ; cmp ecx, 30000 177 | ; cmovl eax, ecx 178 | } 179 | } else { 180 | dynasm! { code 181 | ; lea ecx, [r13 + n] 182 | ; lea eax, [r13 + 30000 + n] 183 | ; test ecx, ecx 184 | ; cmovns eax, ecx 185 | } 186 | } 187 | ; mov cl, [r12 + r13] 188 | ; add BYTE [r12 + rax], cl 189 | ; mov BYTE [r12 + r13], 0 190 | }, 191 | Instruction::MoveUntil(n) => dynasm! { code 192 | ; .arch x64 193 | 194 | ; repeat: 195 | 196 | // check if 0 197 | ; cmp BYTE [r12 + r13], 0 198 | ; je >exit 199 | 200 | // Move n 201 | ;; 202 | if n > 0 { 203 | dynasm! { code 204 | ; lea eax, [r13 + n] 205 | ; add r13, -(30000 - n) 206 | ; cmp eax, 30000 207 | ; cmovl r13d, eax 208 | } 209 | } else { 210 | dynasm! { code 211 | ; lea eax, [r13 + n] 212 | ; add r13d, 30000 + n 213 | ; test eax, eax 214 | ; cmovns r13d, eax 215 | } 216 | } 217 | 218 | ; jmp exit: 235 | ; pop r13 236 | ; pop r12 237 | ; pop rbp 238 | ; ret 239 | } 240 | 241 | Ok(Program { 242 | code: code.finalize().unwrap(), 243 | memory: [0; 30_000], 244 | }) 245 | } 246 | 247 | fn run(&mut self) -> std::io::Result<()> { 248 | let mut buffer = MutableBuffer::new(self.code.len()).unwrap(); 249 | buffer.set_len(self.code.len()); 250 | 251 | buffer.copy_from_slice(&self.code); 252 | 253 | let buffer = buffer.make_exec().unwrap(); 254 | 255 | unsafe { 256 | let code_fn: unsafe extern "sysv64" fn(*mut u8) -> *mut std::io::Error = 257 | std::mem::transmute(buffer.as_ptr()); 258 | 259 | let error = code_fn(self.memory.as_mut_ptr()); 260 | 261 | if !error.is_null() { 262 | return Err(*Box::from_raw(error)); 263 | } 264 | } 265 | 266 | Ok(()) 267 | } 268 | } 269 | 270 | extern "sysv64" fn write(value: u8) -> *mut std::io::Error { 271 | // Writing a non-UTF-8 byte sequence on Windows error out. 272 | if cfg!(target_os = "windows") && value >= 128 { 273 | return std::ptr::null_mut(); 274 | } 275 | 276 | let mut stdout = std::io::stdout().lock(); 277 | 278 | let result = stdout.write_all(&[value]).and_then(|_| stdout.flush()); 279 | 280 | match result { 281 | Err(err) => Box::into_raw(Box::new(err)), 282 | _ => std::ptr::null_mut(), 283 | } 284 | } 285 | 286 | unsafe extern "sysv64" fn read(buf: *mut u8) -> *mut std::io::Error { 287 | let mut stdin = std::io::stdin().lock(); 288 | loop { 289 | let mut value = 0; 290 | let err = stdin.read_exact(std::slice::from_mut(&mut value)); 291 | 292 | if let Err(err) = err { 293 | if err.kind() != std::io::ErrorKind::UnexpectedEof { 294 | return Box::into_raw(Box::new(err)); 295 | } 296 | value = 0; 297 | } 298 | 299 | // ignore CR from Window's CRLF 300 | if cfg!(target_os = "windows") && value == b'\r' { 301 | continue; 302 | } 303 | 304 | *buf = value; 305 | 306 | return std::ptr::null_mut(); 307 | } 308 | } 309 | 310 | fn main() -> ExitCode { 311 | let mut args = std::env::args(); 312 | if args.len() != 2 { 313 | eprintln!("expected a single file path as argument"); 314 | return ExitCode::from(1); 315 | } 316 | 317 | let file_name = args.nth(1).unwrap(); 318 | let source = match std::fs::read(&file_name) { 319 | Ok(x) => x, 320 | Err(err) => { 321 | eprintln!("Error reading '{}': {}", file_name, err); 322 | return ExitCode::from(2); 323 | } 324 | }; 325 | 326 | let mut program = match Program::new(&source) { 327 | Ok(x) => x, 328 | Err(UnbalancedBrackets(c, address)) => { 329 | eprintln!( 330 | "Error parsing file: didn't found pair for `{}` at instruction index {}", 331 | c, address 332 | ); 333 | return ExitCode::from(3); 334 | } 335 | }; 336 | 337 | if let Err(err) = program.run() { 338 | eprintln!("IO error: {}", err); 339 | } 340 | 341 | ExitCode::from(0) 342 | } 343 | -------------------------------------------------------------------------------- /optimized/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::{Read, Write}, 3 | process::ExitCode, 4 | }; 5 | 6 | #[derive(PartialEq, Eq, Clone, Copy)] 7 | enum Instruction { 8 | Add(u8), 9 | Move(isize), 10 | Input, 11 | Output, 12 | JumpRight(usize), 13 | JumpLeft(usize), 14 | Clear, 15 | AddTo(isize), 16 | MoveUntil(isize), 17 | } 18 | 19 | struct UnbalancedBrackets(char, usize); 20 | 21 | #[derive(Default, Debug)] 22 | #[cfg(feature = "profile")] 23 | struct Profile { 24 | add: u64, 25 | mov: u64, 26 | jr: u64, 27 | jl: u64, 28 | inp: u64, 29 | out: u64, 30 | clear: u64, 31 | addto: u64, 32 | movuntil: u64, 33 | loops: std::collections::HashMap, usize>, 34 | } 35 | 36 | struct Program { 37 | program_counter: usize, 38 | pointer: usize, 39 | instructions: Vec, 40 | memory: [u8; 30_000], 41 | #[cfg(feature = "profile")] 42 | profile: Profile, 43 | } 44 | impl Program { 45 | fn new(source: &[u8]) -> Result { 46 | let mut instructions = Vec::new(); 47 | let mut bracket_stack = Vec::new(); 48 | 49 | for b in source { 50 | let instr = match b { 51 | b'+' | b'-' => { 52 | let inc = if *b == b'+' { 1 } else { 1u8.wrapping_neg() }; 53 | if let Some(Instruction::Add(value)) = instructions.last_mut() { 54 | *value = value.wrapping_add(inc); 55 | continue; 56 | } 57 | Instruction::Add(inc) 58 | } 59 | b'.' => Instruction::Output, 60 | b',' => Instruction::Input, 61 | b'>' | b'<' => { 62 | let inc = if *b == b'>' { 1 } else { -1 }; 63 | if let Some(Instruction::Move(value)) = instructions.last_mut() { 64 | *value += inc; 65 | continue; 66 | } 67 | Instruction::Move(inc) 68 | } 69 | b'[' => { 70 | let curr_address = instructions.len(); 71 | bracket_stack.push(curr_address); 72 | // will be fixup at the pair ']'. 73 | Instruction::JumpRight(0) 74 | } 75 | b']' => { 76 | let curr_address = instructions.len(); 77 | match bracket_stack.pop() { 78 | Some(pair_address) => { 79 | instructions[pair_address] = Instruction::JumpRight(curr_address); 80 | 81 | use Instruction::*; 82 | match instructions.as_slice() { 83 | // could enter a infinite loop if n is even. 84 | [.., JumpRight(_), Add(n)] if n % 2 == 1 => { 85 | let len = instructions.len(); 86 | instructions.drain(len - 2..); 87 | Instruction::Clear 88 | } 89 | &[.., JumpRight(_), Add(255), Move(x), Add(1), Move(y)] 90 | if x == -y => 91 | { 92 | let len = instructions.len(); 93 | instructions.drain(len - 5..); 94 | Instruction::AddTo(x) 95 | } 96 | &[.., JumpRight(_), Move(n)] => { 97 | let len = instructions.len(); 98 | instructions.drain(len - 2..); 99 | Instruction::MoveUntil(n) 100 | } 101 | _ => Instruction::JumpLeft(pair_address), 102 | } 103 | } 104 | None => return Err(UnbalancedBrackets(']', curr_address)), 105 | } 106 | } 107 | _ => continue, 108 | }; 109 | instructions.push(instr); 110 | } 111 | 112 | if let Some(unpaired_bracket) = bracket_stack.pop() { 113 | return Err(UnbalancedBrackets('[', unpaired_bracket)); 114 | } 115 | 116 | Ok(Program { 117 | program_counter: 0, 118 | pointer: 0, 119 | instructions, 120 | memory: [0; 30_000], 121 | #[cfg(feature = "profile")] 122 | profile: Profile::default(), 123 | }) 124 | } 125 | 126 | fn run(&mut self) -> std::io::Result<()> { 127 | let mut stdout = std::io::stdout().lock(); 128 | let mut stdin = std::io::stdin().lock(); 129 | 'program: loop { 130 | use Instruction::*; 131 | 132 | #[cfg(feature = "profile")] 133 | { 134 | match self.instructions[self.program_counter] { 135 | Add(_) => self.profile.add += 1, 136 | Output => self.profile.out += 1, 137 | Input => self.profile.inp += 1, 138 | Move(_) => self.profile.mov += 1, 139 | JumpRight(_) => self.profile.jr += 1, 140 | JumpLeft(pair) => { 141 | self.profile.jl += 1; 142 | *self 143 | .profile 144 | .loops 145 | .entry(pair..self.program_counter + 1) 146 | .or_default() += 1; 147 | } 148 | Clear => self.profile.clear += 1, 149 | AddTo(_) => self.profile.addto += 1, 150 | MoveUntil(_) => self.profile.movuntil += 1, 151 | } 152 | } 153 | 154 | match self.instructions[self.program_counter] { 155 | Add(n) => self.memory[self.pointer] = self.memory[self.pointer].wrapping_add(n), 156 | Output => { 157 | let value = self.memory[self.pointer]; 158 | // Writing a non-UTF-8 byte sequence on Windows error out. 159 | if !cfg!(target_os = "windows") || value < 128 { 160 | stdout.write_all(&[value])?; 161 | stdout.flush()?; 162 | } 163 | } 164 | Input => loop { 165 | let err = stdin.read_exact(&mut self.memory[self.pointer..self.pointer + 1]); 166 | match err.as_ref().map_err(|e| e.kind()) { 167 | Err(std::io::ErrorKind::UnexpectedEof) => { 168 | self.memory[self.pointer] = 0; 169 | } 170 | _ => err?, 171 | } 172 | if cfg!(target_os = "windows") && self.memory[self.pointer] == b'\r' { 173 | continue; 174 | } 175 | break; 176 | }, 177 | Move(n) => { 178 | let len = self.memory.len() as isize; 179 | let n = (len + n % len) as usize; 180 | self.pointer = (self.pointer + n) % len as usize; 181 | } 182 | JumpRight(pair_address) => { 183 | if self.memory[self.pointer] == 0 { 184 | self.program_counter = pair_address; 185 | } 186 | } 187 | JumpLeft(pair_address) => { 188 | if self.memory[self.pointer] != 0 { 189 | self.program_counter = pair_address; 190 | } 191 | } 192 | Clear => self.memory[self.pointer] = 0, 193 | AddTo(n) => { 194 | let len = self.memory.len() as isize; 195 | let n = (len + n % len) as usize; 196 | let to = (self.pointer + n) % len as usize; 197 | 198 | self.memory[to] = self.memory[to].wrapping_add(self.memory[self.pointer]); 199 | self.memory[self.pointer] = 0 200 | } 201 | MoveUntil(n) => { 202 | let len = self.memory.len() as isize; 203 | let n = (len + n % len) as usize; 204 | loop { 205 | if self.memory[self.pointer] == 0 { 206 | break; 207 | } 208 | 209 | self.pointer = (self.pointer + n) % len as usize; 210 | } 211 | } 212 | } 213 | self.program_counter += 1; 214 | 215 | if self.instructions.len() == self.program_counter { 216 | break 'program; 217 | } 218 | } 219 | Ok(()) 220 | } 221 | } 222 | 223 | fn main() -> ExitCode { 224 | let mut args = std::env::args(); 225 | if args.len() != 2 { 226 | eprintln!("expected a single file path as argument"); 227 | return ExitCode::from(1); 228 | } 229 | 230 | let file_name = args.nth(1).unwrap(); 231 | let source = match std::fs::read(&file_name) { 232 | Ok(x) => x, 233 | Err(err) => { 234 | eprintln!("Error reading '{}': {}", file_name, err); 235 | return ExitCode::from(2); 236 | } 237 | }; 238 | 239 | let mut program = match Program::new(&source) { 240 | Ok(x) => x, 241 | Err(UnbalancedBrackets(c, address)) => { 242 | eprintln!( 243 | "Error parsing file: didn't found pair for `{}` at instruction index {}", 244 | c, address 245 | ); 246 | return ExitCode::from(3); 247 | } 248 | }; 249 | 250 | if let Err(err) = program.run() { 251 | eprintln!("IO error: {}", err); 252 | } 253 | 254 | #[cfg(feature = "profile")] 255 | { 256 | let profile = std::mem::take(&mut program.profile); 257 | println!("profile:"); 258 | println!(" +: {}", profile.add); 259 | println!(" >: {}", profile.mov); 260 | println!(" [: {}", profile.jr); 261 | println!(" ]: {}", profile.jl); 262 | println!(" .: {}", profile.out); 263 | println!(" ,: {}", profile.inp); 264 | println!(" x: {}", profile.clear); 265 | println!("+>: {}", profile.addto); 266 | println!(">>: {}", profile.movuntil); 267 | println!("loops:"); 268 | 269 | let to_string = |range: std::ops::Range| -> String { 270 | program.instructions[range] 271 | .iter() 272 | .map(|x| match x { 273 | Instruction::Add(n) => { 274 | if *n >= 128 { 275 | format!("-{}", n.wrapping_neg()) 276 | } else { 277 | format!("+{}", n) 278 | } 279 | } 280 | Instruction::Move(n) => { 281 | if *n < 0 { 282 | format!("<{}", -n) 283 | } else { 284 | format!(">{}", n) 285 | } 286 | } 287 | Instruction::Input => ",".to_string(), 288 | Instruction::Output => ".".to_string(), 289 | Instruction::JumpRight(_) => "[".to_string(), 290 | Instruction::JumpLeft(_) => "]".to_string(), 291 | Instruction::Clear => "x".to_string(), 292 | Instruction::AddTo(n) => { 293 | if *n < 0 { 294 | format!("+<{}", -n) 295 | } else { 296 | format!("+>{}", n) 297 | } 298 | } 299 | Instruction::MoveUntil(n) => { 300 | if *n < 0 { 301 | format!("<<{}", -n) 302 | } else { 303 | format!(">>{}", n) 304 | } 305 | } 306 | }) 307 | .fold(String::new(), |a, b| a + &b) 308 | }; 309 | 310 | let mut loops: Vec<_> = profile 311 | .loops 312 | .into_iter() 313 | .map(|(range, count)| (to_string(range), count)) 314 | .collect(); 315 | 316 | // dedup identical code 317 | 318 | loops.sort_by(|a, b| a.0.cmp(&b.0)); 319 | 320 | for i in 1..loops.len() { 321 | if loops[i - 1].0 == loops[i].0 { 322 | loops[i].1 += loops[i - 1].1; 323 | loops[i - 1].1 = 0; // mark to remove 324 | } 325 | } 326 | 327 | loops.retain(|x| x.1 > 0); 328 | 329 | // sort by count 330 | loops.sort_by_key(|x| x.1); 331 | 332 | for (code, count) in loops.into_iter().rev().take(20) { 333 | println!("{:10}: {}", count, code); 334 | } 335 | } 336 | 337 | ExitCode::from(0) 338 | } 339 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "adler" 7 | version = "1.0.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 10 | 11 | [[package]] 12 | name = "ahash" 13 | version = "0.8.2" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107" 16 | dependencies = [ 17 | "cfg-if", 18 | "once_cell", 19 | "version_check", 20 | ] 21 | 22 | [[package]] 23 | name = "arrayvec" 24 | version = "0.7.2" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" 27 | 28 | [[package]] 29 | name = "autocfg" 30 | version = "1.1.0" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 33 | 34 | [[package]] 35 | name = "bf-cranelift-jit" 36 | version = "0.1.0" 37 | dependencies = [ 38 | "cranelift", 39 | "libc", 40 | "memmap2", 41 | "target-lexicon", 42 | ] 43 | 44 | [[package]] 45 | name = "bf-interpreter" 46 | version = "0.1.0" 47 | 48 | [[package]] 49 | name = "bf-optimized" 50 | version = "0.1.0" 51 | 52 | [[package]] 53 | name = "bf-optimized-jit" 54 | version = "0.1.0" 55 | dependencies = [ 56 | "dynasmrt", 57 | ] 58 | 59 | [[package]] 60 | name = "bf-singlepass-jit" 61 | version = "0.1.0" 62 | dependencies = [ 63 | "dynasmrt", 64 | ] 65 | 66 | [[package]] 67 | name = "bitflags" 68 | version = "1.3.2" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 71 | 72 | [[package]] 73 | name = "bumpalo" 74 | version = "3.11.1" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" 77 | 78 | [[package]] 79 | name = "byteorder" 80 | version = "1.4.3" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 83 | 84 | [[package]] 85 | name = "cfg-if" 86 | version = "1.0.0" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 89 | 90 | [[package]] 91 | name = "cranelift" 92 | version = "0.89.2" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "a3d7538c511c4eb16ee38bf2ae5461bb41736bb2807e7e0868bd53429fe10da0" 95 | dependencies = [ 96 | "cranelift-codegen", 97 | "cranelift-frontend", 98 | ] 99 | 100 | [[package]] 101 | name = "cranelift-bforest" 102 | version = "0.89.2" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "593b398dd0c5b1e2e3a9c3dae8584e287894ea84e361949ad506376e99196265" 105 | dependencies = [ 106 | "cranelift-entity", 107 | ] 108 | 109 | [[package]] 110 | name = "cranelift-codegen" 111 | version = "0.89.2" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "afc0d8faabd099ea15ab33d49d150e5572c04cfeb95d675fd41286739b754629" 114 | dependencies = [ 115 | "arrayvec", 116 | "bumpalo", 117 | "cranelift-bforest", 118 | "cranelift-codegen-meta", 119 | "cranelift-codegen-shared", 120 | "cranelift-entity", 121 | "cranelift-isle", 122 | "gimli", 123 | "log", 124 | "regalloc2", 125 | "smallvec", 126 | "target-lexicon", 127 | ] 128 | 129 | [[package]] 130 | name = "cranelift-codegen-meta" 131 | version = "0.89.2" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "1ac1669e42579476f001571d6ba4b825fac686282c97b88b18f8e34242066a81" 134 | dependencies = [ 135 | "cranelift-codegen-shared", 136 | ] 137 | 138 | [[package]] 139 | name = "cranelift-codegen-shared" 140 | version = "0.89.2" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "e2a1b1eef9640ab72c1e7b583ac678083855a509da34b4b4378bd99954127c20" 143 | 144 | [[package]] 145 | name = "cranelift-entity" 146 | version = "0.89.2" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "eea4e17c3791fd8134640b26242a9ddbd7c67db78f0bad98cb778bf563ef81a0" 149 | 150 | [[package]] 151 | name = "cranelift-example" 152 | version = "0.1.0" 153 | dependencies = [ 154 | "cranelift", 155 | "cranelift-codegen", 156 | "memmap2", 157 | "target-lexicon", 158 | ] 159 | 160 | [[package]] 161 | name = "cranelift-frontend" 162 | version = "0.89.2" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "fca1474b5302348799656d43a40eacd716a3b46169405a3af812832c9edf77b4" 165 | dependencies = [ 166 | "cranelift-codegen", 167 | "log", 168 | "smallvec", 169 | "target-lexicon", 170 | ] 171 | 172 | [[package]] 173 | name = "cranelift-isle" 174 | version = "0.89.2" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "77aa537f020ea43483100153278e7215d41695bdcef9eea6642d122675f64249" 177 | 178 | [[package]] 179 | name = "crc32fast" 180 | version = "1.3.2" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" 183 | dependencies = [ 184 | "cfg-if", 185 | ] 186 | 187 | [[package]] 188 | name = "dynasm" 189 | version = "1.2.3" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" 192 | dependencies = [ 193 | "bitflags", 194 | "byteorder", 195 | "lazy_static", 196 | "proc-macro-error", 197 | "proc-macro2", 198 | "quote", 199 | "syn", 200 | ] 201 | 202 | [[package]] 203 | name = "dynasmrt" 204 | version = "1.2.3" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" 207 | dependencies = [ 208 | "byteorder", 209 | "dynasm", 210 | "memmap2", 211 | ] 212 | 213 | [[package]] 214 | name = "fallible-iterator" 215 | version = "0.2.0" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" 218 | 219 | [[package]] 220 | name = "flate2" 221 | version = "1.0.25" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" 224 | dependencies = [ 225 | "crc32fast", 226 | "miniz_oxide", 227 | ] 228 | 229 | [[package]] 230 | name = "fxhash" 231 | version = "0.2.1" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" 234 | dependencies = [ 235 | "byteorder", 236 | ] 237 | 238 | [[package]] 239 | name = "gimli" 240 | version = "0.26.2" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" 243 | dependencies = [ 244 | "fallible-iterator", 245 | "indexmap", 246 | "stable_deref_trait", 247 | ] 248 | 249 | [[package]] 250 | name = "hashbrown" 251 | version = "0.12.3" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 254 | 255 | [[package]] 256 | name = "hashbrown" 257 | version = "0.13.1" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "33ff8ae62cd3a9102e5637afc8452c55acf3844001bd5374e0b0bd7b6616c038" 260 | dependencies = [ 261 | "ahash", 262 | ] 263 | 264 | [[package]] 265 | name = "indexmap" 266 | version = "1.9.1" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" 269 | dependencies = [ 270 | "autocfg", 271 | "hashbrown 0.12.3", 272 | ] 273 | 274 | [[package]] 275 | name = "lazy_static" 276 | version = "1.4.0" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 279 | 280 | [[package]] 281 | name = "libc" 282 | version = "0.2.137" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" 285 | 286 | [[package]] 287 | name = "log" 288 | version = "0.4.17" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 291 | dependencies = [ 292 | "cfg-if", 293 | ] 294 | 295 | [[package]] 296 | name = "memchr" 297 | version = "2.5.0" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 300 | 301 | [[package]] 302 | name = "memmap2" 303 | version = "0.5.8" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc" 306 | dependencies = [ 307 | "libc", 308 | ] 309 | 310 | [[package]] 311 | name = "miniz_oxide" 312 | version = "0.6.2" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" 315 | dependencies = [ 316 | "adler", 317 | ] 318 | 319 | [[package]] 320 | name = "object" 321 | version = "0.30.0" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "239da7f290cfa979f43f85a8efeee9a8a76d0827c356d37f9d3d7254d6b537fb" 324 | dependencies = [ 325 | "crc32fast", 326 | "flate2", 327 | "hashbrown 0.13.1", 328 | "indexmap", 329 | "memchr", 330 | ] 331 | 332 | [[package]] 333 | name = "object-example" 334 | version = "0.1.0" 335 | dependencies = [ 336 | "dynasmrt", 337 | "memmap2", 338 | "object", 339 | ] 340 | 341 | [[package]] 342 | name = "once_cell" 343 | version = "1.16.0" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" 346 | 347 | [[package]] 348 | name = "proc-macro-error" 349 | version = "1.0.4" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 352 | dependencies = [ 353 | "proc-macro-error-attr", 354 | "proc-macro2", 355 | "quote", 356 | "syn", 357 | "version_check", 358 | ] 359 | 360 | [[package]] 361 | name = "proc-macro-error-attr" 362 | version = "1.0.4" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 365 | dependencies = [ 366 | "proc-macro2", 367 | "quote", 368 | "version_check", 369 | ] 370 | 371 | [[package]] 372 | name = "proc-macro2" 373 | version = "1.0.47" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" 376 | dependencies = [ 377 | "unicode-ident", 378 | ] 379 | 380 | [[package]] 381 | name = "quote" 382 | version = "1.0.21" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 385 | dependencies = [ 386 | "proc-macro2", 387 | ] 388 | 389 | [[package]] 390 | name = "regalloc2" 391 | version = "0.4.2" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "91b2eab54204ea0117fe9a060537e0b07a4e72f7c7d182361ecc346cab2240e5" 394 | dependencies = [ 395 | "fxhash", 396 | "log", 397 | "slice-group-by", 398 | "smallvec", 399 | ] 400 | 401 | [[package]] 402 | name = "singlepass-compiler" 403 | version = "0.1.0" 404 | dependencies = [ 405 | "dynasmrt", 406 | "object", 407 | ] 408 | 409 | [[package]] 410 | name = "slice-group-by" 411 | version = "0.3.0" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" 414 | 415 | [[package]] 416 | name = "smallvec" 417 | version = "1.10.0" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" 420 | 421 | [[package]] 422 | name = "stable_deref_trait" 423 | version = "1.2.0" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 426 | 427 | [[package]] 428 | name = "syn" 429 | version = "1.0.103" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" 432 | dependencies = [ 433 | "proc-macro2", 434 | "quote", 435 | "unicode-ident", 436 | ] 437 | 438 | [[package]] 439 | name = "target-lexicon" 440 | version = "0.12.5" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d" 443 | 444 | [[package]] 445 | name = "unicode-ident" 446 | version = "1.0.5" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" 449 | 450 | [[package]] 451 | name = "version_check" 452 | version = "0.9.4" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 455 | -------------------------------------------------------------------------------- /cranelift-jit/src/main.rs: -------------------------------------------------------------------------------- 1 | use cranelift::{ 2 | codegen::{ 3 | entity::EntityRef, 4 | ir::{ 5 | condcodes::IntCC, types::I8, AbiParam, Function, InstBuilder, MemFlags, Signature, 6 | UserFuncName, 7 | }, 8 | isa::{self, CallConv}, 9 | settings::{self, Configurable}, 10 | verify_function, Context, 11 | }, 12 | frontend::{FunctionBuilder, FunctionBuilderContext, Variable}, 13 | }; 14 | use std::{ 15 | io::{Read, Write}, 16 | process::ExitCode, 17 | }; 18 | use target_lexicon::Triple; 19 | 20 | #[derive(PartialEq, Eq, Clone, Copy)] 21 | enum Instruction { 22 | Add(i8), 23 | Move(i32), 24 | Input, 25 | Output, 26 | JumpRight, 27 | JumpLeft, 28 | Clear, 29 | AddTo(i32), 30 | // The MoveUntil was removed, because it does not offer such a better implementation 31 | } 32 | 33 | struct UnbalancedBrackets(char, usize); 34 | 35 | struct Program { 36 | code: Vec, 37 | memory: [u8; 30_000], 38 | } 39 | impl Program { 40 | fn new(source: &[u8], clir: bool) -> Result { 41 | let mut instructions = Vec::new(); 42 | 43 | for b in source { 44 | let instr = match b { 45 | b'+' | b'-' => { 46 | let inc = if *b == b'+' { 1 } else { -1 }; 47 | if let Some(Instruction::Add(value)) = instructions.last_mut() { 48 | *value = value.wrapping_add(inc); 49 | continue; 50 | } 51 | Instruction::Add(inc) 52 | } 53 | b'.' => Instruction::Output, 54 | b',' => Instruction::Input, 55 | b'>' | b'<' => { 56 | let inc = if *b == b'>' { 1 } else { -1 }; 57 | if let Some(Instruction::Move(value)) = instructions.last_mut() { 58 | *value += inc; 59 | continue; 60 | } 61 | Instruction::Move(inc) 62 | } 63 | b'[' => Instruction::JumpRight, 64 | b']' => { 65 | use Instruction::*; 66 | match instructions.as_slice() { 67 | // could enter a infinite loop if n is even. 68 | [.., JumpRight, Add(n)] if *n as u8 % 2 == 1 => { 69 | let len = instructions.len(); 70 | instructions.drain(len - 2..); 71 | Instruction::Clear 72 | } 73 | &[.., JumpRight, Add(-1), Move(x), Add(1), Move(y)] if x == -y => { 74 | let len = instructions.len(); 75 | instructions.drain(len - 5..); 76 | Instruction::AddTo(x) 77 | } 78 | _ => Instruction::JumpLeft, 79 | } 80 | } 81 | _ => continue, 82 | }; 83 | instructions.push(instr); 84 | } 85 | 86 | // possible settings: https://docs.rs/cranelift-codegen/latest/src/cranelift_codegen/opt/rustwide/target/x86_64-unknown-linux-gnu/debug/build/cranelift-codegen-b5deaeb0cd154533/out/settings.rs.html#490-664 87 | let mut builder = settings::builder(); 88 | builder.set("opt_level", "speed").unwrap(); 89 | // issue: https://github.com/bytecodealliance/wasmtime/issues/1148 90 | builder.set("preserve_frame_pointers", "false").unwrap(); 91 | // builder.set("use_egraphs", "true").unwrap(); 92 | 93 | let flags = settings::Flags::new(builder); 94 | 95 | let isa = match isa::lookup(Triple::host()) { 96 | Err(_) => panic!("x86_64 ISA is not avaliable"), 97 | Ok(isa_builder) => isa_builder.finish(flags).unwrap(), 98 | }; 99 | 100 | let pointer_type = isa.pointer_type(); 101 | 102 | let call_conv = CallConv::triple_default(isa.triple()); 103 | 104 | // get memory address parameter, and return pointer to io::Error 105 | let mut sig = Signature::new(call_conv); 106 | sig.params.push(AbiParam::new(pointer_type)); 107 | sig.returns.push(AbiParam::new(pointer_type)); 108 | 109 | let mut func = Function::with_name_signature(UserFuncName::user(0, 0), sig); 110 | 111 | let mut func_ctx = FunctionBuilderContext::new(); 112 | let mut builder = FunctionBuilder::new(&mut func, &mut func_ctx); 113 | 114 | let pointer = Variable::new(0); 115 | builder.declare_var(pointer, pointer_type); 116 | 117 | let exit_block = builder.create_block(); 118 | builder.append_block_param(exit_block, pointer_type); 119 | 120 | let block = builder.create_block(); 121 | builder.seal_block(block); 122 | 123 | builder.append_block_params_for_function_params(block); 124 | builder.switch_to_block(block); 125 | 126 | let memory_address = builder.block_params(block)[0]; 127 | 128 | let zero_byte = builder.ins().iconst(I8, 0); 129 | let zero = builder.ins().iconst(pointer_type, 0); 130 | builder.def_var(pointer, zero); 131 | 132 | let mem_flags = MemFlags::new(); //.with_notrap().with_heap(); 133 | 134 | let (write_sig, write_address) = { 135 | let mut write_sig = Signature::new(call_conv); 136 | write_sig.params.push(AbiParam::new(I8)); 137 | write_sig.returns.push(AbiParam::new(pointer_type)); 138 | let write_sig = builder.import_signature(write_sig); 139 | 140 | let write_address = write as *const () as i64; 141 | let write_address = builder.ins().iconst(pointer_type, write_address); 142 | (write_sig, write_address) 143 | }; 144 | 145 | let (read_sig, read_address) = { 146 | let mut read_sig = Signature::new(call_conv); 147 | read_sig.params.push(AbiParam::new(pointer_type)); 148 | read_sig.returns.push(AbiParam::new(pointer_type)); 149 | let read_sig = builder.import_signature(read_sig); 150 | 151 | let read_address = read as *const () as i64; 152 | let read_address = builder.ins().iconst(pointer_type, read_address); 153 | (read_sig, read_address) 154 | }; 155 | 156 | let mut stack = Vec::new(); 157 | 158 | for (i, instr) in instructions.into_iter().enumerate() { 159 | match instr { 160 | Instruction::Add(n) => { 161 | let n = n as i64; 162 | let pointer_value = builder.use_var(pointer); 163 | let cell_address = builder.ins().iadd(memory_address, pointer_value); 164 | let cell_value = builder.ins().load(I8, mem_flags, cell_address, 0); 165 | let cell_value = builder.ins().iadd_imm(cell_value, n as i64); 166 | builder.ins().store(mem_flags, cell_value, cell_address, 0); 167 | } 168 | Instruction::Move(n) => { 169 | let n = n as i64; 170 | let pointer_value = builder.use_var(pointer); 171 | let pointer_plus = builder.ins().iadd_imm(pointer_value, n); 172 | 173 | let pointer_value = if n > 0 { 174 | let wrapped = builder.ins().iadd_imm(pointer_value, n - 30_000); 175 | let cmp = 176 | builder 177 | .ins() 178 | .icmp_imm(IntCC::SignedLessThan, pointer_plus, 30_000); 179 | builder.ins().select(cmp, pointer_plus, wrapped) 180 | } else { 181 | let wrapped = builder.ins().iadd_imm(pointer_value, n + 30_000); 182 | let cmp = builder 183 | .ins() 184 | .icmp_imm(IntCC::SignedLessThan, pointer_plus, 0); 185 | builder.ins().select(cmp, wrapped, pointer_plus) 186 | }; 187 | 188 | builder.def_var(pointer, pointer_value); 189 | } 190 | Instruction::Output => { 191 | let pointer_value = builder.use_var(pointer); 192 | let cell_address = builder.ins().iadd(memory_address, pointer_value); 193 | let cell_value = builder.ins().load(I8, mem_flags, cell_address, 0); 194 | 195 | let inst = builder 196 | .ins() 197 | .call_indirect(write_sig, write_address, &[cell_value]); 198 | let result = builder.inst_results(inst)[0]; 199 | 200 | let after_block = builder.create_block(); 201 | 202 | builder.ins().brnz(result, exit_block, &[result]); 203 | builder.ins().jump(after_block, &[]); 204 | 205 | builder.seal_block(after_block); 206 | builder.switch_to_block(after_block); 207 | } 208 | Instruction::Input => { 209 | let pointer_value = builder.use_var(pointer); 210 | let cell_address = builder.ins().iadd(memory_address, pointer_value); 211 | 212 | let inst = builder 213 | .ins() 214 | .call_indirect(read_sig, read_address, &[cell_address]); 215 | let result = builder.inst_results(inst)[0]; 216 | 217 | let after_block = builder.create_block(); 218 | 219 | builder.ins().brnz(result, exit_block, &[result]); 220 | builder.ins().jump(after_block, &[]); 221 | 222 | builder.seal_block(after_block); 223 | builder.switch_to_block(after_block); 224 | } 225 | Instruction::JumpRight => { 226 | let inner_block = builder.create_block(); 227 | let after_block = builder.create_block(); 228 | 229 | let pointer_value = builder.use_var(pointer); 230 | let cell_address = builder.ins().iadd(memory_address, pointer_value); 231 | let cell_value = builder.ins().load(I8, mem_flags, cell_address, 0); 232 | 233 | builder.ins().brz(cell_value, after_block, &[]); 234 | builder.ins().jump(inner_block, &[]); 235 | 236 | builder.switch_to_block(inner_block); 237 | 238 | stack.push((inner_block, after_block)); 239 | } 240 | Instruction::JumpLeft => { 241 | let (inner_block, after_block) = match stack.pop() { 242 | Some(x) => x, 243 | None => return Err(UnbalancedBrackets(']', i)), 244 | }; 245 | 246 | let pointer_value = builder.use_var(pointer); 247 | let cell_address = builder.ins().iadd(memory_address, pointer_value); 248 | let cell_value = builder.ins().load(I8, mem_flags, cell_address, 0); 249 | 250 | builder.ins().brnz(cell_value, inner_block, &[]); 251 | builder.ins().jump(after_block, &[]); 252 | 253 | builder.seal_block(inner_block); 254 | builder.seal_block(after_block); 255 | 256 | builder.switch_to_block(after_block); 257 | } 258 | Instruction::Clear => { 259 | let pointer_value = builder.use_var(pointer); 260 | let cell_address = builder.ins().iadd(memory_address, pointer_value); 261 | builder.ins().store(mem_flags, zero_byte, cell_address, 0); 262 | } 263 | Instruction::AddTo(n) => { 264 | let n = n as i64; 265 | let pointer_value = builder.use_var(pointer); 266 | let to_add = builder.ins().iadd_imm(pointer_value, n); 267 | 268 | let to_add = if n > 0 { 269 | let wrapped = builder.ins().iadd_imm(pointer_value, n - 30_000); 270 | let cmp = builder 271 | .ins() 272 | .icmp_imm(IntCC::SignedLessThan, to_add, 30_000); 273 | builder.ins().select(cmp, to_add, wrapped) 274 | } else { 275 | let wrapped = builder.ins().iadd_imm(pointer_value, n + 30_000); 276 | let cmp = builder.ins().icmp_imm(IntCC::SignedLessThan, to_add, 0); 277 | builder.ins().select(cmp, wrapped, to_add) 278 | }; 279 | 280 | let from_address = builder.ins().iadd(memory_address, pointer_value); 281 | let to_address = builder.ins().iadd(memory_address, to_add); 282 | 283 | let from_value = builder.ins().load(I8, mem_flags, from_address, 0); 284 | let to_value = builder.ins().load(I8, mem_flags, to_address, 0); 285 | 286 | let sum = builder.ins().iadd(to_value, from_value); 287 | 288 | builder.ins().store(mem_flags, zero_byte, from_address, 0); 289 | builder.ins().store(mem_flags, sum, to_address, 0); 290 | } 291 | } 292 | } 293 | 294 | if !stack.is_empty() { 295 | return Err(UnbalancedBrackets(']', source.len())); 296 | } 297 | 298 | builder.ins().return_(&[zero]); 299 | 300 | builder.switch_to_block(exit_block); 301 | builder.seal_block(exit_block); 302 | 303 | let result = builder.block_params(exit_block)[0]; 304 | builder.ins().return_(&[result]); 305 | 306 | builder.finalize(); 307 | 308 | let res = verify_function(&func, &*isa); 309 | 310 | if clir { 311 | println!("{}", func.display()); 312 | } 313 | 314 | if let Err(errors) = res { 315 | panic!("{}", errors); 316 | } 317 | 318 | let mut ctx = Context::for_function(func); 319 | let code = match ctx.compile(&*isa) { 320 | Ok(x) => x, 321 | Err(err) => { 322 | eprintln!("error compiling: {:?}", err); 323 | if clir { 324 | println!("{}", ctx.func.display()); 325 | } 326 | std::process::exit(4); 327 | } 328 | }; 329 | 330 | let code = code.code_buffer().to_vec(); 331 | 332 | if clir { 333 | println!("{}", ctx.func.display()); 334 | } 335 | 336 | Ok(Program { 337 | code, 338 | memory: [0; 30_000], 339 | }) 340 | } 341 | 342 | fn run(&mut self) -> std::io::Result<()> { 343 | let mut buffer = memmap2::MmapOptions::new() 344 | .len(self.code.len()) 345 | .map_anon() 346 | .unwrap(); 347 | 348 | buffer.copy_from_slice(self.code.as_slice()); 349 | 350 | let buffer = buffer.make_exec().unwrap(); 351 | 352 | unsafe { 353 | let code_fn: unsafe extern "C" fn(*mut u8) -> *mut std::io::Error = 354 | std::mem::transmute(buffer.as_ptr()); 355 | 356 | let error = code_fn(self.memory.as_mut_ptr()); 357 | 358 | if !error.is_null() { 359 | return Err(*Box::from_raw(error)); 360 | } 361 | } 362 | 363 | Ok(()) 364 | } 365 | } 366 | 367 | extern "C" fn write(value: u8) -> *mut std::io::Error { 368 | // Writing a non-UTF-8 byte sequence on Windows error out. 369 | if cfg!(target_os = "windows") && value >= 128 { 370 | return std::ptr::null_mut(); 371 | } 372 | 373 | let mut stdout = std::io::stdout().lock(); 374 | 375 | let result = stdout.write_all(&[value]).and_then(|_| stdout.flush()); 376 | 377 | match result { 378 | Err(err) => Box::into_raw(Box::new(err)), 379 | _ => std::ptr::null_mut(), 380 | } 381 | } 382 | 383 | unsafe extern "C" fn read(buf: *mut u8) -> *mut std::io::Error { 384 | let mut stdin = std::io::stdin().lock(); 385 | loop { 386 | let mut value = 0; 387 | let err = stdin.read_exact(std::slice::from_mut(&mut value)); 388 | 389 | if let Err(err) = err { 390 | if err.kind() != std::io::ErrorKind::UnexpectedEof { 391 | return Box::into_raw(Box::new(err)); 392 | } 393 | value = 0; 394 | } 395 | 396 | // ignore CR from Window's CRLF 397 | if cfg!(target_os = "windows") && value == b'\r' { 398 | continue; 399 | } 400 | 401 | *buf = value; 402 | 403 | return std::ptr::null_mut(); 404 | } 405 | } 406 | 407 | fn main() -> ExitCode { 408 | let mut args = std::env::args(); 409 | 410 | let mut dump = None; 411 | let mut source = None; 412 | let mut clir = false; 413 | while let Some(arg) = args.next() { 414 | match arg.as_str() { 415 | "-d" | "--dump" => { 416 | dump = args.next(); 417 | assert!(dump.is_some()); 418 | } 419 | "--CLIR" => { 420 | clir = true; 421 | } 422 | _ => source = Some(arg), 423 | } 424 | } 425 | 426 | let source = match source { 427 | Some(x) => x, 428 | None => { 429 | eprintln!("expected a file path as argument"); 430 | return ExitCode::from(1); 431 | } 432 | }; 433 | 434 | let source = match std::fs::read(&source) { 435 | Ok(x) => x, 436 | Err(err) => { 437 | eprintln!("Error reading '{}': {}", source, err); 438 | return ExitCode::from(2); 439 | } 440 | }; 441 | 442 | let mut program = match Program::new(&source, clir) { 443 | Ok(x) => x, 444 | Err(UnbalancedBrackets(c, address)) => { 445 | eprintln!( 446 | "Error parsing file: didn't found pair for `{}` at byte index {}", 447 | c, address 448 | ); 449 | return ExitCode::from(3); 450 | } 451 | }; 452 | 453 | if let Some(dump) = &dump { 454 | std::fs::write(dump, program.code.as_slice()).unwrap(); 455 | } 456 | 457 | if dump.is_some() || clir { 458 | return ExitCode::from(0); 459 | } 460 | 461 | if let Err(err) = program.run() { 462 | eprintln!("IO error: {}", err); 463 | } 464 | 465 | ExitCode::from(0) 466 | } 467 | --------------------------------------------------------------------------------