Skip to content
This repository was archived by the owner on Nov 4, 2019. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ BUILDDIR=bin/
PREFIX=/usr/local/bin/
SOURCES=$(wildcard src/*.c)
MAIN=main.c
override CFLAGS+=-std=c11 -O2 -Wno-gnu -lpthread
DEVFLAGS=-Werror -Wall -g -fPIC -DNDEBUG -Wfloat-equal -Wundef -Wwrite-strings -Wuninitialized -pedantic -fsanitize=address -O0
override CFLAGS+=-std=c11 -O2 -g -Wno-gnu -lpthread
DEVFLAGS=-Werror -Wall -g -fPIC -DNDEBUG -Wfloat-equal -Wundef -Wwrite-strings -Wuninitialized -pedantic -O0

all: main.c
mkdir -p $(BUILDDIR)
$(CC) $(MAIN) $(SOURCES) -o $(BUILDDIR)$(TARGET) $(CFLAGS)

no_wrap: main.c
CFLAGS+=-DNO_WRAP make
CFLAGS=-DNO_WRAP make

dev: main.c
mkdir -p $(BUILDDIR)
Expand Down
33 changes: 11 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
actor that is effectively a Brainfuck program with added primitives for
receiving from and sending to other actors.

This is the JIT version. Read [my write-up](https://blog.veitheller.de/Speeding_up_an_Interpreter.html)
about it.

## Usage

You can build the program through its Makefile by typing `make`. This will leave
Expand Down Expand Up @@ -43,35 +46,21 @@ And that’s all that `cspfuck` is.

I’m a terrible Brainfuck programmer, so I only provide one proof-of-concept
example for now. It is contained in a directory named
`example/`—appropriately singular—, in which I provide a hello world program.
It is three actors, printing hello world concurrently. At the end of the
program, the first actor goes back to the cell containing `d`, and sends it
to the actor below, which will receive it into a new cell, print it, and print
a newline to make it pretty.
`example/`—appropriately singular—, in which I provide a ping pong program.
It contains two actors, one printing ping, then waiting for the other, the
other waiting, and then printing pong, ten times.

## Implementation

Actors are implemented as pthreads. The virtual machine is a simple direct
threaded bytecode VM that offers 30,000 elements to each Brainfuck program. It
will wrap around if you go past the low or high threshold—this can be turned
off by passing `-DNO_WRAP` to the compiler or calling `make no_wrap`, as
disabling it might buy you a little bit of performance, depending on your
workfload.

It should be reasonably performant, but who cares? I hope noone’s going to run
their MapReduce jobs on it. There are some low-hanging fruits for optimization,
like making the VM loop use direct threaded code, but I chose not to for now.
Feel free to hack on it you want to! I’m happy to help you get started.
Actors are implemented as pthreads. The virtual machine is a simple handrolled
JIT that offers 30,000 elements to each Brainfuck program. It will segfault if
you go past the low or high threshold. This version is about four times faster
than the bytecode VM provided in the [`master`](https://github.com/hellerve/cspfuck/tree/master) branch.

The VM does seem to execute [ridiculous programs](http://www.clifford.at/bfcpu/hanoi.html)
in standard Brainfuck pretty efficiently, which makes me unreasonably happy.

It’s only about 400 lines of C, so it should be reasonably consumable. The
It’s only about 550 lines of C, so it should be reasonably consumable. The
code isn’t necessarily pretty, but it seems to work well. It is not incredibly
battle-tested, though.

If you want to know more, read [my blog post](http://blog.veitheller.de/Brainfuck_and_Actors.html)!

**Disclaimer**: I know approximately as much about concurrent programming in C
as I know about writing production-grade Brainfuck. The system should be
expected to be brittle.
Expand Down
5 changes: 0 additions & 5 deletions example/hello_world.bf

This file was deleted.

5 changes: 5 additions & 0 deletions example/ping.bf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
+++++++++++++++++++++++++++++++++++[>++>++>++>++<<<<-]>++++++++++>+++>++++++++>+>++++++++++<<<<<
++++++++++[>.>.>.>.>.>vu<<<<<<-]

+++++++++++++++++++++++++++++++++++[>++>++>++>++<<<<-]>++++++++++>+++++++++>++++++++>+>++++++++++<<<<<
++++++++++[>>>>>>u<<<<<<>.>.>.>.>.^<<<<<-]
4 changes: 3 additions & 1 deletion main.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ int main(int argc, char** argv) {
fseek(f, 0, SEEK_SET);

char *string = malloc(fsize + 1);
fread(string, fsize, 1, f);
int read = fread(string, 1, fsize, f);
fclose(f);

if (read != fsize) return err("couldn’t read file %s.\n", argv[1]);

string[fsize] = 0;

actors* ac = parse(string);
Expand Down
74 changes: 74 additions & 0 deletions src/debug.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

Expand Down Expand Up @@ -86,3 +87,76 @@ void print_src(actors* ac) {
printf("\n\n");
}
}

uint32_t calc_offset(uint32_t jump_frm, uint32_t jump_to) {
if (jump_to >= jump_frm) return jump_to - jump_frm;
else return ~((uint32_t)jump_frm-jump_to) + 1;
}

void print_faux_assembly_bytecode(long offset, bytecode* code) {
#define print(s) (printf("0x%lx: %s\n", idx+offset, s))
#define print_arg(s) (printf("0x%lx: %s $0x%x, (%%r13)\n", idx+offset, s, c.arg))
#define print_arg_ref(s) (printf("0x%lx: %s $0x%x, %%r13b\n", idx+offset, s, c.arg))
#define print_syscall(d) {\
printf("0x%lx: movq 0x200000%d, %%rax\n", idx+offset, d);\
idx+=7;\
print("movq 0x1, %%rdi");\
idx+=7;\
print("movq %%r13, %%rsi");\
idx+=3;\
print("movq 0x1, %%rdx");\
idx+=7;\
print("syscall ");\
idx+=2;\
}
int i = 0;
long idx = 10;
size_t loop_stack[256];
int loop_depth = 0;
printf("0x%lx: movabsq $random-addr, %%r13\n", offset);
while(1) {
bytecode c = code[i++];
switch (c.code) {
case INC: print_arg("addb"); idx+=5;break;
case DEC: print_arg("subb"); idx+=5;break;
case FWD: print_arg_ref("addb"); idx+=4;break;
case BCK: print_arg_ref("subb"); idx+=4;break;
case PRN: print_syscall(4); break;
case ZERO: print("movb $0x0, (%r13)"); idx+=5;break;
case READ: print_syscall(3); break;
case STARTL:
print("cmpb $0x0, (%r13)");
idx+=5;
printf("0x%lx: je 0x%lx\n", idx+offset, c.arg+offset);
loop_stack[loop_depth++] = idx;
idx+=6;
break;
case ENDL: {
size_t begin = loop_stack[--loop_depth];

print("cmpb $0x0, (%r13)");
idx+=5;

size_t jmp_frm = idx + 6;
size_t jmp_to = begin + 6;
uint32_t pcrel_offset = calc_offset(jmp_frm, jmp_to);
printf("0x%lx: jne 0x%x\n", idx+offset, pcrel_offset);
idx+=6;
break;
}
case HALT:
print("retq");
return;
}
}
#undef print
#undef print_arg
#undef print_arg_ref
#undef print_syscall
}

void print_faux_assembly(long offset, actors* ac) {
for (int i = 0; i < ac->num; i++) {
print_faux_assembly_bytecode(offset, ac->code[i]);
}
}
1 change: 1 addition & 0 deletions src/debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@

void print_actors(actors*);
void print_src(actors*);
void print_faux_assembly(long, actors*);

#endif
Loading