diff options
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | Makefile | 16 | ||||
-rw-r--r-- | bfcomp.c | 312 |
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; +} |