From a935faa65e3978ab463a35be53e799ea1f18d80c Mon Sep 17 00:00:00 2001 From: Jaromil Date: Mon, 30 Dec 2024 08:53:16 +0100 Subject: [PATCH] fix: make TCC opaque from the CJIT api this way calling code doesn't needs to include TCC's API which is now managed internally. This is the first step to adding more backends like libgcc, but also helps debugging. also added a --verb flag for verbose output and removed the old repl --- build/embed-asset-path.sh | 2 +- build/init.mk | 2 +- src/cjit.c | 126 +++++++++++++++++++++++++++----------- src/cjit.h | 29 +++++++-- src/main.c | 31 +++++----- 5 files changed, 132 insertions(+), 58 deletions(-) diff --git a/build/embed-asset-path.sh b/build/embed-asset-path.sh index cf7005c..5d3043c 100644 --- a/build/embed-asset-path.sh +++ b/build/embed-asset-path.sh @@ -69,7 +69,7 @@ cat <> src/assets.c snprintf(incpath,511,"%s/%s",CJIT->tmpdir,"${name}"); if(CJIT->fresh) res = muntargz_to_path(CJIT->tmpdir,(const uint8_t*)&${varname},${varname}_len); if(res!=0) { _err("Error extracting %s",incpath); return(false); } -tcc_add_include_path(CJIT->TCC, incpath); +cjit_add_include_path(CJIT, incpath); // ^^ ${name} ^^ EOF diff --git a/build/init.mk b/build/init.mk index 877225d..75d33db 100644 --- a/build/init.mk +++ b/build/init.mk @@ -17,7 +17,7 @@ cflags := ${CFLAGS} ${cflags_includes} SOURCES := src/file.o src/cjit.o \ src/main.o src/assets.o \ - src/cwalk.o src/repl.o \ + src/cwalk.o \ src/muntar.o src/tinflate.o src/tinfgzip.o \ src/embed_libtcc1.a.o src/embed_include.o #src/embed_source.o diff --git a/src/cjit.c b/src/cjit.c index f652bbe..8f8c436 100644 --- a/src/cjit.c +++ b/src/cjit.c @@ -35,6 +35,9 @@ #define MAX_PATH 260 // rather short paths #define MAX_STRING 20480 // max 20KiB strings +#define tcc(cjit) (TCCState*)cjit->TCC +#define setup if(!cjit->done_setup)cjit_setup(cjit) +#define debug(fmt,par) if(!cjit->quiet)_err(fmt,par) // declared at bottom void _out(const char *fmt, ...); void _err(const char *fmt, ...); @@ -117,7 +120,7 @@ CJITState* cjit_new() { } // instantiate TCC before extracting assets because // extract_assets() will also add include paths using TCC - cjit->TCC = tcc_new(); + cjit->TCC = (void*)tcc_new(); if (!cjit->TCC) { _err("CJIT: Could not initialize tinyCC"); free(cjit); @@ -132,65 +135,65 @@ CJITState* cjit_new() { return(NULL); } // error handler callback for TCC - tcc_set_error_func(cjit->TCC, stderr, cjit_tcc_handle_error); + tcc_set_error_func(tcc(cjit), stderr, cjit_tcc_handle_error); return(cjit); } -bool cjit_setup(CJITState *cjit) { +static bool cjit_setup(CJITState *cjit) { // set output in memory for just in time execution if(cjit->done_setup) { _err("Warning: cjit_setup called twice or more times"); return(true); } - tcc_set_output_type(cjit->TCC, cjit->tcc_output); + tcc_set_output_type(tcc(cjit), cjit->tcc_output); #if defined(LIBC_MUSL) - tcc_add_libc_symbols(cjit->TCC); + tcc_add_libc_symbols(tcc(cjit)); #endif if(getenv("CFLAGS")) { char *extra_cflags = NULL; extra_cflags = getenv("CFLAGS"); _err("CFLAGS: %s",extra_cflags); - tcc_set_options(cjit->TCC, extra_cflags); + tcc_set_options(tcc(cjit), extra_cflags); } #if defined(_WIN32) // add symbols for windows compatibility - tcc_add_symbol(cjit->TCC, "usleep", &win_compat_usleep); - tcc_add_symbol(cjit->TCC, "getline", &win_compat_getline); + tcc_add_symbol(tcc(cjit), "usleep", &win_compat_usleep); + tcc_add_symbol(tcc(cjit), "getline", &win_compat_getline); #endif // When using SDL2 these defines are needed - tcc_define_symbol(cjit->TCC,"SDL_DISABLE_IMMINTRIN_H",NULL); - tcc_define_symbol(cjit->TCC,"SDL_MAIN_HANDLED",NULL); + tcc_define_symbol(tcc(cjit),"SDL_DISABLE_IMMINTRIN_H",NULL); + tcc_define_symbol(tcc(cjit),"SDL_MAIN_HANDLED",NULL); // where is libtcc1.a found - tcc_add_library_path(cjit->TCC, cjit->tmpdir); + tcc_add_library_path(tcc(cjit), cjit->tmpdir); // tcc_set_lib_path(TCC,tmpdir); // this overrides all? - tcc_add_sysinclude_path(cjit->TCC, cjit->tmpdir); - tcc_add_sysinclude_path(cjit->TCC, "."); - tcc_add_sysinclude_path(cjit->TCC, "include"); - tcc_add_library_path(cjit->TCC, "."); + tcc_add_sysinclude_path(tcc(cjit), cjit->tmpdir); + tcc_add_sysinclude_path(tcc(cjit), "."); + tcc_add_sysinclude_path(tcc(cjit), "include"); + tcc_add_library_path(tcc(cjit), "."); #if defined(_WIN32) { // windows system32 libraries //tcc_add_library_path(TCC, "C:\\Windows\\System32") // 64bit - tcc_add_library_path(cjit->TCC, "C:\\Windows\\SysWOW64"); + tcc_add_library_path(tcc(cjit), "C:\\Windows\\SysWOW64"); // tinycc win32 headers char *tpath = malloc(strlen(cjit->tmpdir)+32); strcpy(tpath,cjit->tmpdir); strcat(tpath,"/tinycc_win32/winapi"); - tcc_add_sysinclude_path(cjit->TCC, tpath); + tcc_add_sysinclude_path(tcc(cjit), tpath); free(tpath); // windows SDK headers char *sdkpath = malloc(512); if( get_winsdkpath(sdkpath,511) ) { int pathend = strlen(sdkpath); strcpy(&sdkpath[pathend],"\\um"); // um/GL - tcc_add_sysinclude_path(cjit->TCC, sdkpath); + tcc_add_sysinclude_path(tcc(cjit), sdkpath); strcpy(&sdkpath[pathend],"\\shared"); // winapifamili.h etc. - tcc_add_sysinclude_path(cjit->TCC, sdkpath); + tcc_add_sysinclude_path(tcc(cjit), sdkpath); } free(sdkpath); } @@ -290,7 +293,14 @@ static int detect_bom(const char *filename,size_t *filesize) { } } -static bool cjit_add_source(CJITState *cjit, const char *path) { +bool cjit_add_buffer(CJITState *cjit, const char *buffer) { + setup; + tcc_compile_string(tcc(cjit),buffer); + debug("+B %p",buffer); +} + +bool cjit_add_source(CJITState *cjit, const char *path) { + setup; size_t length; int res = detect_bom(path,&length); if(res<0) { @@ -336,38 +346,37 @@ static bool cjit_add_source(CJITState *cjit, const char *path) { char *tmp = malloc(dirname+1); strncpy(tmp,path,dirname); tmp[dirname] = 0x0; - tcc_add_include_path(cjit->TCC,tmp); + tcc_add_include_path(tcc(cjit),tmp); free(tmp); } - tcc_compile_string(cjit->TCC,contents); + tcc_compile_string(tcc(cjit),contents); free(contents); + debug("+S %s",path); return true; } bool cjit_add_file(CJITState *cjit, const char *path) { - // _err("%s",__func__); + setup; int is_source = has_source_extension(path); if(is_source == 0) { // no extension, we still add - cjit_setup(cjit); - if(tcc_add_file(cjit->TCC, path)<0) { + if(tcc_add_file(tcc(cjit), path)<0) { _err("%s: tcc_add_file error: %s",__func__,path); return false; } return true; } if(is_source>0) { - cjit_setup(cjit); if(!cjit_add_source(cjit, path)) { _err("%s: error: %s",__func__,path); return false; } } else { - cjit_setup(cjit); - if(tcc_add_file(cjit->TCC, path)<0) { + if(tcc_add_file(tcc(cjit), path)<0) { _err("%s: tcc_add_file error: %s",__func__,path); return false; } } + debug("+F %s",path); return true; } @@ -384,13 +393,13 @@ bool cjit_compile_file(CJITState *cjit, const char *path) { __func__,path); return false; } - cjit_setup(cjit); - tcc_add_file(cjit->TCC, path); + setup; + tcc_add_file(tcc(cjit), path); if(cjit->output_filename) { if(!cjit->quiet) _err("Compiling: %s -> %s",path, cjit->output_filename); - tcc_output_file(cjit->TCC, + tcc_output_file(tcc(cjit), cjit->output_filename); } else { char *ext; @@ -405,13 +414,30 @@ bool cjit_compile_file(CJITState *cjit, const char *path) { strcpy(ext,".o"); if(!cjit->quiet) _err("Compiling: %s -> %s",path,tmp); - tcc_output_file(cjit->TCC,tmp); + tcc_output_file(tcc(cjit),tmp); free(tmp); } return true; } +// link all setup and create an executable file +int cjit_link(CJITState *cjit) { + if(!cjit->done_setup) { + _err("%s: no source code found",__func__); + return 1; + } + if(!cjit->output_filename) { + _err("%s: no output file configured (-o)",__func__); + return 1; + } + return( tcc_output_file(tcc(cjit),cjit->output_filename)); +} + int cjit_exec(CJITState *cjit, int argc, char **argv) { + if(!cjit->done_setup) { + _err("%s: no source code found",__func__); + return 1; + } if(cjit->done_exec) { _err("%s: CJIT already executed once",__func__); return 1; @@ -419,12 +445,12 @@ int cjit_exec(CJITState *cjit, int argc, char **argv) { int res = 1; int (*_ep)(int, char**); // relocate the code (link symbols) - if (tcc_relocate(cjit->TCC) < 0) { + if (tcc_relocate(tcc(cjit)) < 0) { _err("%s: TCC linker error",__func__); _err("Library functions missing."); return -1; } - _ep = tcc_get_symbol(cjit->TCC, cjit->entry?cjit->entry:"main"); + _ep = tcc_get_symbol(tcc(cjit), cjit->entry?cjit->entry:"main"); if (!_ep) { _err("Symbol not found in source: %s",cjit->entry?cjit->entry:"main"); return -1; @@ -494,11 +520,41 @@ void cjit_free(CJITState *cjit) { if(cjit->write_pid) free(cjit->write_pid); if(cjit->entry) free(cjit->entry); if(cjit->output_filename) free(cjit->output_filename); - if(cjit->TCC) tcc_delete(cjit->TCC); + if(cjit->TCC) tcc_delete(tcc(cjit)); free(cjit); } +// wrappers to make TCC opaque +void cjit_set_output(CJITState *cjit, int output) { + if(output>5 || output<1) + _err("%s: invalid output: %i",__func__,output); + else + cjit->tcc_output = output; +} +void cjit_define_symbol(CJITState *cjit, const char *sym, const char *value) { + tcc_define_symbol(tcc(cjit),sym,value); + if(!cjit->quiet)_err("+D %s",sym,value?value:""); +} +void cjit_add_include_path(CJITState *cjit, const char *path) { + tcc_add_include_path(tcc(cjit), path); + if(!cjit->quiet)_err("+I %s",path); +} +// TODO: temporary, to be reimplemented in linker.c +void cjit_add_library_path(CJITState *cjit, const char *path) { + tcc_add_library_path(tcc(cjit), path); + if(!cjit->quiet)_err("+L %s",path); +} +// TODO: temporary, to be reimplemented in linker.c +void cjit_add_library(CJITState *cjit, const char *path) { + tcc_add_library(tcc(cjit), path); + if(!cjit->quiet)_err("+l %s",path); +} +void cjit_set_tcc_options(CJITState *cjit, const char *opts) { + tcc_set_options(tcc(cjit),opts); + if(!cjit->quiet)_err("+O %s",opts); +} + // stdout message free from context void _out(const char *fmt, ...) { char msg[MAX_STRING+4]; diff --git a/src/cjit.h b/src/cjit.h index 9c8e12d..3d0eeec 100644 --- a/src/cjit.h +++ b/src/cjit.h @@ -22,11 +22,10 @@ #include #include -#include // passed to cjit_exec with CJIT execution parameters struct CJITState { - TCCState *TCC; // the tinyCC context + void *TCC; // the tinyCC context char *tmpdir; // path to execution temporary directory char *write_pid; // filename to write the pid of execution char *entry; // entry point, default "main" if left NULL @@ -47,15 +46,35 @@ struct CJITState { typedef struct CJITState CJITState; extern CJITState* cjit_new(); -extern bool cjit_setup(CJITState *cjit); +// extern bool cjit_setup(CJITState *cjit); extern bool cjit_status(CJITState *cjit); -extern bool cjit_compile_file(CJITState *cjit, const char *_path); + +// setup functions to add source and libs extern bool cjit_add_file(CJITState *cjit, const char *path); +extern bool cjit_add_source(CJITState *cjit, const char *path); +extern bool cjit_add_buffer(CJITState *cjit, const char *buffer); -extern int cjit_exec(CJITState *cjit, int argc, char **argv); +// end game functions +extern int cjit_link(CJITState *cjit); // link and create an executable file +extern int cjit_exec(CJITState *cjit, int argc, char **argv); // exec in mem +extern bool cjit_compile_file(CJITState *cjit, const char *_path); // compile a single file to a bytecode object extern void cjit_free(CJITState *CJIT); + +#define MEM 1 /* output will be run in memory */ +#define EXE 2 /* executable file */ +#define OBJ 3 /* object file */ +#define DLL 4 /* dynamic library */ +#define PRE 5 /* only preprocess */ +void cjit_set_output(CJITState *cjit, int output); + +void cjit_define_symbol(CJITState *cjit, const char *sym, const char *value); +void cjit_add_include_path(CJITState *cjit, const char *path); +void cjit_add_library_path(CJITState *cjit, const char *path); +void cjit_add_library(CJITState *cjit, const char *path); +void cjit_set_tcc_options(CJITState *cjit, const char *opts); + ///////////// // from embedded.c - generated at build time extern bool extract_assets(CJITState *CJIT); diff --git a/src/main.c b/src/main.c index 09168e6..55889d1 100644 --- a/src/main.c +++ b/src/main.c @@ -125,33 +125,33 @@ int main(int argc, char **argv) { int _res; _res = parse_value(opt.arg); if(_res==0) { // -Dsym (no key=value) - tcc_define_symbol(CJIT->TCC, opt.arg, NULL); + cjit_define_symbol(CJIT, opt.arg, NULL); } else if(_res>0) { // -Dkey=value - tcc_define_symbol(CJIT->TCC, opt.arg, &opt.arg[_res]); + cjit_define_symbol(CJIT, opt.arg, &opt.arg[_res]); } else { // invalid char _err("Invalid char used in -D define symbol: %s", opt.arg); cjit_free(CJIT); exit(1); } } else if (c == 'c') { // don't link or execute, just compile to .o - CJIT->tcc_output = TCC_OUTPUT_OBJ; + cjit_set_output(CJIT, OBJ); } else if (c == 'o') { // override output filename if(CJIT->output_filename) free(CJIT->output_filename); CJIT->output_filename = malloc(strlen(opt.arg)+1); strcpy(CJIT->output_filename,opt.arg); - CJIT->tcc_output = TCC_OUTPUT_EXE; + cjit_set_output(CJIT, EXE); } else if (c == 'L') { // library path if(!CJIT->quiet)_err("lib path: %s",opt.arg); - tcc_add_library_path(CJIT->TCC, opt.arg); + cjit_add_library_path(CJIT, opt.arg); } else if (c == 'l') { // library link if(!CJIT->quiet)_err("lib: %s",opt.arg); - tcc_add_library(CJIT->TCC, opt.arg); + cjit_add_library(CJIT, opt.arg); } else if (c == 'C') { // cflags compiler options if(!CJIT->quiet)_err("cflags: %s",opt.arg); - tcc_set_options(CJIT->TCC, opt.arg); + cjit_set_tcc_options(CJIT->TCC, opt.arg); } else if (c == 'I') { // include paths in cflags if(!CJIT->quiet)_err("inc: %s",opt.arg); - tcc_add_include_path(CJIT->TCC, opt.arg); + cjit_add_include_path(CJIT, opt.arg); } else if (c == 'e') { // entry point (default main) if(!CJIT->quiet)_err("entry: %s",opt.arg); if(CJIT->entry) free(CJIT->entry); @@ -194,6 +194,7 @@ int main(int argc, char **argv) { } if(!CJIT->quiet) _err("CJIT %s by Dyne.org",VERSION); +#if 0 // If no arguments then start the REPL if (argc == 0 ) { _err("No input file: interactive mode"); @@ -210,6 +211,7 @@ int main(int argc, char **argv) { } // end of REPL ///////////////////////////////////// +#endif // number of args at the left hand of arg separator, or all of them int left_args = arg_separator? arg_separator: argc; @@ -228,23 +230,22 @@ int main(int argc, char **argv) { _err("Error reading from standard input"); goto endgame; } - cjit_setup(CJIT); - if( tcc_compile_string(CJIT->TCC,stdin_code) < 0) { + if( cjit_add_buffer(CJIT->TCC,stdin_code) < 0) { _err("Code runtime error in stdin"); free(stdin_code); goto endgame; } + free(stdin_code); // end of STDIN //////////////// - } else if(CJIT->tcc_output==3) { + } else if(CJIT->tcc_output==OBJ) { ///////////////////////////// // Compile one .c file to .o if(left_args - opt.ind != 1) { _err("Compiling to object files supports only one file argument"); goto endgame; } - cjit_setup(CJIT); //if(!CJIT->quiet)_err("Compile: %s",argv[opt.ind]); res = cjit_compile_file(CJIT, argv[opt.ind]) ?0:1; // 0 on success goto endgame; @@ -267,8 +268,7 @@ int main(int argc, char **argv) { _err("Error reading from standard input"); goto endgame; } - cjit_setup(CJIT); - if( tcc_compile_string(CJIT->TCC,stdin_code) < 0) { + if( cjit_add_buffer(CJIT,stdin_code) < 0) { _err("Code runtime error in stdin"); free(stdin_code); goto endgame; @@ -283,8 +283,7 @@ int main(int argc, char **argv) { // compile to executable if(CJIT->output_filename) { _err("Create executable: %s", CJIT->output_filename); - cjit_setup(CJIT); - if(tcc_output_file(CJIT->TCC,CJIT->output_filename)<0) { + if(cjit_link(CJIT)<0) { _err("Error in linker compiling to file: %s", CJIT->output_filename); res = 1;