Skip to content

Instantly share code, notes, and snippets.

@aabiji
Created August 3, 2025 21:31
Show Gist options
  • Select an option

  • Save aabiji/17ea168e51aba9eec1c2756b4e8fd9ec to your computer and use it in GitHub Desktop.

Select an option

Save aabiji/17ea168e51aba9eec1c2756b4e8fd9ec to your computer and use it in GitHub Desktop.
JIT compiled brainfuck
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
typedef struct { int length; int capacity; uint8_t* buffer; } Vector;
void vector_append(Vector *v, uint8_t *data, int length) {
if (v->length + length >= v->capacity) {
v->capacity *= 2;
v->buffer = (uint8_t*)realloc(v->buffer, v->capacity);
}
memcpy(v->buffer + v->length, data, length);
v->length += length;
}
int handle_loop(Vector *code, bool opening) {
uint8_t jmp = opening ? 0x84 : 0x85;
uint8_t instructions[9] = {
0x80, 0x3F, 0x00, // cmpb $0x0,(%rdi)
0x0F, jmp, // je rel32 or jne rel32
0x00, 0x00, 0x00, 0x00, // signed 32 bit offset
};
vector_append(code, instructions, 9);
return code->length;
}
typedef void (*run_brainfuck)(char *memory); // %rdi -> data buffer (data ptr)
uint8_t dataptr_add[3] = {0x48, 0xff, 0xc7}; // inc %rdi
uint8_t dataptr_sub[3] = {0x48, 0xff, 0xcf}; // dec %rdi
uint8_t data_add[2] = {0xfe, 0x07}; // incb (%rdi)
uint8_t data_sub[2] = {0xfe, 0x0f}; // decb (%rdi)
uint8_t printf_value[35] = {
0x57, // push rdi
0x8A, 0x07, // mov al, byte ptr [rdi]
0x48, 0x83, 0xEC, 0x08, // sub rsp, 8
0x88, 0x04, 0x24, // mov [rsp], al
0xB8, 0x01, 0x00, 0x00, 0x00, // mov eax, 1
0xBF, 0x01, 0x00, 0x00, 0x00, // mov edi, 1
0x48, 0x89, 0xE6, // mov rsi, rsp
0xBA, 0x01, 0x00, 0x00, 0x00, // mov edx, 1
0x0F, 0x05, // syscall
0x48, 0x83, 0xC4, 0x08, // add rsp, 8
0x5F // pop rdi
};
uint8_t read_value[22] = {
0x57, // push rdi
0x48, 0x89, 0xFE, // mov rsi, rdi
0xB8, 0x00, 0x00, 0x00, 0x00, // mov eax, 0
0xBF, 0x00, 0x00, 0x00, 0x00, // mov edi, 0
0xBA, 0x01, 0x00, 0x00, 0x00, // mov edx, 1
0x0F, 0x05, // syscall
0x5F // pop rdi
};
void jit_compile(Vector *code, char *bf_source, int length) {
size_t stack[2048] = {0};
int stack_index = 0;
char *current = bf_source;
while (current - bf_source < length) {
int offset = current - bf_source;
if (*current == '>') vector_append(code, dataptr_add, 3);
else if (*current == '<') vector_append(code, dataptr_sub, 3);
else if (*current == '+') vector_append(code, data_add, 2);
else if (*current == '-') vector_append(code, data_sub, 2);
else if (*current == '.') vector_append(code, printf_value, 35);
else if (*current == ',') vector_append(code, read_value, 22);
// emit machine code for the loops, while updating the jump offsets
else if (*current == '[')
stack[stack_index++] = handle_loop(code, true);
else if (*current == ']') {
int opening_offset_pos = stack[--stack_index];
int closing_offset_pos = handle_loop(code, false);
int32_t forward = (closing_offset_pos - 9) - (opening_offset_pos - 3);
int32_t backward = (opening_offset_pos - 9) - (closing_offset_pos - 3);
for (int i = 0; i < 4; i++) {
code->buffer[opening_offset_pos - 4 + i] = (forward >> (8 * i)) & 0xFF;
code->buffer[closing_offset_pos - 4 + i] = (backward >> (8 * i)) & 0xFF;
}
}
current++;
}
uint8_t ret = 0xc3;
vector_append(code, &ret, 1);
}
void run_jit(char *bf_src, char *memory, int src_len) {
Vector code = { .length = 0, .capacity = 1024, .buffer = (uint8_t*)malloc(1024) };
jit_compile(&code, bf_src, src_len);
// align to a page boundary
size_t page_size = sysconf(_SC_PAGESIZE);
uintptr_t page_start = (uintptr_t)code.buffer & ~(page_size - 1);
uintptr_t page_end =
((uintptr_t)code.buffer + code.length + page_size - 1) & ~(page_size - 1);
size_t total_len = page_end - page_start;
// mark as executable
if (mprotect((void *)page_start, total_len, PROT_READ | PROT_WRITE | PROT_EXEC)) {
perror("mprotect");
exit(-1);
}
((run_brainfuck)code.buffer)(memory);
free(code.buffer);
}
int main(int argc, char **argv) {
if (argc != 2) {
printf("Usage: brainfuck PROGRAM_PATH\n");
exit(-1);
}
FILE *f = fopen(argv[1], "r");
fseek(f, 0, SEEK_END);
long file_length = ftell(f);
fseek(f, 0, SEEK_SET);
char *instructions = (char *)calloc(file_length + 1, sizeof(char));
fread(instructions, file_length, 1, f);
char *head = instructions;
char *memory = (char *)calloc(1024 * 1024, sizeof(char));
run_jit(instructions, memory, file_length);
free(memory);
free(instructions);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment