summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--Makefile16
-rw-r--r--bfcomp.c312
3 files changed, 332 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..261e785
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+bfcomp
+*.o
+*.dSYM
+dst
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..73e948a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,16 @@
+CC := gcc
+CFLAGS := -Wall -Wextra -std=c11 -O2 -fwrapv
+BIN := bfcomp
+
+.PHONY: all clean remake
+
+all: $(BIN)
+
+clean:
+ rm -rf $(BIN) *.dSYM
+
+remake: clean all
+
+
+$(BIN): $(wildcard *.c *.h)
+ $(CC) $(CFLAGS) -o $@ $(filter-out %.h,$^)
diff --git a/bfcomp.c b/bfcomp.c
new file mode 100644
index 0000000..9de8cf2
--- /dev/null
+++ b/bfcomp.c
@@ -0,0 +1,312 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+
+#define DBGTIME(tag,block) \
+ do { \
+ clock_t start; \
+ if(params.verbose){ \
+ start=clock(); \
+ fprintf(stderr,"%s",(tag)); \
+ } \
+ {block} \
+ if(params.verbose){ \
+ fprintf(stderr," (%lfs)\n",(double)(clock()-start)/CLOCKS_PER_SEC); \
+ } \
+ } while(0)
+
+
+int uniqid(void){
+ static int i=0;
+ return i++;
+}
+
+void usage(const char *argv0){
+ fprintf(stderr,
+ "Usage: %s [-h] [-m <memsize>] <srcfile> <dstfile>\n"
+ "Reads a Brainfuck program from <srcfile> and saves the compiled executable\n"
+ "in <dstfile>.\n"
+ "Uses nasm for assembling and gcc for linking.\n"
+ " -h Show help\n"
+ " -m <memsize> Specify amount of tape cells (bytes) for the BF code\n",
+ argv0);
+}
+
+__attribute__((noreturn)) void usage1(const char *argv0){
+ usage(argv0);
+ exit(1);
+}
+
+typedef struct Params{
+ int memsize;
+ const char *srcfname,*dstfname;
+ bool verbose;
+} Params;
+Params params={30000,NULL,NULL,false};
+
+void parseargs(int argc,char **argv){
+ if(argc<2)usage1(argv[0]);
+
+ for(int i=1;i<argc;i++){
+ bool skipnext=false;
+ if(argv[i][0]=='-'){
+ for(int j=1;argv[i][j];j++){
+ switch(argv[i][j]){
+ case 'h':
+ usage(argv[0]);
+ exit(0);
+
+ case 'm':{
+ if(i==argc-1)usage1(argv[0]);
+ char *endp;
+ params.memsize=strtol(argv[i+1],&endp,10);
+ if(!argv[i+1][0]||*endp||params.memsize<1)usage1(argv[0]);
+ skipnext=true;
+ break;
+ }
+
+ case 'v':
+ params.verbose=true;
+ break;
+ }
+ }
+ } else if(params.srcfname==NULL)params.srcfname=argv[i];
+ else if(params.dstfname==NULL)params.dstfname=argv[i];
+ else usage1(argv[0]);
+ if(skipnext)i++;
+ }
+
+ if(!params.srcfname||!params.dstfname)usage1(argv[0]);
+}
+
+int runcmd(const char *cmd/*,int in,int out*/){
+ pid_t pid=fork();
+ assert(pid>=0);
+ if(pid==0){
+ /*dup2(in,0);
+ dup2(out,1);*/
+ execl("/bin/sh","/bin/sh","-c",cmd,NULL);
+ __builtin_unreachable();
+ } else {
+ int statloc;
+ DBGTIME(cmd,{
+ while(true){
+ waitpid(pid,&statloc,0);
+ if(WIFEXITED(statloc))break;
+ }
+ });
+ return WEXITSTATUS(statloc);
+ }
+}
+
+FILE* gettempfilew(char name[20]){
+ memcpy(name,"/tmp/tmp.XXXXXXXXXX",20);
+ int fd=mkstemp(name);
+ assert(fd>=0);
+ FILE *f=fdopen(fd,"w");
+ assert(f);
+ return f;
+}
+
+int compilechain(const char *asmfname,const char *dstfname){
+ char ofname[20];
+ FILE *f=gettempfilew(ofname);
+ if(params.verbose)fprintf(stderr,"o -> %s\n",ofname);
+ assert(strlen(ofname)==19);
+ fclose(f);
+ char buf[25+19+1+strlen(asmfname)+1];
+ sprintf(buf,"nasm -f macho64 -w+all -o %s %s",ofname,asmfname);
+ int ret=runcmd(buf);
+ if(ret)return ret;
+ char buf2[4+19+4+strlen(dstfname)+1];
+ sprintf(buf2,"gcc %s -o %s",ofname,dstfname);
+ ret=runcmd(buf2);
+ unlink(ofname);
+ if(params.verbose)fprintf(stderr,"unlink(%s) (ofile)\n",ofname);
+ return ret;
+}
+
+void writeprologue(FILE *asmf){
+ fprintf(asmf,
+ "global _main\n"
+ "\n"
+ "extern _malloc\n"
+ "extern _free\n"
+ "extern _printf\n"
+ "extern _putchar\n"
+ "extern _write\n"
+ "\n"
+ "default rel\n"
+ "\n"
+ "section .text\n"
+ "\n"
+ "_main:\n"
+ "\tpush rbp ; prologue\n"
+ "\tmov rbp, rsp\n"
+ "\tadd rsp, 8\n"
+ "\tpush rbx\n"
+ "\tmov edi, %d\n"
+ "\tcall _malloc\n"
+ "\tcmp rax, 0\n"
+ "\tjz nomem\n"
+ "\tmov rbx, rax ;bufp\n"
+ "\tmov [buf], rax ;buf\n"
+ "\n",
+ params.memsize);
+}
+
+void writeprogram(FILE *srcf,FILE *asmf){
+ int addc=0,shiftc=0;
+ //int lastshiftc=0;
+ struct stackitem {int id; struct stackitem *next;} *loopstack=NULL;
+ bool eof=false;
+ while(!eof){
+ char c=fgetc(srcf);
+ if(feof(srcf))eof=1;
+ if(!eof&&strchr("+-><[].,",c)==NULL)continue;
+ if(addc&&c!='+'&&c!='-'){
+ addc=(addc%256)+256%256;
+ if(addc==1)fprintf(asmf,"\tinc byte [rbx] ; 1 +\n");
+ else if(addc==-1)fprintf(asmf,"\tdec byte [rbx] ; 1 -\n");
+ else fprintf(asmf,"\tadd byte [rbx], %d ; %d %c\n",addc,addc<0?-addc:addc,addc<0?'-':'+');
+ addc=0;
+ }
+ if(shiftc&&c!='>'&&c!='<'){
+ shiftc=(shiftc%256)+256%256;
+ /*if(lastshiftc>0&&shiftc<0){
+ int id=uniqid();
+ fprintf(asmf,
+ "\tcmp [maxp], rbx\n"
+ "\tjge skip%d\n"
+ "\tmov [maxp], rbx\n"
+ "skip%d:\n",
+ id,id);
+ }
+ lastshiftc=shiftc;*/
+ if(shiftc==1)fprintf(asmf,"\tinc rbx ; 1 >\n");
+ else if(shiftc==-1)fprintf(asmf,"\tdec rbx ; 1 <\n");
+ else fprintf(asmf,"\tadd rbx, %d ; %d %c\n",shiftc,shiftc<0?-shiftc:shiftc,shiftc<0?'<':'>');
+ shiftc=0;
+ }
+ switch(c){
+ case '+': addc++; break;
+ case '-': addc--; break;
+ case '>': shiftc++; break;
+ case '<': shiftc--; break;
+ case '[':{
+ struct stackitem *ls=malloc(sizeof(struct stackitem));
+ assert(ls);
+ ls->id=uniqid();
+ ls->next=loopstack;
+ loopstack=ls;
+ fprintf(asmf,
+ "loop%d: ; [\n"
+ "\tcmp byte [rbx], 0\n"
+ "\tjz loop%d_end\n"
+ "loop%d_body:\n",
+ ls->id,ls->id,ls->id);
+ break;
+ }
+ case ']':{
+ assert(loopstack);
+ fprintf(asmf,
+ "\tcmp byte [rbx], 0 ; ]\n"
+ "\tjnz loop%d_body\n"
+ "loop%d_end:\n",
+ loopstack->id,loopstack->id);
+ struct stackitem *ls=loopstack->next;
+ free(loopstack);
+ loopstack=ls;
+ break;
+ }
+ case '.':
+ fprintf(asmf,
+ "\tmov edi, 0 ; .\n"
+ "\tmov dil, byte [rbx]\n"
+ "\tcall _putchar\n");
+ break;
+ case ',':
+ fprintf(asmf,
+ "\tcall _getchar ; ,\n"
+ "\tmov byte [rbx], al\n");
+ break;
+ default:
+ if(!eof)assert(false);
+ }
+ }
+ assert(!loopstack);
+}
+
+void writeepilogue(FILE *asmf){
+ fprintf(asmf,
+ "\n"
+ "\tmov rdi, [buf] ; epilogue\n"
+ "\tcall _free\n"
+ "\n"
+ /*"\tlea rdi, [formatstr] ;output last cell reached\n"
+ "\tmov rsi, [maxp]\n"
+ "\tsub rsi, [buf]\n"
+ "\txor eax, eax\n"
+ "\tcall _printf\n"
+ "\n"*/
+ "\txor eax, eax\n"
+ "\tjmp mainend\n"
+ "\n"
+ "nomem:\n"
+ "\tmov edi, 2\n"
+ "\tlea rsi, [nomemstr]\n"
+ "\tmov rdx, nomemstr.len\n"
+ "\tcall _write\n"
+ "\tmov eax, 1\n"
+ "\n"
+ "mainend:\n"
+ "\tpop rbx\n"
+ "\tmov rsp, rbp\n"
+ "\tpop rbp\n"
+ "\tret\n"
+ "\n"
+ "\n"
+ "section .data\n"
+ "sectalign 8\n"
+ "\n"
+ "nomemstr: db \"Out of memory!\"\n"
+ ".len: equ $-nomemstr\n"
+ "\n"
+ "formatstr: db \"Last cell reached: %%d\", 10, 0\n"
+ "\n"
+ "align 8, db 0\n"
+ "maxp: dq 0\n"
+ "\n"
+ "\n"
+ "section .bss\n"
+ "sectalign 8\n"
+ "\n"
+ "buf: resq 1\n");
+}
+
+int main(int argc,char **argv){
+ parseargs(argc,argv);
+
+ FILE *srcf=fopen(params.srcfname,"r");
+ assert(srcf);
+
+ char asmfname[20];
+ FILE *asmf=gettempfilew(asmfname);
+ if(params.verbose)fprintf(stderr,"asm -> %s\n",asmfname);
+
+ DBGTIME("writeprologue",writeprologue(asmf););
+ DBGTIME("writeprogram",writeprogram(srcf,asmf););
+ DBGTIME("writeepilogue",writeepilogue(asmf););
+
+ fclose(asmf);
+ fclose(srcf);
+
+ int ret=compilechain(asmfname,params.dstfname);
+ unlink(asmfname);
+ if(params.verbose)fprintf(stderr,"unlink(%s) (asmfname)\n",asmfname);
+ return ret;
+}