Funki Crab is an optimising Brainfuck compiler written in Rust.
++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++
..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.
#include <stdio.h>
char outputs[13] = { 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 10 };
int main() {
fwrite(outputs, sizeof(char), 13, stdout);
return 0;
}
Hello World!
I created Funki Crab as an exercise in learning about compiler development, Immediate Representation (IR) techniques and optimisation. Brainfuck struck me as a sensible language for such a project given its simplicity, Turing-completeness and wealth of potential optimisations.
Funki Crab is an anagram of Brainfuck. 'Crab' is a reference to Ferris, the Rust mascot.
Currently, Funki Crab can only transpile Brainfuck to C. In the future, it will be capable of compiling to assembly or even raw binaries.
Funki Crab can combine consecutive shifts, increments and decrements together into a single IR instruction.
Example:
++++>>>><<---+<
Becomes:
*ptr += 4;
ptr += 2;
*ptr -= 2;
--ptr;
Funki Crab can unroll loops that count down one cell in order to add-and-multiply other cells into single IR instructions.
Example:
[->+++>>>--<<<<]
Becomes:
ptr[1] += *ptr * 3;
ptr[4] -= *ptr * 2;
*ptr = 0;
Funki Crab can transform loops that decrement to zero and add into a single IR instruction.
Example:
[-]++++
Becomes:
*ptr = 4;
Funki Crab can elide shift operations by keeping track of the increment and passing it on to future operations.
Example:
+>>>+
Becomes:
++*ptr;
++ptr[3];
Funki Crab can elide shifts across loop boundaries.
Example:
+<<[>]>>
++*ptr;
while (ptr[-2]) {
++ptr;
}
Funki Crab can eliminate many common instances of dead code.
Example:
+,>[-],
Becomes:
*ptr = getchar();
++ptr;
*ptr = getchar();
Funki Crab can analyse the value of cells and operations performed upon cells at compile-time and reason about their effect on later parts of the program. This means that even more dead code can be eliminated and certain instructions can safely be elided. In addition, Funki Crab can use this analysis information to test the accessibility of a loop, eliding it if possible.
Example:
>[.-]
Becomes:
Nothing
Funki Crab can analyse instructions at the tail of a program and eliminate them if it can prove that they have no impact.
Example:
+.>>+<---+>
Becomes:
++*ptr;
putchar(ptr[0]);
Funki Crab is capable of partial execution of the program at compile-time for all instructions other than .
. For programs that do not rely on input, and finish
execution with fewer than 1 million instructions, it's even possible for Funki Crab to fully execute the entire program and emit only the code necessary to print
the end result.
Funki Crab is capable of combining the optimisations listed above, and more, through multiple transformations of the IR.
Example:
+.>>+<---+>[-<+[-][>>++++[.-]][-++]->+-]--
Becomes:
Nothing
Typical speedup with Funki Crab optimisations enabled vs disabled for substantial Brainfuck programs is ~100x.
I don't plan to maintain Funki Crab as a long-term project. Perhaps I'll add things like better loop unrolling, constant propagation, compile-time execution or instruction reordering to improve cache coherency in the future.