/* Expression Evaluator Library (NS-EEL) v2 Copyright (C) 2004-2013 Cockos Incorporated Copyright (C) 1999-2003 Nullsoft, Inc. nseel-compiler.c This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "ns-eel-int.h" #include "denormal.h" #include "wdlcstring.h" #include #include #include #include #if !defined(EEL_TARGET_PORTABLE) && !defined(_WIN32) #include #include #include #endif #include "glue_x86.h" #ifdef _WIN64 #include "glue_x86_64.h" #endif // _WIN64 #define NSEEL_VARS_MALLOC_CHUNKSIZE 8 //#define LOG_OPT //#define EEL_PRINT_FAILS //#define EEL_VALIDATE_WORKTABLE_USE //#define EEL_VALIDATE_FSTUBS #ifdef EEL_PRINT_FAILS #ifdef _WIN32 #define RET_MINUS1_FAIL(x) { OutputDebugString(x); return -1; } #else #define RET_MINUS1_FAIL(x) { printf("%s\n",x); return -1; } #endif #else #define RET_MINUS1_FAIL(x) return -1; #endif #ifdef EEL_DUMP_OPS FILE *g_eel_dump_fp, *g_eel_dump_fp2; #endif #ifdef EEL_VALIDATE_WORKTABLE_USE #define MIN_COMPUTABLE_SIZE 0 #define COMPUTABLE_EXTRA_SPACE 64 // safety buffer, if EEL_VALIDATE_WORKTABLE_USE set, used for magic-value-checking #else #define MIN_COMPUTABLE_SIZE 32 // always use at least this big of a temp storage table (and reset the temp ptr when it goes past this boundary) #define COMPUTABLE_EXTRA_SPACE 16 // safety buffer, if EEL_VALIDATE_WORKTABLE_USE set, used for magic-value-checking #endif /* P1 is rightmost parameter P2 is second rightmost, if any P3 is third rightmost, if any registers on x86 are (RAX etc on x86-64) P1(ret) EAX P2 EDI P3 ECX WTP RSI x86_64: r12 is a pointer to ram_state.blocks x86_64: r13 is a pointer to closenessfactor registers on PPC are: P1(ret) r3 P2 r14 P3 r15 WTP r16 (r17 has the original value) r13 is a pointer to ram_state.blocks ppc uses f31 and f30 and others for certain constants */ #ifdef EEL_TARGET_PORTABLE #define EEL_DOESNT_NEED_EXEC_PERMS #include "glue_port.h" #elif defined(__ppc__) #include "glue_ppc.h" #elif defined(__aarch64__) #include "glue_aarch64.h" #elif defined(__arm__) || (defined (_M_ARM) && _M_ARM == 7) #include "glue_arm.h" #elif defined(_WIN64) || defined(__LP64__) #include "glue_x86_64.h" #else #include "glue_x86.h" #endif #ifndef GLUE_INVSQRT_NEEDREPL #define GLUE_INVSQRT_NEEDREPL 0 #endif // used by //#eel-no-optimize:xxx, in ctx->optimizeDisableFlags #define OPTFLAG_NO_OPTIMIZE 1 #define OPTFLAG_NO_FPSTACK 2 #define OPTFLAG_NO_INLINEFUNC 4 #define OPTFLAG_FULL_DENORMAL_CHECKS 8 // if set, denormals/NaN are always filtered on assign #define OPTFLAG_NO_DENORMAL_CHECKS 16 // if set and FULL not set, denormals/NaN are never filtered on assign #define DENORMAL_CLEARING_THRESHOLD 1.0e-50 // when adding/subtracting a constant, assume if it's greater than this, it will clear denormal (the actual value is probably 10^-290...) #define MAX_SUB_NAMESPACES 32 typedef struct { const char *namespacePathToThis; const char *subParmInfo[MAX_SUB_NAMESPACES]; } namespaceInformation; static int nseel_evallib_stats[5]; // source bytes, static code bytes, call code bytes, data bytes, segments int *NSEEL_getstats() { return nseel_evallib_stats; } static int findLineNumber(const char *exp, int byteoffs) { int lc=0; while (byteoffs-->0 && *exp) if (*exp++ =='\n') lc++; return lc; } static int nseel_vms_referencing_globallist_cnt; nseel_globalVarItem *nseel_globalreg_list; static EEL_F *get_global_var(compileContext *ctx, const char *gv, int addIfNotPresent); static void *__newBlock(llBlock **start,int size, int wantMprotect); #define OPCODE_IS_TRIVIAL(x) ((x)->opcodeType <= OPCODETYPE_VARPTRPTR) enum { OPCODETYPE_DIRECTVALUE=0, OPCODETYPE_DIRECTVALUE_TEMPSTRING, // like directvalue, but will generate a new tempstring value on generate OPCODETYPE_VALUE_FROM_NAMESPACENAME, // this.* or namespace.* are encoded this way OPCODETYPE_VARPTR, OPCODETYPE_VARPTRPTR, OPCODETYPE_FUNC1, OPCODETYPE_FUNC2, OPCODETYPE_FUNC3, OPCODETYPE_FUNCX, OPCODETYPE_MOREPARAMS, OPCODETYPE_INVALID, }; struct opcodeRec { int opcodeType; int fntype; void *fn; union { struct opcodeRec *parms[3]; struct { double directValue; EEL_F *valuePtr; // if direct value, valuePtr can be cached } dv; } parms; int namespaceidx; // OPCODETYPE_VALUE_FROM_NAMESPACENAME (relname is either empty or blah) // OPCODETYPE_VARPTR if it represents a global variable, will be nonempty // OPCODETYPE_FUNC* with fntype=FUNCTYPE_EELFUNC const char *relname; }; static void *newTmpBlock(compileContext *ctx, int size) { const int align = 8; const int a1=align-1; char *p=(char*)__newBlock(&ctx->tmpblocks_head,size+a1, 0); return p+((align-(((INT_PTR)p)&a1))&a1); } static void *__newBlock_align(compileContext *ctx, int size, int align, int isForCode) { const int a1=align-1; char *p=(char*)__newBlock( ( isForCode < 0 ? (isForCode == -2 ? &ctx->pblocks : &ctx->tmpblocks_head) : isForCode > 0 ? &ctx->blocks_head : &ctx->blocks_head_data) ,size+a1, isForCode>0); return p+((align-(((INT_PTR)p)&a1))&a1); } static opcodeRec *newOpCode(compileContext *ctx, const char *str, int opType) { const size_t strszfull = str ? strlen(str) : 0; const size_t str_sz = wdl_min(NSEEL_MAX_VARIABLE_NAMELEN, strszfull); opcodeRec *rec = (opcodeRec*)__newBlock_align(ctx, (int) (sizeof(opcodeRec) + (str_sz>0 ? str_sz+1 : 0)), 8, ctx->isSharedFunctions ? 0 : -1); if (rec) { memset(rec,0,sizeof(*rec)); rec->opcodeType = opType; if (str_sz > 0) { char *p = (char *)(rec+1); memcpy(p,str,str_sz); p[str_sz]=0; rec->relname = p; } else { rec->relname = ""; } } return rec; } #define newCodeBlock(x,a) __newBlock_align(ctx,x,a,1) #define newDataBlock(x,a) __newBlock_align(ctx,x,a,0) #define newCtxDataBlock(x,a) __newBlock_align(ctx,x,a,-2) static void freeBlocks(llBlock **start); static int __growbuf_resize(eel_growbuf *buf, int newsize) { if (newsize<0) { free(buf->ptr); buf->ptr=NULL; buf->alloc=buf->size=0; return 0; } if (newsize > buf->alloc) { const int newalloc = newsize + 4096 + newsize/2; void *newptr = realloc(buf->ptr,newalloc); if (!newptr) { newptr = malloc(newalloc); if (!newptr) return 1; if (buf->ptr && buf->size) memcpy(newptr,buf->ptr,buf->size); free(buf->ptr); buf->ptr=newptr; } else buf->ptr = newptr; buf->alloc=newalloc; } buf->size = newsize; return 0; } #ifndef DECL_ASMFUNC #define DECL_ASMFUNC(x) \ void nseel_asm_##x(void); \ void nseel_asm_##x##_end(void); void _asm_megabuf(void); void _asm_megabuf_end(void); void _asm_gmegabuf(void); void _asm_gmegabuf_end(void); #endif DECL_ASMFUNC(booltofp) DECL_ASMFUNC(fptobool) DECL_ASMFUNC(fptobool_rev) DECL_ASMFUNC(sin) DECL_ASMFUNC(cos) DECL_ASMFUNC(tan) DECL_ASMFUNC(1pdd) DECL_ASMFUNC(2pdd) DECL_ASMFUNC(2pdds) DECL_ASMFUNC(1pp) DECL_ASMFUNC(2pp) DECL_ASMFUNC(sqr) DECL_ASMFUNC(sqrt) DECL_ASMFUNC(log) DECL_ASMFUNC(log10) DECL_ASMFUNC(abs) DECL_ASMFUNC(min) DECL_ASMFUNC(max) DECL_ASMFUNC(min_fp) DECL_ASMFUNC(max_fp) DECL_ASMFUNC(sig) DECL_ASMFUNC(sign) DECL_ASMFUNC(band) DECL_ASMFUNC(bor) DECL_ASMFUNC(bnot) DECL_ASMFUNC(bnotnot) DECL_ASMFUNC(if) DECL_ASMFUNC(fcall) DECL_ASMFUNC(repeat) DECL_ASMFUNC(repeatwhile) DECL_ASMFUNC(equal) DECL_ASMFUNC(equal_exact) DECL_ASMFUNC(notequal_exact) DECL_ASMFUNC(notequal) DECL_ASMFUNC(below) DECL_ASMFUNC(above) DECL_ASMFUNC(beloweq) DECL_ASMFUNC(aboveeq) DECL_ASMFUNC(assign) DECL_ASMFUNC(assign_fromfp) DECL_ASMFUNC(assign_fast) DECL_ASMFUNC(assign_fast_fromfp) DECL_ASMFUNC(add) DECL_ASMFUNC(sub) DECL_ASMFUNC(add_op) DECL_ASMFUNC(sub_op) DECL_ASMFUNC(add_op_fast) DECL_ASMFUNC(sub_op_fast) DECL_ASMFUNC(mul) DECL_ASMFUNC(div) DECL_ASMFUNC(mul_op) DECL_ASMFUNC(div_op) DECL_ASMFUNC(mul_op_fast) DECL_ASMFUNC(div_op_fast) DECL_ASMFUNC(mod) DECL_ASMFUNC(shl) DECL_ASMFUNC(shr) DECL_ASMFUNC(mod_op) DECL_ASMFUNC(or) DECL_ASMFUNC(or0) DECL_ASMFUNC(xor) DECL_ASMFUNC(xor_op) DECL_ASMFUNC(and) DECL_ASMFUNC(or_op) DECL_ASMFUNC(and_op) DECL_ASMFUNC(uplus) DECL_ASMFUNC(uminus) DECL_ASMFUNC(invsqrt) DECL_ASMFUNC(dbg_getstackptr) #ifdef NSEEL_EEL1_COMPAT_MODE DECL_ASMFUNC(exec2) #endif DECL_ASMFUNC(stack_push) DECL_ASMFUNC(stack_pop) DECL_ASMFUNC(stack_pop_fast) // just returns value, doesn't mod param DECL_ASMFUNC(stack_peek) DECL_ASMFUNC(stack_peek_int) DECL_ASMFUNC(stack_peek_top) DECL_ASMFUNC(stack_exch) static void *NSEEL_PProc_GRAM(void *data, int data_size, compileContext *ctx) { if (data_size>0) data=EEL_GLUE_set_immediate(data, (INT_PTR)ctx->gram_blocks); return data; } static void *NSEEL_PProc_Stack(void *data, int data_size, compileContext *ctx) { codeHandleType *ch=ctx->tmpCodeHandle; if (data_size>0) { UINT_PTR m1=(UINT_PTR)(NSEEL_STACK_SIZE * sizeof(EEL_F) - 1); UINT_PTR stackptr = ((UINT_PTR) (&ch->stack)); ch->want_stack=1; if (!ch->stack) ch->stack = newDataBlock(NSEEL_STACK_SIZE*sizeof(EEL_F),NSEEL_STACK_SIZE*sizeof(EEL_F)); data=EEL_GLUE_set_immediate(data, stackptr); data=EEL_GLUE_set_immediate(data, m1); // and data=EEL_GLUE_set_immediate(data, ((UINT_PTR)ch->stack&~m1)); //or } return data; } static void *NSEEL_PProc_Stack_PeekInt(void *data, int data_size, compileContext *ctx, INT_PTR offs) { codeHandleType *ch=ctx->tmpCodeHandle; if (data_size>0) { UINT_PTR m1=(UINT_PTR)(NSEEL_STACK_SIZE * sizeof(EEL_F) - 1); UINT_PTR stackptr = ((UINT_PTR) (&ch->stack)); ch->want_stack=1; if (!ch->stack) ch->stack = newDataBlock(NSEEL_STACK_SIZE*sizeof(EEL_F),NSEEL_STACK_SIZE*sizeof(EEL_F)); data=EEL_GLUE_set_immediate(data, stackptr); data=EEL_GLUE_set_immediate(data, offs); data=EEL_GLUE_set_immediate(data, m1); // and data=EEL_GLUE_set_immediate(data, ((UINT_PTR)ch->stack&~m1)); //or } return data; } static void *NSEEL_PProc_Stack_PeekTop(void *data, int data_size, compileContext *ctx) { codeHandleType *ch=ctx->tmpCodeHandle; if (data_size>0) { UINT_PTR stackptr = ((UINT_PTR) (&ch->stack)); ch->want_stack=1; if (!ch->stack) ch->stack = newDataBlock(NSEEL_STACK_SIZE*sizeof(EEL_F),NSEEL_STACK_SIZE*sizeof(EEL_F)); data=EEL_GLUE_set_immediate(data, stackptr); } return data; } #if defined(_MSC_VER) && _MSC_VER >= 1400 //static double __floor(double a) { return floor(a); } //static double __ceil(double a) { return ceil(a); } #define floor __floor #define ceil __ceil #endif #ifdef NSEEL_EEL1_COMPAT_MODE static double eel1band(double a, double b) { return (fabs(a)>NSEEL_CLOSEFACTOR && fabs(b) > NSEEL_CLOSEFACTOR) ? 1.0 : 0.0; } static double eel1bor(double a, double b) { return (fabs(a)>NSEEL_CLOSEFACTOR || fabs(b) > NSEEL_CLOSEFACTOR) ? 1.0 : 0.0; } static double eel1sigmoid(double x, double constraint) { double t = (1+exp(-x * (constraint))); return fabs(t)>NSEEL_CLOSEFACTOR ? 1.0/t : 0; } #endif #define FUNCTIONTYPE_PARAMETERCOUNTMASK 0xff #define BIF_NPARAMS_MASK 0x7ffff00 #define BIF_RETURNSONSTACK 0x0000100 #define BIF_LASTPARMONSTACK 0x0000200 #define BIF_RETURNSBOOL 0x0000400 #define BIF_LASTPARM_ASBOOL 0x0000800 // 0x00?0000 -- taken by FP stack flags #define BIF_TAKES_VARPARM 0x0400000 #define BIF_TAKES_VARPARM_EX 0x0C00000 // this is like varparm but check count exactly #define BIF_WONTMAKEDENORMAL 0x0100000 #define BIF_CLEARDENORMAL 0x0200000 #if defined(GLUE_HAS_FXCH) && GLUE_MAX_FPSTACK_SIZE > 0 #define BIF_SECONDLASTPARMST 0x0001000 // use with BIF_LASTPARMONSTACK only (last two parameters get passed on fp stack) #define BIF_LAZYPARMORDERING 0x0002000 // allow optimizer to avoid fxch when using BIF_TWOPARMSONFPSTACK_LAZY etc #define BIF_REVERSEFPORDER 0x0004000 // force a fxch (reverse order of last two parameters on fp stack, used by comparison functions) #ifndef BIF_FPSTACKUSE #define BIF_FPSTACKUSE(x) (((x)>=0&&(x)<8) ? ((7-(x))<<16):0) #endif #ifndef BIF_GETFPSTACKUSE #define BIF_GETFPSTACKUSE(x) (7 - (((x)>>16)&7)) #endif #else // do not support fp stack use unless GLUE_HAS_FXCH and GLUE_MAX_FPSTACK_SIZE>0 #define BIF_SECONDLASTPARMST 0 #define BIF_LAZYPARMORDERING 0 #define BIF_REVERSEFPORDER 0 #define BIF_FPSTACKUSE(x) 0 #define BIF_GETFPSTACKUSE(x) 0 #endif #define BIF_TWOPARMSONFPSTACK (BIF_SECONDLASTPARMST|BIF_LASTPARMONSTACK) #define BIF_TWOPARMSONFPSTACK_LAZY (BIF_LAZYPARMORDERING|BIF_SECONDLASTPARMST|BIF_LASTPARMONSTACK) #ifndef GLUE_HAS_NATIVE_TRIGSQRTLOG static double sqrt_fabs(double a) { return sqrt(fabs(a)); } #endif EEL_F NSEEL_CGEN_CALL nseel_int_rand(EEL_F f); #define FNPTR_HAS_CONDITIONAL_EXEC(op) \ (op->fntype == FN_LOGICAL_AND || \ op->fntype == FN_LOGICAL_OR || \ op->fntype == FN_IF_ELSE || \ op->fntype == FN_WHILE || \ op->fntype == FN_LOOP) static functionType fnTable1[] = { #ifndef GLUE_HAS_NATIVE_TRIGSQRTLOG { "sin", nseel_asm_1pdd,nseel_asm_1pdd_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_WONTMAKEDENORMAL, {&sin} }, { "cos", nseel_asm_1pdd,nseel_asm_1pdd_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_CLEARDENORMAL, {&cos} }, { "tan", nseel_asm_1pdd,nseel_asm_1pdd_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK, {&tan} }, { "sqrt", nseel_asm_1pdd,nseel_asm_1pdd_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_WONTMAKEDENORMAL, {&sqrt_fabs}, }, { "log", nseel_asm_1pdd,nseel_asm_1pdd_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK, {&log} }, { "log10", nseel_asm_1pdd,nseel_asm_1pdd_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK, {&log10} }, #else { "sin", nseel_asm_sin,nseel_asm_sin_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_WONTMAKEDENORMAL|BIF_FPSTACKUSE(1) }, { "cos", nseel_asm_cos,nseel_asm_cos_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_CLEARDENORMAL|BIF_FPSTACKUSE(1) }, { "tan", nseel_asm_tan,nseel_asm_tan_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(1) }, { "sqrt", nseel_asm_sqrt,nseel_asm_sqrt_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(1)|BIF_WONTMAKEDENORMAL }, { "log", nseel_asm_log,nseel_asm_log_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(3), }, { "log10", nseel_asm_log10,nseel_asm_log10_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(3), }, #endif { "asin", nseel_asm_1pdd,nseel_asm_1pdd_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK, {&asin}, }, { "acos", nseel_asm_1pdd,nseel_asm_1pdd_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK, {&acos}, }, { "atan", nseel_asm_1pdd,nseel_asm_1pdd_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK, {&atan}, }, { "atan2", nseel_asm_2pdd,nseel_asm_2pdd_end, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK, {&atan2}, }, { "exp", nseel_asm_1pdd,nseel_asm_1pdd_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK, {&exp}, }, { "abs", nseel_asm_abs,nseel_asm_abs_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(0)|BIF_WONTMAKEDENORMAL }, { "sqr", nseel_asm_sqr,nseel_asm_sqr_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(1) }, { "min", nseel_asm_min,nseel_asm_min_end, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_FPSTACKUSE(3)|BIF_WONTMAKEDENORMAL }, { "max", nseel_asm_max,nseel_asm_max_end, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_FPSTACKUSE(3)|BIF_WONTMAKEDENORMAL }, { "sign", nseel_asm_sign,nseel_asm_sign_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL, }, { "rand", nseel_asm_1pdd,nseel_asm_1pdd_end, 1|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_CLEARDENORMAL, {&nseel_int_rand}, }, //{ "floor", nseel_asm_1pdd,nseel_asm_1pdd_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_CLEARDENORMAL, {&floor} }, //{ "ceil", nseel_asm_1pdd,nseel_asm_1pdd_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_CLEARDENORMAL, {&ceil} }, { "invsqrt", nseel_asm_invsqrt,nseel_asm_invsqrt_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(3), {GLUE_INVSQRT_NEEDREPL} }, { "__dbg_getstackptr", nseel_asm_dbg_getstackptr,nseel_asm_dbg_getstackptr_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(1), }, #ifdef NSEEL_EEL1_COMPAT_MODE { "sigmoid", nseel_asm_2pdd,nseel_asm_2pdd_end, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK, {&eel1sigmoid}, }, // these differ from _and/_or, they always evaluate both... { "band", nseel_asm_2pdd,nseel_asm_2pdd_end, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK|BIF_CLEARDENORMAL , {&eel1band}, }, { "bor", nseel_asm_2pdd,nseel_asm_2pdd_end, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK|BIF_CLEARDENORMAL , {&eel1bor}, }, {"exec2",nseel_asm_exec2,nseel_asm_exec2_end,2|NSEEL_NPARAMS_FLAG_CONST|BIF_WONTMAKEDENORMAL}, {"exec3",nseel_asm_exec2,nseel_asm_exec2_end,3|NSEEL_NPARAMS_FLAG_CONST|BIF_WONTMAKEDENORMAL}, #endif // end EEL1 compat {"freembuf",_asm_generic1parm,_asm_generic1parm_end,1,{&__NSEEL_RAM_MemFree},NSEEL_PProc_RAM}, {"memcpy",_asm_generic3parm,_asm_generic3parm_end,3,{&__NSEEL_RAM_MemCpy},NSEEL_PProc_RAM}, {"memset",_asm_generic3parm,_asm_generic3parm_end,3,{&__NSEEL_RAM_MemSet},NSEEL_PProc_RAM}, {"__memtop",_asm_generic1parm,_asm_generic1parm_end,1,{&__NSEEL_RAM_MemTop},NSEEL_PProc_RAM}, {"mem_set_values",_asm_generic2parm_retd,_asm_generic2parm_retd_end,2|BIF_TAKES_VARPARM|BIF_RETURNSONSTACK,{&__NSEEL_RAM_Mem_SetValues},NSEEL_PProc_RAM}, {"mem_get_values",_asm_generic2parm_retd,_asm_generic2parm_retd_end,2|BIF_TAKES_VARPARM|BIF_RETURNSONSTACK,{&__NSEEL_RAM_Mem_GetValues},NSEEL_PProc_RAM}, {"stack_push",nseel_asm_stack_push,nseel_asm_stack_push_end,1|BIF_FPSTACKUSE(0),{0,},NSEEL_PProc_Stack}, {"stack_pop",nseel_asm_stack_pop,nseel_asm_stack_pop_end,1|BIF_FPSTACKUSE(1),{0,},NSEEL_PProc_Stack}, {"stack_peek",nseel_asm_stack_peek,nseel_asm_stack_peek_end,1|NSEEL_NPARAMS_FLAG_CONST|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(0),{0,},NSEEL_PProc_Stack}, {"stack_exch",nseel_asm_stack_exch,nseel_asm_stack_exch_end,1|BIF_FPSTACKUSE(1), {0,},NSEEL_PProc_Stack_PeekTop}, }; static eel_function_table default_user_funcs; static int functable_lowerbound(functionType *list, int list_sz, const char *name, int *ismatch) { int a = 0, c = list_sz; while (a != c) { const int b = (a+c)/2; const int cmp = stricmp(name,list[b].name); if (cmp > 0) a = b+1; else if (cmp < 0) c = b; else { *ismatch = 1; return b; } } *ismatch = 0; return a; } static int funcTypeCmp(const void *a, const void *b) { return stricmp(((functionType*)a)->name,((functionType*)b)->name); } functionType *nseel_getFunctionByName(compileContext *ctx, const char *name, int *mchk) { eel_function_table *tab = ctx && ctx->registered_func_tab ? ctx->registered_func_tab : &default_user_funcs; static char sorted; const int fn1size = (int) (sizeof(fnTable1)/sizeof(fnTable1[0])); int idx,match; if (!sorted) { NSEEL_HOSTSTUB_EnterMutex(); if (!sorted) qsort(fnTable1,fn1size,sizeof(fnTable1[0]),funcTypeCmp); sorted=1; NSEEL_HOSTSTUB_LeaveMutex(); } idx=functable_lowerbound(fnTable1,fn1size,name,&match); if (match) return fnTable1+idx; if ((!ctx || !(ctx->current_compile_flags&NSEEL_CODE_COMPILE_FLAG_ONLY_BUILTIN_FUNCTIONS)) && tab->list) { idx=functable_lowerbound(tab->list,tab->list_size,name,&match); if (match) { if (mchk) { while (idx>0 && !stricmp(tab->list[idx-1].name,name)) idx--; *mchk = tab->list_size - 1 - idx; } return tab->list + idx; } } return NULL; } int NSEEL_init() // returns 0 on success { #ifdef EEL_VALIDATE_FSTUBS int a; for (a=0;a < sizeof(fnTable1)/sizeof(fnTable1[0]);a++) { char *code_startaddr = (char*)fnTable1[a].afunc; char *endp = (char *)fnTable1[a].func_e; // validate int sz=0; char *f=(char *)GLUE_realAddress(code_startaddr,endp,&sz); if (f+sz > endp) { #ifdef _WIN32 OutputDebugString("bad eel function stub\n"); #else printf("bad eel function stub\n"); #endif *(char *)NULL = 0; } } #ifdef _WIN32 OutputDebugString("eel function stub (builtin) validation complete\n"); #else printf("eel function stub (builtin) validation complete\n"); #endif #endif NSEEL_quit(); return 0; } void NSEEL_quit() { free(default_user_funcs.list); default_user_funcs.list = NULL; default_user_funcs.list_size = 0; } void NSEEL_addfunc_varparm_ex(const char *name, int min_np, int want_exact, NSEEL_PPPROC pproc, EEL_F (NSEEL_CGEN_CALL *fptr)(void *, INT_PTR, EEL_F **), eel_function_table *destination) { const int sz = (int) ((char *)_asm_generic2parm_retd_end-(char *)_asm_generic2parm_retd); NSEEL_addfunctionex2(name,min_np|(want_exact?BIF_TAKES_VARPARM_EX:BIF_TAKES_VARPARM),(char *)_asm_generic2parm_retd,sz,pproc,fptr,NULL,destination); } void NSEEL_addfunc_ret_type(const char *name, int np, int ret_type, NSEEL_PPPROC pproc, void *fptr, eel_function_table *destination) // ret_type=-1 for bool, 1 for value, 0 for ptr { char *stub=NULL; int stubsz=0; #define DOSTUB(np) { \ stub = (ret_type == 1 ? (char*)_asm_generic##np##parm_retd : (char*)_asm_generic##np##parm); \ stubsz = (int) ((ret_type == 1 ? (char*)_asm_generic##np##parm_retd_end : (char *)_asm_generic##np##parm_end) - stub); \ } if (np == 1) DOSTUB(1) else if (np == 2) DOSTUB(2) else if (np == 3) DOSTUB(3) #undef DOSTUB if (stub) NSEEL_addfunctionex2(name,np|(ret_type == -1 ? BIF_RETURNSBOOL:0), stub, stubsz, pproc,fptr,NULL,destination); } void NSEEL_addfunctionex2(const char *name, int nparms, char *code_startaddr, int code_len, NSEEL_PPPROC pproc, void *fptr, void *fptr2, eel_function_table *destination) { const int list_size_chunk = 128; functionType *r; if (!destination) destination = &default_user_funcs; if (!destination->list || !(destination->list_size & (list_size_chunk-1))) { void *nv = realloc(destination->list, (destination->list_size + list_size_chunk)*sizeof(functionType)); if (!nv) return; destination->list = (functionType *)nv; } if (destination->list) { int match,idx; idx=functable_lowerbound(destination->list,destination->list_size,name,&match); #ifdef EEL_VALIDATE_FSTUBS { char *endp = code_startaddr+code_len; // validate int sz=0; char *f=(char *)GLUE_realAddress(code_startaddr,endp,&sz); if (f+sz > endp) { #ifdef _WIN32 OutputDebugString("bad eel function stub\n"); #else printf("bad eel function stub\n"); #endif *(char *)NULL = 0; } #ifdef _WIN32 OutputDebugString(name); OutputDebugString(" - validated eel function stub\n"); #else printf("eel function stub validation complete for %s\n",name); #endif } #endif r = destination->list + idx; if (idx < destination->list_size) memmove(r + 1, r, (destination->list_size - idx) * sizeof(functionType)); destination->list_size++; memset(r, 0, sizeof(functionType)); if (!(nparms & BIF_RETURNSBOOL)) { if (code_startaddr == (void *)&_asm_generic1parm_retd || code_startaddr == (void *)&_asm_generic2parm_retd || code_startaddr == (void *)&_asm_generic3parm_retd) { nparms |= BIF_RETURNSONSTACK; } } r->nParams = nparms; r->name = name; r->afunc = code_startaddr; r->func_e = code_startaddr + code_len; r->pProc = pproc; r->replptrs[0] = fptr; r->replptrs[1] = fptr2; } } //--------------------------------------------------------------------------------------------------------------- static void freeBlocks(llBlock **start) { llBlock *s=*start; *start=0; while (s) { llBlock *llB = s->next; free(s); s=llB; } } //--------------------------------------------------------------------------------------------------------------- static void *__newBlock(llBlock **start, int size, int wantMprotect) { #if !defined(EEL_DOESNT_NEED_EXEC_PERMS) && defined(_WIN32) DWORD ov; UINT_PTR offs,eoffs; #endif llBlock *llb; int alloc_size; if (*start && (LLB_DSIZE - (*start)->sizeused) >= size) { void *t=(*start)->block+(*start)->sizeused; (*start)->sizeused+=(size+7)&~7; return t; } alloc_size=sizeof(llBlock); if ((int)size > LLB_DSIZE) alloc_size += size - LLB_DSIZE; llb = (llBlock *)malloc(alloc_size); // grab bigger block if absolutely necessary (heh) if (!llb) return NULL; #ifndef EEL_DOESNT_NEED_EXEC_PERMS if (wantMprotect) { #ifdef _WIN32 offs=((UINT_PTR)llb)&~4095; eoffs=((UINT_PTR)llb + alloc_size + 4095)&~4095; VirtualProtect((LPVOID)offs,eoffs-offs,PAGE_EXECUTE_READWRITE,&ov); // MessageBox(NULL,"vprotecting, yay\n","a",0); #else { static int pagesize = 0; if (!pagesize) { pagesize=sysconf(_SC_PAGESIZE); if (!pagesize) pagesize=4096; } uintptr_t offs,eoffs; offs=((uintptr_t)llb)&~(pagesize-1); eoffs=((uintptr_t)llb + alloc_size + pagesize-1)&~(pagesize-1); mprotect((void*)offs,eoffs-offs,PROT_WRITE|PROT_READ|PROT_EXEC); } #endif } #endif llb->sizeused=(size+7)&~7; llb->next = *start; *start = llb; return llb->block; } //--------------------------------------------------------------------------------------------------------------- opcodeRec *nseel_createCompiledValue(compileContext *ctx, EEL_F value) { opcodeRec *r=newOpCode(ctx,NULL,OPCODETYPE_DIRECTVALUE); if (r) { r->parms.dv.directValue = value; } return r; } opcodeRec *nseel_createCompiledValuePtr(compileContext *ctx, EEL_F *addrValue, const char *namestr) { opcodeRec *r=newOpCode(ctx,namestr,OPCODETYPE_VARPTR); if (!r) return 0; r->parms.dv.valuePtr=addrValue; return r; } static int validate_varname_for_function(compileContext *ctx, const char *name) { if (!ctx->function_curName || !ctx->function_globalFlag) return 1; if (ctx->function_localTable_Size[2] > 0 && ctx->function_localTable_Names[2]) { char * const * const namelist = ctx->function_localTable_Names[2]; const int namelist_sz = ctx->function_localTable_Size[2]; int i; const size_t name_len = strlen(name); for (i=0;i 1 && nmchk[l-1] == '*') { if (name_len >= l && !strnicmp(nmchk,name,l-1) && name[l-1]=='.') return 1; } else { if (name_len == l && !stricmp(nmchk,name)) return 1; } } } return 0; } opcodeRec *nseel_resolve_named_symbol(compileContext *ctx, opcodeRec *rec, int parmcnt, int *errOut) { const int isFunctionMode = parmcnt >= 0; int rel_prefix_len=0; int rel_prefix_idx=-2; int i; char match_parmcnt[4]={-1,-1,-1,-1}; // [3] is guess unsigned char match_parmcnt_pos=0; char *sname = (char *)rec->relname; int is_string_prefix = parmcnt < 0 && sname[0] == '#'; const char *prevent_function_calls = NULL; if (errOut) *errOut = 0; if (sname) sname += is_string_prefix; if (rec->opcodeType != OPCODETYPE_VARPTR || !sname || !sname[0]) return NULL; if (!isFunctionMode && !is_string_prefix && !strnicmp(sname,"reg",3) && isdigit(sname[3]) && isdigit(sname[4]) && !sname[5]) { EEL_F *a=get_global_var(ctx,sname,1); if (a) { rec->parms.dv.valuePtr = a; sname[0]=0; // for dump_ops compat really, but this shouldn't be needed anyway } return rec; } if (ctx->function_curName) { if (!strnicmp(sname,"this.",5)) { rel_prefix_len=5; rel_prefix_idx=-1; } else if (!stricmp(sname,"this")) { rel_prefix_len=4; rel_prefix_idx=-1; } // scan for parameters/local variables before user functions if (rel_prefix_idx < -1 && ctx->function_localTable_Size[0] > 0 && ctx->function_localTable_Names[0] && ctx->function_localTable_ValuePtrs) { char * const * const namelist = ctx->function_localTable_Names[0]; const int namelist_sz = ctx->function_localTable_Size[0]; for (i=0; i < namelist_sz; i++) { const char *p = namelist[i]; if (p) { if (!isFunctionMode && !is_string_prefix && !strnicmp(p,sname,NSEEL_MAX_VARIABLE_NAMELEN)) { rec->opcodeType = OPCODETYPE_VARPTRPTR; rec->parms.dv.valuePtr=(EEL_F *)(ctx->function_localTable_ValuePtrs+i); rec->parms.dv.directValue=0.0; return rec; } else { const size_t plen = strlen(p); if (plen > 1 && p[plen-1] == '*' && !strnicmp(p,sname,plen-1) && ((sname[plen-1] == '.'&&sname[plen]) || !sname[plen-1])) { rel_prefix_len=(int) (sname[plen-1] ? plen : plen-1); rel_prefix_idx=i; break; } } } } } // if instance name set, translate sname or sname.* into "this.sname.*" if (rel_prefix_idx < -1 && ctx->function_localTable_Size[1] > 0 && ctx->function_localTable_Names[1]) { char * const * const namelist = ctx->function_localTable_Names[1]; const int namelist_sz = ctx->function_localTable_Size[1]; const char *full_sname = rec->relname; // include # in checks for (i=0; i < namelist_sz; i++) { const char *p = namelist[i]; if (p && *p) { const size_t tl = strlen(p); if (!strnicmp(p,full_sname,tl) && (full_sname[tl] == 0 || full_sname[tl] == '.')) { rel_prefix_len=0; // treat as though this. prefixes is present rel_prefix_idx=-1; break; } } } } if (rel_prefix_idx >= -1) { ctx->function_usesNamespaces=1; } } // ctx->function_curName if (!isFunctionMode) { // instance variables if (rel_prefix_idx >= -1) { rec->opcodeType = OPCODETYPE_VALUE_FROM_NAMESPACENAME; rec->namespaceidx = rel_prefix_idx; if (rel_prefix_len > 0) { if (is_string_prefix) sname[-1] = '#'; memmove(sname, sname+rel_prefix_len, strlen(sname + rel_prefix_len) + 1); } } else { // no namespace index, so it must be a global if (!validate_varname_for_function(ctx,rec->relname)) { if (errOut) *errOut = 1; if (ctx->last_error_string[0]) lstrcatn(ctx->last_error_string, ", ", sizeof(ctx->last_error_string)); snprintf_append(ctx->last_error_string,sizeof(ctx->last_error_string),"global '%s' inaccessible",rec->relname); return NULL; } } return rec; } if (ctx->func_check) prevent_function_calls = ctx->func_check(sname,ctx->func_check_user); ////////// function mode // first off, while() and loop() are special and can't be overridden // if (parmcnt == 1 && !stricmp("while",sname) && !prevent_function_calls) { rec->opcodeType = OPCODETYPE_FUNC1; rec->fntype = FN_WHILE; return rec; } if (parmcnt == 2 && !stricmp("loop",sname) && !prevent_function_calls) { rec->opcodeType = OPCODETYPE_FUNC2; rec->fntype = FN_LOOP; return rec; } // // resolve user function names before builtin functions -- this allows the user to override default functions if (!(ctx->current_compile_flags & NSEEL_CODE_COMPILE_FLAG_ONLY_BUILTIN_FUNCTIONS)) { _codeHandleFunctionRec *best=NULL; size_t bestlen=0; const char * const ourcall = sname+rel_prefix_len; const size_t ourcall_len = strlen(ourcall); int pass; for (pass=0;pass<2;pass++) { _codeHandleFunctionRec *fr = pass ? ctx->functions_common : ctx->functions_local; // sname is [namespace.[ns.]]function, find best match of function that matches the right end while (fr) { int this_np = fr->num_params; const char *thisfunc = fr->fname; const size_t thisfunc_len = strlen(thisfunc); if (this_np < 1) this_np=1; if (thisfunc_len == ourcall_len && !stricmp(thisfunc,ourcall)) { if (this_np == parmcnt) { bestlen = thisfunc_len; best = fr; break; // found exact match, finished } else { if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = fr->num_params; } } if (thisfunc_len > bestlen && thisfunc_len < ourcall_len && ourcall[ourcall_len - thisfunc_len - 1] == '.' && !stricmp(thisfunc,ourcall + ourcall_len - thisfunc_len)) { if (this_np == parmcnt) { bestlen = thisfunc_len; best = fr; } else if (match_parmcnt[3]<0) match_parmcnt[3]=fr->num_params; } fr=fr->next; } if (fr) break; // found exact match, finished } if (best) { switch (parmcnt) { case 0: case 1: rec->opcodeType = OPCODETYPE_FUNC1; break; case 2: rec->opcodeType = OPCODETYPE_FUNC2; break; case 3: rec->opcodeType = OPCODETYPE_FUNC3; break; default: rec->opcodeType = OPCODETYPE_FUNCX; break; } if (ourcall != rec->relname) memmove((char *)rec->relname, ourcall, strlen(ourcall)+1); if (ctx->function_curName && rel_prefix_idx<0) { // if no namespace specified, and this.commonprefix.func() called, remove common prefixes and set prefixidx to be this const char *p=ctx->function_curName; if (*p) p++; while (*p && *p != '.') p++; if (*p && p[1]) // we have a dot! { while (p[1]) p++; // go to last char of string, which doesn't allow possible trailing dot to be checked while (--p > ctx->function_curName) // do not check possible leading dot { if (*p == '.') { const size_t cmplen = p+1-ctx->function_curName; if (!strnicmp(rec->relname,ctx->function_curName,cmplen) && rec->relname[cmplen]) { const char *src=rec->relname + cmplen; memmove((char *)rec->relname, src, strlen(src)+1); rel_prefix_idx=-1; ctx->function_usesNamespaces=1; break; } } } } } if (ctx->function_curName && rel_prefix_idx < -1 && strchr(rec->relname,'.') && !validate_varname_for_function(ctx,rec->relname)) { if (errOut) *errOut = 1; if (ctx->last_error_string[0]) lstrcatn(ctx->last_error_string, ", ", sizeof(ctx->last_error_string)); snprintf_append(ctx->last_error_string,sizeof(ctx->last_error_string),"namespaced function '%s' inaccessible",rec->relname); return NULL; } rec->namespaceidx = rel_prefix_idx; rec->fntype = FUNCTYPE_EELFUNC; rec->fn = best; return rec; } } if (prevent_function_calls) { if (ctx->last_error_string[0]) lstrcatn(ctx->last_error_string, ", ", sizeof(ctx->last_error_string)); snprintf_append(ctx->last_error_string,sizeof(ctx->last_error_string),"'%.30s': %s",sname, prevent_function_calls); if (errOut) *errOut = 0; return NULL; } #ifdef NSEEL_EEL1_COMPAT_MODE if (!stricmp(sname,"assign")) { if (parmcnt == 2) { rec->opcodeType = OPCODETYPE_FUNC2; rec->fntype = FN_ASSIGN; return rec; } if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 2; } else if (!stricmp(sname,"if")) { if (parmcnt == 3) { rec->opcodeType = OPCODETYPE_FUNC3; rec->fntype = FN_IF_ELSE; return rec; } if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 3; } else if (!stricmp(sname,"equal")) { if (parmcnt == 2) { rec->opcodeType = OPCODETYPE_FUNC2; rec->fntype = FN_EQ; return rec; } if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 2; } else if (!stricmp(sname,"below")) { if (parmcnt == 2) { rec->opcodeType = OPCODETYPE_FUNC2; rec->fntype = FN_LT; return rec; } if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 2; } else if (!stricmp(sname,"above")) { if (parmcnt == 2) { rec->opcodeType = OPCODETYPE_FUNC2; rec->fntype = FN_GT; return rec; } if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 2; } else if (!stricmp(sname,"bnot")) { if (parmcnt == 1) { rec->opcodeType = OPCODETYPE_FUNC1; rec->fntype = FN_NOT; return rec; } if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 1; } else if (!stricmp(sname,"megabuf")) { if (parmcnt == 1) { rec->opcodeType = OPCODETYPE_FUNC1; rec->fntype = FN_MEMORY; return rec; } if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 1; } else if (!stricmp(sname,"gmegabuf")) { if (parmcnt == 1) { rec->opcodeType = OPCODETYPE_FUNC1; rec->fntype = FN_GMEMORY; return rec; } if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 1; } else #endif // convert legacy pow() to FN_POW if (!stricmp("pow",sname)) { if (parmcnt == 2) { rec->opcodeType = OPCODETYPE_FUNC2; rec->fntype = FN_POW; return rec; } if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 2; } else if (!stricmp("__denormal_likely",sname) || !stricmp("__denormal_unlikely",sname)) { if (parmcnt == 1) { rec->opcodeType = OPCODETYPE_FUNC1; rec->fntype = !stricmp("__denormal_likely",sname) ? FN_DENORMAL_LIKELY : FN_DENORMAL_UNLIKELY; return rec; } } { int chkamt=0; functionType *f=nseel_getFunctionByName(ctx,sname,&chkamt); if (f) while (chkamt-->=0) { const int pc_needed=(f->nParams&FUNCTIONTYPE_PARAMETERCOUNTMASK); if ((f->nParams&BIF_TAKES_VARPARM_EX)==BIF_TAKES_VARPARM ? (parmcnt >= pc_needed) : (parmcnt == pc_needed)) { rec->fntype = FUNCTYPE_FUNCTIONTYPEREC; rec->fn = (void *)f; switch (parmcnt) { case 0: case 1: rec->opcodeType = OPCODETYPE_FUNC1; break; case 2: rec->opcodeType = OPCODETYPE_FUNC2; break; case 3: rec->opcodeType = OPCODETYPE_FUNC3; break; default: rec->opcodeType = OPCODETYPE_FUNCX; break; } return rec; } if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = (f->nParams&FUNCTIONTYPE_PARAMETERCOUNTMASK); f++; if (stricmp(f->name,sname)) break; } } if (ctx->last_error_string[0]) lstrcatn(ctx->last_error_string, ", ", sizeof(ctx->last_error_string)); if (match_parmcnt[3] >= 0) { if (match_parmcnt_pos<3) match_parmcnt[match_parmcnt_pos] = match_parmcnt[3]; match_parmcnt_pos++; } if (!match_parmcnt_pos) snprintf_append(ctx->last_error_string,sizeof(ctx->last_error_string),"'%.30s' undefined",sname); else { int x; snprintf_append(ctx->last_error_string,sizeof(ctx->last_error_string),"'%.30s' needs ",sname); for (x = 0; x < match_parmcnt_pos; x++) snprintf_append(ctx->last_error_string,sizeof(ctx->last_error_string),"%s%d",x==0?"" : x == match_parmcnt_pos-1?" or ":",",match_parmcnt[x]); lstrcatn(ctx->last_error_string," parms",sizeof(ctx->last_error_string)); } if (errOut) *errOut = match_parmcnt_pos > 0 ? parmcntopcodeType != OPCODETYPE_VARPTR || !fn->relname || !fn->relname[0]) { return NULL; } fn->parms.parms[0] = code1; fn->parms.parms[1] = code2; fn->parms.parms[2] = code3; for (x=0;x<3;x++) { opcodeRec *prni=fn->parms.parms[x]; while (prni && np < NSEEL_MAX_EELFUNC_PARAMETERS) { const int isMP = prni->opcodeType == OPCODETYPE_MOREPARAMS; np++; if (!isMP) break; prni = prni->parms.parms[1]; } } r = nseel_resolve_named_symbol(ctx, fn, np<1 ? 1 : np ,errOut); if (postCode && r) { if (code1 && r->opcodeType == OPCODETYPE_FUNC1 && r->fntype == FN_WHILE) { // change while(x) (postcode) to be // while ((x) ? (postcode;1) : 0); r->parms.parms[0] = nseel_createIfElse(ctx,r->parms.parms[0], nseel_createSimpleCompiledFunction(ctx,FN_JOIN_STATEMENTS,2,postCode,nseel_createCompiledValue(ctx,1.0f)), NULL); // NULL defaults to 0.0 } else { snprintf_append(ctx->last_error_string,sizeof(ctx->last_error_string),"syntax error following function"); *errOut = -1; return NULL; } } return r; } struct eelStringSegmentRec *nseel_createStringSegmentRec(compileContext *ctx, const char *str, int len) { struct eelStringSegmentRec *r = newTmpBlock(ctx,sizeof(struct eelStringSegmentRec)); if (r) { r->_next=0; r->str_start=str; r->str_len = len; } return r; } opcodeRec *nseel_eelMakeOpcodeFromStringSegments(compileContext *ctx, struct eelStringSegmentRec *rec) { if (ctx && ctx->onString) { return nseel_createCompiledValue(ctx, ctx->onString(ctx->caller_this,rec)); } return NULL; } opcodeRec *nseel_createMoreParametersOpcode(compileContext *ctx, opcodeRec *code1, opcodeRec *code2) { opcodeRec *r=code1 && code2 ? newOpCode(ctx,NULL,OPCODETYPE_MOREPARAMS) : NULL; if (r) { r->parms.parms[0] = code1; r->parms.parms[1] = code2; } return r; } opcodeRec *nseel_createIfElse(compileContext *ctx, opcodeRec *code1, opcodeRec *code2, opcodeRec *code3) { opcodeRec *r=code1 ? newOpCode(ctx,NULL,OPCODETYPE_FUNC3) : NULL; if (r) { if (!code2) code2 = nseel_createCompiledValue(ctx,0.0); if (!code3) code3 = nseel_createCompiledValue(ctx,0.0); if (!code2||!code3) return NULL; r->fntype = FN_IF_ELSE; r->parms.parms[0] = code1; r->parms.parms[1] = code2; r->parms.parms[2] = code3; } return r; } opcodeRec *nseel_createMemoryAccess(compileContext *ctx, opcodeRec *code1, opcodeRec *code2) { if (code1 && code1->opcodeType == OPCODETYPE_VARPTR && !stricmp(code1->relname,"gmem")) { return nseel_createSimpleCompiledFunction(ctx, FN_GMEMORY,1,code2?code2:nseel_createCompiledValue(ctx,0.0),0); } if (code2 && (code2->opcodeType != OPCODETYPE_DIRECTVALUE || code2->parms.dv.directValue != 0.0)) { code1 = nseel_createSimpleCompiledFunction(ctx,FN_ADD,2,code1,code2); } return nseel_createSimpleCompiledFunction(ctx, FN_MEMORY,1,code1,0); } opcodeRec *nseel_createSimpleCompiledFunction(compileContext *ctx, int fn, int np, opcodeRec *code1, opcodeRec *code2) { opcodeRec *r=code1 && (np<2 || code2) ? newOpCode(ctx,NULL,np>=2 ? OPCODETYPE_FUNC2:OPCODETYPE_FUNC1) : NULL; if (r) { r->fntype = fn; r->parms.parms[0] = code1; r->parms.parms[1] = code2; if (fn == FN_JOIN_STATEMENTS) { r->fn = r; // for joins, fn is temporarily used for _tail pointers if (code1 && code1->opcodeType == OPCODETYPE_FUNC2 && code1->fntype == fn) { opcodeRec *t = (opcodeRec *)code1->fn; // keep joins in the form of dosomething->morestuff. // in this instance, code1 is previous stuff to do, code2 is new stuff to do r->parms.parms[0] = t->parms.parms[1]; code1->fn = (t->parms.parms[1] = r); return code1; } } } return r; } // these are bitmasks; on request you can tell what is supported, and compileOpcodes will return one of them #define RETURNVALUE_IGNORE 0 // ignore return value #define RETURNVALUE_NORMAL 1 // pointer #define RETURNVALUE_FPSTACK 2 #define RETURNVALUE_BOOL 4 // P1 is nonzero if true #define RETURNVALUE_BOOL_REVERSED 8 // P1 is zero if true #define RETURNVALUE_CACHEABLE 16 // only to be used when (at least) RETURNVALUE_NORMAL is set static int compileOpcodes(compileContext *ctx, opcodeRec *op, unsigned char *bufOut, int bufOut_len, int *computTable, const namespaceInformation *namespacePathToThis, int supportedReturnValues, int *rvType, int *fpStackUsage, int *canHaveDenormalOutput); static unsigned char *compileCodeBlockWithRet(compileContext *ctx, opcodeRec *rec, int *computTableSize, const namespaceInformation *namespacePathToThis, int supportedReturnValues, int *rvType, int *fpStackUse, int *canHaveDenormalOutput); _codeHandleFunctionRec *eel_createFunctionNamespacedInstance(compileContext *ctx, _codeHandleFunctionRec *fr, const char *nameptr) { size_t n; _codeHandleFunctionRec *subfr = fr->isCommonFunction ? ctx->isSharedFunctions ? newDataBlock(sizeof(_codeHandleFunctionRec),8) : newCtxDataBlock(sizeof(_codeHandleFunctionRec),8) : // if common function, but derived version is in non-common context, set ownership to VM rather than us newTmpBlock(ctx,sizeof(_codeHandleFunctionRec)); if (!subfr) return 0; // fr points to functionname()'s rec, nameptr to blah.functionname() *subfr = *fr; n = strlen(nameptr); if (n > sizeof(subfr->fname)-1) n=sizeof(subfr->fname)-1; memcpy(subfr->fname,nameptr,n); subfr->fname[n]=0; subfr->next = NULL; subfr->startptr=0; // make sure this code gets recompiled (with correct member ptrs) for this instance! subfr->startptr_size=-1; // subfr->derivedCopies already points to the right place fr->derivedCopies = subfr; return subfr; } static void combineNamespaceFields(char *nm, const namespaceInformation *namespaceInfo, const char *relname, int thisctx) // nm must be NSEEL_MAX_VARIABLE_NAMELEN+1 bytes { const char *prefix = namespaceInfo ? thisctx<0 ? (thisctx == -1 ? namespaceInfo->namespacePathToThis : NULL) : (thisctx < MAX_SUB_NAMESPACES ? namespaceInfo->subParmInfo[thisctx] : NULL) : NULL; int lfp = 0, lrn=relname ? (int)strlen(relname) : 0; if (prefix) while (prefix[lfp] && prefix[lfp] != ':' && lfp < NSEEL_MAX_VARIABLE_NAMELEN) lfp++; if (!relname) relname = ""; while (*relname == '.') // if relname begins with ., then remove a chunk of context from prefix { relname++; while (lfp>0 && prefix[lfp-1] != '.') lfp--; if (lfp>0) lfp--; } if (lfp > NSEEL_MAX_VARIABLE_NAMELEN-3) lfp=NSEEL_MAX_VARIABLE_NAMELEN-3; if (lfp>0) memcpy(nm,prefix,lfp); if (lrn > NSEEL_MAX_VARIABLE_NAMELEN - lfp - (lfp>0)) lrn=NSEEL_MAX_VARIABLE_NAMELEN - lfp - (lfp>0); if (lrn > 0) { if (lfp>0) nm[lfp++] = '.'; memcpy(nm+lfp,relname,lrn); lfp+=lrn; } nm[lfp++]=0; } //--------------------------------------------------------------------------------------------------------------- static void *nseel_getBuiltinFunctionAddress(compileContext *ctx, int fntype, void *fn, NSEEL_PPPROC *pProc, void ***replList, void **endP, int *abiInfo, int preferredReturnValues, const EEL_F *hasConstParm1, const EEL_F *hasConstParm2) { const EEL_F *firstConstParm = hasConstParm1 ? hasConstParm1 : hasConstParm2; static void *pow_replptrs[4]={&pow,}; switch (fntype) { #define RF(x) *endP = nseel_asm_##x##_end; return (void*)nseel_asm_##x case FN_MUL_OP: *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; RF(mul_op); case FN_DIV_OP: *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; RF(div_op); case FN_OR_OP: *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; RF(or_op); case FN_XOR_OP: *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; RF(xor_op); case FN_AND_OP: *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; RF(and_op); case FN_MOD_OP: *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; RF(mod_op); case FN_ADD_OP: *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; RF(add_op); case FN_SUB_OP: *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; RF(sub_op); case FN_POW_OP: *abiInfo=BIF_LASTPARMONSTACK|BIF_CLEARDENORMAL; *replList = pow_replptrs; RF(2pdds); case FN_POW: *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK;//BIF_FPSTACKUSE(2) might be safe, need to look at pow()'s implementation, but safer bet is to disallow fp stack caching for this expression *replList = pow_replptrs; RF(2pdd); case FN_ADD: *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK_LAZY|BIF_FPSTACKUSE(2); // for x +- non-denormal-constant, we can set BIF_CLEARDENORMAL if (firstConstParm && fabs(*firstConstParm) > DENORMAL_CLEARING_THRESHOLD) *abiInfo |= BIF_CLEARDENORMAL; RF(add); case FN_SUB: *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK|BIF_FPSTACKUSE(2); // for x +- non-denormal-constant, we can set BIF_CLEARDENORMAL if (firstConstParm && fabs(*firstConstParm) > DENORMAL_CLEARING_THRESHOLD) *abiInfo |= BIF_CLEARDENORMAL; RF(sub); case FN_MULTIPLY: *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK_LAZY|BIF_FPSTACKUSE(2); // for x*constant-greater-than-eq-1, we can set BIF_WONTMAKEDENORMAL if (firstConstParm && fabs(*firstConstParm) >= 1.0) *abiInfo |= BIF_WONTMAKEDENORMAL; RF(mul); case FN_DIVIDE: *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK|BIF_FPSTACKUSE(2); // for x/constant-less-than-eq-1, we can set BIF_WONTMAKEDENORMAL if (firstConstParm && fabs(*firstConstParm) <= 1.0) *abiInfo |= BIF_WONTMAKEDENORMAL; RF(div); case FN_MOD: *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK|BIF_FPSTACKUSE(1)|BIF_CLEARDENORMAL; RF(mod); case FN_ASSIGN: *abiInfo = BIF_FPSTACKUSE(1)|BIF_CLEARDENORMAL; RF(assign); case FN_AND: *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK_LAZY|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; RF(and); case FN_OR: *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK_LAZY|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; RF(or); case FN_XOR: *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK_LAZY|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; RF(xor); case FN_SHR: *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; RF(shr); case FN_SHL: *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; RF(shl); #ifndef EEL_TARGET_PORTABLE case FN_NOTNOT: *abiInfo = BIF_LASTPARM_ASBOOL|BIF_RETURNSBOOL|BIF_FPSTACKUSE(1); RF(uplus); #else case FN_NOTNOT: *abiInfo = BIF_LASTPARM_ASBOOL|BIF_RETURNSBOOL|BIF_FPSTACKUSE(1); RF(bnotnot); #endif case FN_UMINUS: *abiInfo = BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_WONTMAKEDENORMAL; RF(uminus); case FN_NOT: *abiInfo = BIF_LASTPARM_ASBOOL|BIF_RETURNSBOOL|BIF_FPSTACKUSE(1); RF(bnot); case FN_EQ: *abiInfo = BIF_TWOPARMSONFPSTACK_LAZY|BIF_RETURNSBOOL|BIF_FPSTACKUSE(2); RF(equal); case FN_EQ_EXACT: *abiInfo=BIF_TWOPARMSONFPSTACK_LAZY|BIF_RETURNSBOOL|BIF_FPSTACKUSE(2); RF(equal_exact); case FN_NE: *abiInfo=BIF_TWOPARMSONFPSTACK_LAZY|BIF_RETURNSBOOL|BIF_FPSTACKUSE(2); RF(notequal); case FN_NE_EXACT: *abiInfo=BIF_TWOPARMSONFPSTACK_LAZY|BIF_RETURNSBOOL|BIF_FPSTACKUSE(2); RF(notequal_exact); case FN_LOGICAL_AND: *abiInfo = BIF_RETURNSBOOL; RF(band); case FN_LOGICAL_OR: *abiInfo = BIF_RETURNSBOOL; RF(bor); #ifdef GLUE_HAS_FXCH case FN_GT: *abiInfo = BIF_TWOPARMSONFPSTACK|BIF_RETURNSBOOL|BIF_FPSTACKUSE(2); RF(above); case FN_GTE: *abiInfo = BIF_TWOPARMSONFPSTACK|BIF_RETURNSBOOL|BIF_REVERSEFPORDER|BIF_FPSTACKUSE(2); RF(beloweq); case FN_LT: *abiInfo = BIF_TWOPARMSONFPSTACK|BIF_RETURNSBOOL|BIF_REVERSEFPORDER|BIF_FPSTACKUSE(2); RF(above); case FN_LTE: *abiInfo = BIF_TWOPARMSONFPSTACK|BIF_RETURNSBOOL|BIF_FPSTACKUSE(2); RF(beloweq); #else case FN_GT: *abiInfo = BIF_RETURNSBOOL|BIF_LASTPARMONSTACK; RF(above); case FN_GTE: *abiInfo = BIF_RETURNSBOOL|BIF_LASTPARMONSTACK; RF(aboveeq); case FN_LT: *abiInfo = BIF_RETURNSBOOL|BIF_LASTPARMONSTACK; RF(below); case FN_LTE: *abiInfo = BIF_RETURNSBOOL|BIF_LASTPARMONSTACK; RF(beloweq); #endif #undef RF #define RF(x) *endP = _asm_##x##_end; return (void*)_asm_##x case FN_MEMORY: { static void *replptrs[4]={&__NSEEL_RAMAlloc,}; *replList = replptrs; *abiInfo = BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(1)|BIF_CLEARDENORMAL; #ifdef GLUE_MEM_NEEDS_PPROC *pProc = NSEEL_PProc_RAM; #endif RF(megabuf); } break; case FN_GMEMORY: { static void *replptrs[4]={&__NSEEL_RAMAllocGMEM,}; *replList = replptrs; *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(1)|BIF_CLEARDENORMAL; *pProc=NSEEL_PProc_GRAM; RF(gmegabuf); } break; #undef RF case FUNCTYPE_FUNCTIONTYPEREC: if (fn) { functionType *p=(functionType *)fn; // if prefers fpstack or bool, or ignoring value, then use fp-stack versions if ((preferredReturnValues&(RETURNVALUE_BOOL|RETURNVALUE_FPSTACK)) || !preferredReturnValues) { static functionType min2={ "min", nseel_asm_min_fp,nseel_asm_min_fp_end, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK_LAZY|BIF_FPSTACKUSE(2)|BIF_WONTMAKEDENORMAL }; static functionType max2={ "max", nseel_asm_max_fp,nseel_asm_max_fp_end, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK_LAZY|BIF_FPSTACKUSE(2)|BIF_WONTMAKEDENORMAL }; if (p->afunc == (void*)nseel_asm_min) p = &min2; else if (p->afunc == (void*)nseel_asm_max) p = &max2; } *replList=p->replptrs; *pProc=p->pProc; *endP = p->func_e; *abiInfo = p->nParams & BIF_NPARAMS_MASK; if (firstConstParm) { const char *name=p->name; if (!strcmp(name,"min") && *firstConstParm < -1.0e-10) *abiInfo |= BIF_CLEARDENORMAL; else if (!strcmp(name,"max") && *firstConstParm > 1.0e-10) *abiInfo |= BIF_CLEARDENORMAL; } return p->afunc; } break; } return 0; } static void *nseel_getEELFunctionAddress(compileContext *ctx, opcodeRec *op, int *customFuncParmSize, int *customFuncLocalStorageSize, EEL_F ***customFuncLocalStorage, int *computTableTop, void **endP, int *isRaw, int wantCodeGenerated, const namespaceInformation *namespacePathToThis, int *rvMode, int *fpStackUse, int *canHaveDenormalOutput, opcodeRec **ordered_parmptrs, int num_ordered_parmptrs ) // if wantCodeGenerated is false, can return bogus pointers in raw mode { _codeHandleFunctionRec *fn = (_codeHandleFunctionRec*)op->fn; namespaceInformation local_namespace={NULL}; char prefix_buf[NSEEL_MAX_VARIABLE_NAMELEN+1], nm[NSEEL_MAX_FUNCSIG_NAME+1]; if (!fn) return NULL; // op->relname ptr is [whatever.]funcname if (fn->parameterAsNamespaceMask || fn->usesNamespaces) { if (wantCodeGenerated) { char *p = prefix_buf; combineNamespaceFields(nm,namespacePathToThis,op->relname,op->namespaceidx); lstrcpyn_safe(prefix_buf,nm,sizeof(prefix_buf)); local_namespace.namespacePathToThis = prefix_buf; // nm is full path of function, prefix_buf will be the path not including function name (unless function name only) while (*p) p++; while (p >= prefix_buf && *p != '.') p--; if (p > prefix_buf) *p=0; } if (fn->parameterAsNamespaceMask) { int x; for(x=0;xnum_params;x++) { if (fn->parameterAsNamespaceMask & (((unsigned int)1)<opcodeType == OPCODETYPE_VARPTR) { rn=ordered_parmptrs[x]->relname; } else if (ordered_parmptrs[x]->opcodeType == OPCODETYPE_VALUE_FROM_NAMESPACENAME) { const char *p=ordered_parmptrs[x]->relname; if (*p == '#') p++; combineNamespaceFields(tmp,namespacePathToThis,p,ordered_parmptrs[x]->namespaceidx); rn = tmp; } } if (!rn) { // todo: figure out how to give correct line number/offset (ugh) snprintf(ctx->last_error_string,sizeof(ctx->last_error_string),"parameter %d to %.120s() must be namespace",x+1,fn->fname); return NULL; } lstrcatn(nm,":",sizeof(nm)); local_namespace.subParmInfo[x] = nm+strlen(nm); lstrcatn(nm,rn,sizeof(nm)); } ordered_parmptrs[x] = NULL; // prevent caller from bothering generating parameters } } } if (wantCodeGenerated) { _codeHandleFunctionRec *fr = fn; // find namespace-adjusted function (if generating code, otherwise assume size is the same) fn = 0; // if this gets re-set, it will be the new function while (fr && !fn) { if (!stricmp(fr->fname,nm)) fn = fr; fr=fr->derivedCopies; } if (!fn) // generate copy of function { fn = eel_createFunctionNamespacedInstance(ctx,(_codeHandleFunctionRec*)op->fn,nm); } } } if (!fn) return NULL; if (!fn->startptr && fn->opcodes && fn->startptr_size != 0) { int sz = fn->startptr_size; if (sz < 0) { fn->tmpspace_req=0; fn->rvMode = RETURNVALUE_IGNORE; fn->canHaveDenormalOutput=0; sz = compileOpcodes(ctx,fn->opcodes,NULL,128*1024*1024,&fn->tmpspace_req, wantCodeGenerated ? &local_namespace : NULL,RETURNVALUE_NORMAL|RETURNVALUE_FPSTACK, &fn->rvMode,&fn->fpStackUsage,&fn->canHaveDenormalOutput); if (sz<0) return NULL; fn->startptr_size = sz; } if (!wantCodeGenerated) { // don't compile anything for now, just give stats if (computTableTop) *computTableTop += fn->tmpspace_req; *customFuncParmSize = fn->num_params; *customFuncLocalStorage = fn->localstorage; *customFuncLocalStorageSize = fn->localstorage_size; *rvMode = fn->rvMode; *fpStackUse = fn->fpStackUsage; if (canHaveDenormalOutput) *canHaveDenormalOutput=fn->canHaveDenormalOutput; if (sz <= NSEEL_MAX_FUNCTION_SIZE_FOR_INLINE && !(ctx->optimizeDisableFlags&OPTFLAG_NO_INLINEFUNC)) { *isRaw = 1; *endP = ((char *)1) + sz; return (char *)1; } *endP = (void*)nseel_asm_fcall_end; return (void*)nseel_asm_fcall; } if (sz <= NSEEL_MAX_FUNCTION_SIZE_FOR_INLINE && !(ctx->optimizeDisableFlags&OPTFLAG_NO_INLINEFUNC)) { void *p=newTmpBlock(ctx,sz); fn->tmpspace_req=0; if (p) { fn->canHaveDenormalOutput=0; if (fn->isCommonFunction) ctx->isGeneratingCommonFunction++; sz=compileOpcodes(ctx,fn->opcodes,(unsigned char*)p,sz,&fn->tmpspace_req,&local_namespace,RETURNVALUE_NORMAL|RETURNVALUE_FPSTACK,&fn->rvMode,&fn->fpStackUsage,&fn->canHaveDenormalOutput); if (fn->isCommonFunction) ctx->isGeneratingCommonFunction--; // recompile function with native context pointers if (sz>0) { fn->startptr_size=sz; fn->startptr=p; } } } else { unsigned char *codeCall; fn->tmpspace_req=0; fn->fpStackUsage=0; fn->canHaveDenormalOutput=0; if (fn->isCommonFunction) ctx->isGeneratingCommonFunction++; codeCall=compileCodeBlockWithRet(ctx,fn->opcodes,&fn->tmpspace_req,&local_namespace,RETURNVALUE_NORMAL|RETURNVALUE_FPSTACK,&fn->rvMode,&fn->fpStackUsage,&fn->canHaveDenormalOutput); if (fn->isCommonFunction) ctx->isGeneratingCommonFunction--; if (codeCall) { void *f=GLUE_realAddress(nseel_asm_fcall,nseel_asm_fcall_end,&sz); fn->startptr = newTmpBlock(ctx,sz); if (fn->startptr) { memcpy(fn->startptr,f,sz); EEL_GLUE_set_immediate(fn->startptr,(INT_PTR)codeCall); fn->startptr_size = sz; } } } } if (fn->startptr) { if (computTableTop) *computTableTop += fn->tmpspace_req; *customFuncParmSize = fn->num_params; *customFuncLocalStorage = fn->localstorage; *customFuncLocalStorageSize = fn->localstorage_size; *rvMode = fn->rvMode; *fpStackUse = fn->fpStackUsage; if (canHaveDenormalOutput) *canHaveDenormalOutput= fn->canHaveDenormalOutput; *endP = (char*)fn->startptr + fn->startptr_size; *isRaw=1; return fn->startptr; } return 0; } // returns true if does something (other than calculating and throwing away a value) static char optimizeOpcodes(compileContext *ctx, opcodeRec *op, int needsResult) { opcodeRec *lastJoinOp=NULL; char retv, retv_parm[3], joined_retv=0; while (op && op->opcodeType == OPCODETYPE_FUNC2 && op->fntype == FN_JOIN_STATEMENTS) { if (!optimizeOpcodes(ctx,op->parms.parms[0], 0) || OPCODE_IS_TRIVIAL(op->parms.parms[0])) { // direct value, can skip ourselves memcpy(op,op->parms.parms[1],sizeof(*op)); } else { joined_retv |= 1; lastJoinOp = op; op = op->parms.parms[1]; } } goto start_over; #define RESTART_DIRECTVALUE(X) { op->parms.dv.directValue = (X); goto start_over_directvalue; } start_over_directvalue: op->opcodeType = OPCODETYPE_DIRECTVALUE; op->parms.dv.valuePtr=NULL; start_over: // when an opcode changed substantially in optimization, goto here to reprocess it retv = retv_parm[0]=retv_parm[1]=retv_parm[2]=0; if (!op || // should never really happen OPCODE_IS_TRIVIAL(op) || // should happen often (vars) op->opcodeType < 0 || op->opcodeType >= OPCODETYPE_INVALID // should never happen (assert would be appropriate heh) ) return joined_retv; if (!needsResult) { if (op->fntype == FUNCTYPE_EELFUNC) { needsResult=1; // assume eel functions are non-const for now } else if (op->fntype == FUNCTYPE_FUNCTIONTYPEREC) { functionType *pfn = (functionType *)op->fn; if (!pfn || !(pfn->nParams&NSEEL_NPARAMS_FLAG_CONST)) needsResult=1; } else if (op->fntype >= FN_NONCONST_BEGIN && op->fntype < FUNCTYPE_SIMPLEMAX) { needsResult=1; } } if (op->opcodeType>=OPCODETYPE_FUNC2) retv_parm[1] = optimizeOpcodes(ctx,op->parms.parms[1], needsResult); if (op->opcodeType>=OPCODETYPE_FUNC3) retv_parm[2] = optimizeOpcodes(ctx,op->parms.parms[2], needsResult); retv_parm[0] = optimizeOpcodes(ctx,op->parms.parms[0], needsResult || (FNPTR_HAS_CONDITIONAL_EXEC(op) && (retv_parm[1] || retv_parm[2] || op->opcodeType <= OPCODETYPE_FUNC1)) ); if (op->opcodeType != OPCODETYPE_MOREPARAMS) { if (op->fntype >= 0 && op->fntype < FUNCTYPE_SIMPLEMAX) { if (op->opcodeType == OPCODETYPE_FUNC1) // within FUNCTYPE_SIMPLE { if (op->parms.parms[0]->opcodeType == OPCODETYPE_DIRECTVALUE) { switch (op->fntype) { case FN_NOTNOT: RESTART_DIRECTVALUE(fabs(op->parms.parms[0]->parms.dv.directValue)>=NSEEL_CLOSEFACTOR ? 1.0 : 0.0); case FN_NOT: RESTART_DIRECTVALUE(fabs(op->parms.parms[0]->parms.dv.directValue)>=NSEEL_CLOSEFACTOR ? 0.0 : 1.0); case FN_UMINUS: RESTART_DIRECTVALUE(- op->parms.parms[0]->parms.dv.directValue); } } else if (op->fntype == FN_NOT || op->fntype == FN_NOTNOT) { if (op->parms.parms[0]->opcodeType == OPCODETYPE_FUNC1) { switch (op->parms.parms[0]->fntype) { case FN_UMINUS: case FN_NOTNOT: // ignore any NOTNOTs UMINUS or UPLUS, they would have no effect anyway op->parms.parms[0] = op->parms.parms[0]->parms.parms[0]; goto start_over; case FN_NOT: op->fntype = op->fntype==FN_NOT ? FN_NOTNOT : FN_NOT; // switch between FN_NOT and FN_NOTNOT op->parms.parms[0] = op->parms.parms[0]->parms.parms[0]; goto start_over; } } else if (op->parms.parms[0]->opcodeType == OPCODETYPE_FUNC2) { int repl_type = -1; switch (op->parms.parms[0]->fntype) { case FN_EQ: repl_type = FN_NE; break; case FN_NE: repl_type = FN_EQ; break; case FN_EQ_EXACT: repl_type = FN_NE_EXACT; break; case FN_NE_EXACT: repl_type = FN_EQ_EXACT; break; case FN_LT: repl_type = FN_GTE; break; case FN_LTE: repl_type = FN_GT; break; case FN_GT: repl_type = FN_LTE; break; case FN_GTE: repl_type = FN_LT; break; } if (repl_type != -1) { const int oldtype = op->fntype; memcpy(op,op->parms.parms[0],sizeof(*op)); if (oldtype == FN_NOT) op->fntype = repl_type; goto start_over; } } } } else if (op->opcodeType == OPCODETYPE_FUNC2) // within FUNCTYPE_SIMPLE { const int dv0 = op->parms.parms[0]->opcodeType == OPCODETYPE_DIRECTVALUE; const int dv1 = op->parms.parms[1]->opcodeType == OPCODETYPE_DIRECTVALUE; if (dv0 && dv1) { int reval = -1; switch (op->fntype) { case FN_MOD: { int a = (int) op->parms.parms[1]->parms.dv.directValue; if (a) { a = (int) op->parms.parms[0]->parms.dv.directValue % a; if (a<0) a=-a; } RESTART_DIRECTVALUE((EEL_F)a); } break; case FN_SHL: RESTART_DIRECTVALUE(((int)op->parms.parms[0]->parms.dv.directValue) << ((int)op->parms.parms[1]->parms.dv.directValue)); case FN_SHR: RESTART_DIRECTVALUE(((int)op->parms.parms[0]->parms.dv.directValue) >> ((int)op->parms.parms[1]->parms.dv.directValue)); case FN_POW: RESTART_DIRECTVALUE(pow(op->parms.parms[0]->parms.dv.directValue, op->parms.parms[1]->parms.dv.directValue)); case FN_DIVIDE: RESTART_DIRECTVALUE(op->parms.parms[0]->parms.dv.directValue / op->parms.parms[1]->parms.dv.directValue); case FN_MULTIPLY: RESTART_DIRECTVALUE(op->parms.parms[0]->parms.dv.directValue * op->parms.parms[1]->parms.dv.directValue); case FN_ADD: RESTART_DIRECTVALUE(op->parms.parms[0]->parms.dv.directValue + op->parms.parms[1]->parms.dv.directValue); case FN_SUB: RESTART_DIRECTVALUE(op->parms.parms[0]->parms.dv.directValue - op->parms.parms[1]->parms.dv.directValue); case FN_AND: RESTART_DIRECTVALUE((double) (((WDL_INT64)op->parms.parms[0]->parms.dv.directValue) & ((WDL_INT64)op->parms.parms[1]->parms.dv.directValue))); case FN_OR: RESTART_DIRECTVALUE((double) (((WDL_INT64)op->parms.parms[0]->parms.dv.directValue) | ((WDL_INT64)op->parms.parms[1]->parms.dv.directValue))); case FN_XOR: RESTART_DIRECTVALUE((double) (((WDL_INT64)op->parms.parms[0]->parms.dv.directValue) ^ ((WDL_INT64)op->parms.parms[1]->parms.dv.directValue))); case FN_EQ: reval = fabs(op->parms.parms[0]->parms.dv.directValue - op->parms.parms[1]->parms.dv.directValue) < NSEEL_CLOSEFACTOR; break; case FN_NE: reval = fabs(op->parms.parms[0]->parms.dv.directValue - op->parms.parms[1]->parms.dv.directValue) >= NSEEL_CLOSEFACTOR; break; case FN_EQ_EXACT: reval = op->parms.parms[0]->parms.dv.directValue == op->parms.parms[1]->parms.dv.directValue; break; case FN_NE_EXACT: reval = op->parms.parms[0]->parms.dv.directValue != op->parms.parms[1]->parms.dv.directValue; break; case FN_LT: reval = op->parms.parms[0]->parms.dv.directValue < op->parms.parms[1]->parms.dv.directValue; break; case FN_LTE: reval = op->parms.parms[0]->parms.dv.directValue <= op->parms.parms[1]->parms.dv.directValue; break; case FN_GT: reval = op->parms.parms[0]->parms.dv.directValue > op->parms.parms[1]->parms.dv.directValue; break; case FN_GTE: reval = op->parms.parms[0]->parms.dv.directValue >= op->parms.parms[1]->parms.dv.directValue; break; case FN_LOGICAL_AND: reval = fabs(op->parms.parms[0]->parms.dv.directValue) >= NSEEL_CLOSEFACTOR && fabs(op->parms.parms[1]->parms.dv.directValue) >= NSEEL_CLOSEFACTOR; break; case FN_LOGICAL_OR: reval = fabs(op->parms.parms[0]->parms.dv.directValue) >= NSEEL_CLOSEFACTOR || fabs(op->parms.parms[1]->parms.dv.directValue) >= NSEEL_CLOSEFACTOR; break; } if (reval >= 0) RESTART_DIRECTVALUE((EEL_F) reval); } else if (dv0 || dv1) { double dvalue = op->parms.parms[!dv0]->parms.dv.directValue; switch (op->fntype) { case FN_OR: case FN_XOR: if (!(WDL_INT64)dvalue) { // replace with or0 static functionType fr={"or0",nseel_asm_or0, nseel_asm_or0_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_LASTPARMONSTACK|BIF_RETURNSONSTACK|BIF_CLEARDENORMAL, {0}, NULL}; op->opcodeType = OPCODETYPE_FUNC1; op->fntype = FUNCTYPE_FUNCTIONTYPEREC; op->fn = &fr; if (dv0) op->parms.parms[0] = op->parms.parms[1]; goto start_over; } break; case FN_SUB: if (dv0) { if (dvalue == 0.0) { op->opcodeType = OPCODETYPE_FUNC1; op->fntype = FN_UMINUS; op->parms.parms[0] = op->parms.parms[1]; goto start_over; } break; } // fall through, if dv1 we can remove +0.0 case FN_ADD: if (dvalue == 0.0) { memcpy(op,op->parms.parms[!!dv0],sizeof(*op)); goto start_over; } break; case FN_AND: if ((WDL_INT64)dvalue) break; dvalue = 0.0; // treat x&0 as x*0, which optimizes to 0 // fall through case FN_MULTIPLY: if (dvalue == 0.0) // remove multiply by 0.0 (using 0.0 direct value as replacement), unless the nonzero side did something { if (!retv_parm[!!dv0]) { memcpy(op,op->parms.parms[!dv0],sizeof(*op)); // set to 0 if other action wouldn't do anything goto start_over; } else { // this is 0.0 * oldexpressionthatmustbeprocessed or oldexpressionthatmustbeprocessed*0.0 op->fntype = FN_JOIN_STATEMENTS; if (dv0) // 0.0*oldexpression, reverse the order so that 0 is returned { // set to (oldexpression;0) opcodeRec *tmp = op->parms.parms[1]; op->parms.parms[1] = op->parms.parms[0]; op->parms.parms[0] = tmp; } goto start_over; } } else if (dvalue == 1.0) // remove multiply by 1.0 (using non-1.0 value as replacement) { memcpy(op,op->parms.parms[!!dv0],sizeof(*op)); goto start_over; } break; case FN_POW: if (dv1) { // x^0 = 1 if (fabs(dvalue) < 1e-30) { RESTART_DIRECTVALUE(1.0); } // x^1 = x if (fabs(dvalue-1.0) < 1e-30) { memcpy(op,op->parms.parms[0],sizeof(*op)); goto start_over; } } else if (dv0) { // pow(constant, x) = exp((x) * ln(constant)), if constant>0 // opcodeRec *parm0 = op->parms.parms[0]; if (dvalue > 0.0) { static functionType expcpy={ "exp", nseel_asm_1pdd,nseel_asm_1pdd_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK, {&exp}, }; // 1^x = 1 if (fabs(dvalue-1.0) < 1e-30) { RESTART_DIRECTVALUE(1.0); } dvalue=log(dvalue); if (fabs(dvalue-1.0) < 1e-9) { // caller wanted e^x op->parms.parms[0]=op->parms.parms[1]; } else { // it would be nice to replace 10^x with exp(log(10)*x) or 2^x with exp(log(2),x), but // doing so breaks rounding. we could maybe only allow 10^x, which is used for dB conversion, // but for now we should just force the programmer do it exp(log(10)*x) themselves. break; /* parm0->opcodeType = OPCODETYPE_FUNC2; parm0->fntype = FN_MULTIPLY; parm0->parms.parms[0] = nseel_createCompiledValue(ctx,dvalue); parm0->parms.parms[1] = op->parms.parms[1]; */ } op->opcodeType = OPCODETYPE_FUNC1; op->fntype = FUNCTYPE_FUNCTIONTYPEREC; op->fn = &expcpy; goto start_over; } } break; case FN_MOD: if (dv1) { const int a = (int) dvalue; if (!a) { RESTART_DIRECTVALUE(0.0); } } break; case FN_DIVIDE: if (dv1) { if (dvalue == 1.0) // remove divide by 1.0 (using non-1.0 value as replacement) { memcpy(op,op->parms.parms[!!dv0],sizeof(*op)); goto start_over; } else { // change to a multiply if (dvalue == 0.0) { op->fntype = FN_MULTIPLY; goto start_over; } else { double d = 1.0/dvalue; WDL_DenormalDoubleAccess *p = (WDL_DenormalDoubleAccess*)&d; // allow conversion to multiply if reciprocal is exact // we could also just look to see if the last few digits of the mantissa were 0, which would probably be good // enough, but if the user really wants it they should do * (1/x) instead to force precalculation of reciprocal. if (!p->w.lw && !(p->w.hw & 0xfffff)) { op->fntype = FN_MULTIPLY; op->parms.parms[1]->parms.dv.directValue = d; op->parms.parms[1]->parms.dv.valuePtr=NULL; goto start_over; } } } } else if (dvalue == 0.0) { if (!retv_parm[!!dv0]) { // if 0/x set to always 0. // this is 0.0 / (oldexpression that can be eliminated) memcpy(op,op->parms.parms[!dv0],sizeof(*op)); // set to 0 if other action wouldn't do anything } else { opcodeRec *tmp; // this is 0.0 / oldexpressionthatmustbeprocessed op->fntype = FN_JOIN_STATEMENTS; tmp = op->parms.parms[1]; op->parms.parms[1] = op->parms.parms[0]; op->parms.parms[0] = tmp; // set to (oldexpression;0) } goto start_over; } break; case FN_EQ: if (dvalue == 0.0) { // convert x == 0.0 to !x op->opcodeType=OPCODETYPE_FUNC1; op->fntype = FN_NOT; if (dv0) op->parms.parms[0]=op->parms.parms[1]; goto start_over; } break; case FN_NE: if (dvalue == 0.0) { // convert x != 0.0 to !! op->opcodeType=OPCODETYPE_FUNC1; op->fntype = FN_NOTNOT; if (dv0) op->parms.parms[0]=op->parms.parms[1]; goto start_over; } break; case FN_LOGICAL_AND: if (dv0) { // dvalue && expr if (fabs(dvalue) < NSEEL_CLOSEFACTOR) { // 0 && expr, replace with 0 RESTART_DIRECTVALUE(0.0); } else { // 1 && expr, replace with 0 != expr op->fntype = FN_NE; op->parms.parms[0]->parms.dv.valuePtr=NULL; op->parms.parms[0]->parms.dv.directValue = 0.0; } } else { // expr && dvalue if (fabs(dvalue) < NSEEL_CLOSEFACTOR) { // expr && 0 if (!retv_parm[0]) { // expr has no consequence, drop it RESTART_DIRECTVALUE(0.0); } else { // replace with (expr; 0) op->fntype = FN_JOIN_STATEMENTS; op->parms.parms[1]->parms.dv.valuePtr=NULL; op->parms.parms[1]->parms.dv.directValue = 0.0; } } else { // expr && 1, replace with expr != 0 op->fntype = FN_NE; op->parms.parms[1]->parms.dv.valuePtr=NULL; op->parms.parms[1]->parms.dv.directValue = 0.0; } } goto start_over; case FN_LOGICAL_OR: if (dv0) { // dvalue || expr if (fabs(dvalue) >= NSEEL_CLOSEFACTOR) { // 1 || expr, replace with 1 RESTART_DIRECTVALUE(1.0); } else { // 0 || expr, replace with 0 != expr op->fntype = FN_NE; op->parms.parms[0]->parms.dv.valuePtr=NULL; op->parms.parms[0]->parms.dv.directValue = 0.0; } } else { // expr || dvalue if (fabs(dvalue) >= NSEEL_CLOSEFACTOR) { // expr || 1 if (!retv_parm[0]) { // expr has no consequence, drop it and return 1 RESTART_DIRECTVALUE(1.0); } else { // replace with (expr; 1) op->fntype = FN_JOIN_STATEMENTS; op->parms.parms[1]->parms.dv.valuePtr=NULL; op->parms.parms[1]->parms.dv.directValue = 1.0; } } else { // expr || 0, replace with expr != 0 op->fntype = FN_NE; op->parms.parms[1]->parms.dv.valuePtr=NULL; op->parms.parms[1]->parms.dv.directValue = 0.0; } } goto start_over; } } // dv0 || dv1 // general optimization of two parameters switch (op->fntype) { case FN_MULTIPLY: { opcodeRec *first_parm = op->parms.parms[0],*second_parm = op->parms.parms[1]; if (second_parm->opcodeType == first_parm->opcodeType) { switch(first_parm->opcodeType) { case OPCODETYPE_VALUE_FROM_NAMESPACENAME: if (first_parm->namespaceidx != second_parm->namespaceidx) break; // fall through case OPCODETYPE_VARPTR: if (first_parm->relname && second_parm->relname && !stricmp(second_parm->relname,first_parm->relname)) second_parm=NULL; break; case OPCODETYPE_VARPTRPTR: if (first_parm->parms.dv.valuePtr && first_parm->parms.dv.valuePtr==second_parm->parms.dv.valuePtr) second_parm=NULL; break; } if (!second_parm) // switch from x*x to sqr(x) { static functionType sqrcpy={ "sqr", nseel_asm_sqr,nseel_asm_sqr_end, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(1) }; op->opcodeType = OPCODETYPE_FUNC1; op->fntype = FUNCTYPE_FUNCTIONTYPEREC; op->fn = &sqrcpy; goto start_over; } } } break; case FN_POW: { opcodeRec *first_parm = op->parms.parms[0]; if (first_parm->opcodeType == op->opcodeType && first_parm->fntype == FN_POW) { // since first_parm is a pow too, we can multiply the exponents. // set our base to be the base of the inner pow op->parms.parms[0] = first_parm->parms.parms[0]; // make the old extra pow be a multiply of the exponents first_parm->fntype = FN_MULTIPLY; first_parm->parms.parms[0] = op->parms.parms[1]; // put that as the exponent op->parms.parms[1] = first_parm; goto start_over; } } break; case FN_LOGICAL_AND: case FN_LOGICAL_OR: if (op->parms.parms[0]->fntype == FN_NOTNOT) { // remove notnot, unnecessary for input to &&/|| operators op->parms.parms[0] = op->parms.parms[0]->parms.parms[0]; goto start_over; } if (op->parms.parms[1]->fntype == FN_NOTNOT) { // remove notnot, unnecessary for input to &&/|| operators op->parms.parms[1] = op->parms.parms[1]->parms.parms[0]; goto start_over; } break; } } else if (op->opcodeType==OPCODETYPE_FUNC3) // within FUNCTYPE_SIMPLE { if (op->fntype == FN_IF_ELSE) { if (op->parms.parms[0]->opcodeType == OPCODETYPE_DIRECTVALUE) { int s = fabs(op->parms.parms[0]->parms.dv.directValue) >= NSEEL_CLOSEFACTOR; memcpy(op,op->parms.parms[s ? 1 : 2],sizeof(opcodeRec)); goto start_over; } if (op->parms.parms[0]->opcodeType == OPCODETYPE_FUNC1) { if (op->parms.parms[0]->fntype == FN_NOTNOT) { // remove notnot, unnecessary for input to ? operator op->parms.parms[0] = op->parms.parms[0]->parms.parms[0]; goto start_over; } } } } if (op->fntype >= FN_NONCONST_BEGIN && op->fntype < FUNCTYPE_SIMPLEMAX) retv|=1; // FUNCTYPE_SIMPLE } else if (op->fntype == FUNCTYPE_FUNCTIONTYPEREC && op->fn) { /* probably worth doing reduction on: _divop (constant change to multiply) _and _or abs maybe: min max also, optimize should (recursively or maybe iteratively?) search transitive functions (mul/div) for more constant reduction possibilities */ functionType *pfn = (functionType *)op->fn; if (!(pfn->nParams&NSEEL_NPARAMS_FLAG_CONST)) retv|=1; if (op->opcodeType==OPCODETYPE_FUNC1) // within FUNCTYPE_FUNCTIONTYPEREC { if (op->parms.parms[0]->opcodeType == OPCODETYPE_DIRECTVALUE) { int suc=1; EEL_F v = op->parms.parms[0]->parms.dv.directValue; #define DOF(x) if (!strcmp(pfn->name,#x)) v = x(v); else #define DOF2(x,y) if (!strcmp(pfn->name,#x)) v = x(y); else DOF(sin) DOF(cos) DOF(tan) DOF(asin) DOF(acos) DOF(atan) DOF2(sqrt, fabs(v)) DOF(exp) DOF(log) DOF(log10) /* else */ suc=0; #undef DOF #undef DOF2 if (suc) { RESTART_DIRECTVALUE(v); } } } else if (op->opcodeType==OPCODETYPE_FUNC2) // within FUNCTYPE_FUNCTIONTYPEREC { const int dv0=op->parms.parms[0]->opcodeType == OPCODETYPE_DIRECTVALUE; const int dv1=op->parms.parms[1]->opcodeType == OPCODETYPE_DIRECTVALUE; if (dv0 && dv1) { if (!strcmp(pfn->name,"atan2")) { RESTART_DIRECTVALUE(atan2(op->parms.parms[0]->parms.dv.directValue, op->parms.parms[1]->parms.dv.directValue)); } } } // FUNCTYPE_FUNCTIONTYPEREC } else { // unknown or eel func, assume non-const retv |= 1; } } // if we need results, or our function has effects itself, then finish if (retv || needsResult) { return retv || joined_retv || retv_parm[0] || retv_parm[1] || retv_parm[2]; } // we don't need results here, and our function is const, which means we can remove it { int cnt=0, idx1=0, idx2=0, x; for (x=0;x<3;x++) if (retv_parm[x]) { if (!cnt++) idx1=x; else idx2=x; } if (!cnt) // none of the parameters do anything, remove this opcode { if (lastJoinOp) { // replace previous join with its first linked opcode, removing this opcode completely memcpy(lastJoinOp,lastJoinOp->parms.parms[0],sizeof(*lastJoinOp)); } else if (op->opcodeType != OPCODETYPE_DIRECTVALUE) { // allow caller to easily detect this as trivial and remove it op->opcodeType = OPCODETYPE_DIRECTVALUE; op->parms.dv.valuePtr=NULL; op->parms.dv.directValue=0.0; } // return joined_retv below } else { // if parameters are non-const, and we're a conditional, preserve our function if (FNPTR_HAS_CONDITIONAL_EXEC(op)) return 1; // otherwise, condense into either the non-const statement, or a join if (cnt==1) { memcpy(op,op->parms.parms[idx1],sizeof(*op)); } else if (cnt == 2) { op->opcodeType = OPCODETYPE_FUNC2; op->fntype = FN_JOIN_STATEMENTS; op->fn = op; op->parms.parms[0] = op->parms.parms[idx1]; op->parms.parms[1] = op->parms.parms[idx2]; op->parms.parms[2] = NULL; } else { // todo need to create a new opcodeRec here, for now just leave as is // (non-conditional const 3 parameter functions are rare anyway) } return 1; } } return joined_retv; } static int generateValueToReg(compileContext *ctx, opcodeRec *op, unsigned char *bufOut, int whichReg, const namespaceInformation *functionPrefix, int allowCache) { EEL_F *b=NULL; if (op->opcodeType==OPCODETYPE_VALUE_FROM_NAMESPACENAME) { char nm[NSEEL_MAX_VARIABLE_NAMELEN+1]; const char *p = op->relname; combineNamespaceFields(nm,functionPrefix,p+(*p == '#'),op->namespaceidx); if (!nm[0]) return -1; if (*p == '#') { if (ctx->isGeneratingCommonFunction) b = newCtxDataBlock(sizeof(EEL_F),sizeof(EEL_F)); else b = newDataBlock(sizeof(EEL_F),sizeof(EEL_F)); if (!b) RET_MINUS1_FAIL("error creating storage for str") if (!ctx->onNamedString) return -1; // should never happen, will not generate OPCODETYPE_VALUE_FROM_NAMESPACENAME with # prefix if !onNamedString *b = ctx->onNamedString(ctx->caller_this,nm); } else { b = nseel_int_register_var(ctx,nm,0,NULL); if (!b) RET_MINUS1_FAIL("error registering var") } } else { if (op->opcodeType != OPCODETYPE_DIRECTVALUE) allowCache=0; if (op->opcodeType==OPCODETYPE_DIRECTVALUE_TEMPSTRING && ctx->onNamedString) { op->parms.dv.directValue = ctx->onNamedString(ctx->caller_this,""); op->parms.dv.valuePtr = NULL; } b=op->parms.dv.valuePtr; if (!b && op->opcodeType == OPCODETYPE_VARPTR && op->relname && op->relname[0]) { op->parms.dv.valuePtr = b = nseel_int_register_var(ctx,op->relname,0,NULL); } if (b && op->opcodeType == OPCODETYPE_VARPTRPTR) b = *(EEL_F **)b; if (!b && allowCache) { int n=50; // only scan last X items opcodeRec *r = ctx->directValueCache; while (r && n--) { if (r->parms.dv.directValue == op->parms.dv.directValue && (b=r->parms.dv.valuePtr)) break; r=(opcodeRec*)r->fn; } } if (!b) { ctx->l_stats[3]++; if (ctx->isGeneratingCommonFunction) b = newCtxDataBlock(sizeof(EEL_F),sizeof(EEL_F)); else b = newDataBlock(sizeof(EEL_F),sizeof(EEL_F)); if (!b) RET_MINUS1_FAIL("error allocating data block") if (op->opcodeType != OPCODETYPE_VARPTRPTR) op->parms.dv.valuePtr = b; #if EEL_F_SIZE == 8 *b = denormal_filter_double2(op->parms.dv.directValue); #else *b = denormal_filter_float2(op->parms.dv.directValue); #endif if (allowCache) { op->fn = ctx->directValueCache; ctx->directValueCache = op; } } } GLUE_MOV_PX_DIRECTVALUE_GEN(bufOut,(INT_PTR)b,whichReg); return GLUE_MOV_PX_DIRECTVALUE_SIZE; } unsigned char *compileCodeBlockWithRet(compileContext *ctx, opcodeRec *rec, int *computTableSize, const namespaceInformation *namespacePathToThis, int supportedReturnValues, int *rvType, int *fpStackUsage, int *canHaveDenormalOutput) { unsigned char *p, *newblock2; // generate code call int funcsz=compileOpcodes(ctx,rec,NULL,1024*1024*128,NULL,namespacePathToThis,supportedReturnValues, rvType,fpStackUsage, NULL); if (funcsz<0) return NULL; p = newblock2 = newCodeBlock(funcsz+ sizeof(GLUE_RET)+GLUE_FUNC_ENTER_SIZE+GLUE_FUNC_LEAVE_SIZE,32); if (!newblock2) return NULL; #if GLUE_FUNC_ENTER_SIZE > 0 memcpy(p,&GLUE_FUNC_ENTER,GLUE_FUNC_ENTER_SIZE); p += GLUE_FUNC_ENTER_SIZE; #endif *fpStackUsage=0; funcsz=compileOpcodes(ctx,rec,p, funcsz, computTableSize,namespacePathToThis,supportedReturnValues, rvType,fpStackUsage, canHaveDenormalOutput); if (funcsz<0) return NULL; p+=funcsz; #if GLUE_FUNC_LEAVE_SIZE > 0 memcpy(p,&GLUE_FUNC_LEAVE,GLUE_FUNC_LEAVE_SIZE); p+=GLUE_FUNC_LEAVE_SIZE; #endif memcpy(p,&GLUE_RET,sizeof(GLUE_RET)); p+=sizeof(GLUE_RET); #if defined(__arm__) || defined(__aarch64__) __clear_cache(newblock2,p); #endif ctx->l_stats[2]+=funcsz+2; return newblock2; } static int compileNativeFunctionCall(compileContext *ctx, opcodeRec *op, unsigned char *bufOut, int bufOut_len, int *computTableSize, const namespaceInformation *namespacePathToThis, int *rvMode, int *fpStackUsage, int preferredReturnValues, int *canHaveDenormalOutput) { // builtin function generation int func_size=0; int cfunc_abiinfo=0; int local_fpstack_use=0; // how many items we have pushed onto the fp stack int parm_size=0; int restore_stack_amt=0; void *func_e=NULL; NSEEL_PPPROC preProc=0; void **repl=NULL; int n_params= 1 + op->opcodeType - OPCODETYPE_FUNC1; const int parm0_dv = op->parms.parms[0]->opcodeType == OPCODETYPE_DIRECTVALUE; const int parm1_dv = n_params > 1 && op->parms.parms[1]->opcodeType == OPCODETYPE_DIRECTVALUE; void *func = nseel_getBuiltinFunctionAddress(ctx, op->fntype, op->fn, &preProc,&repl,&func_e,&cfunc_abiinfo,preferredReturnValues, parm0_dv ? &op->parms.parms[0]->parms.dv.directValue : NULL, parm1_dv ? &op->parms.parms[1]->parms.dv.directValue : NULL ); if (!func) RET_MINUS1_FAIL("error getting funcaddr") *fpStackUsage=BIF_GETFPSTACKUSE(cfunc_abiinfo); *rvMode = RETURNVALUE_NORMAL; if (cfunc_abiinfo & BIF_TAKES_VARPARM) { #if defined(__arm__) || (defined (_M_ARM) && _M_ARM == 7) const int max_params=16384; // arm uses up to two instructions, should be good for at leaast 64k (16384*4) #elif defined(__ppc__) const int max_params=4096; // 32kb max offset addressing for stack, so 4096*4 = 16384, should be safe #elif defined(__aarch64__) const int max_params=32768; #else const int max_params=32768; // sanity check, the stack is free to grow on x86/x86-64 #endif int x; // this mode is less efficient in that it creates a list of pointers on the stack to pass to the function // but it is more flexible and works for >3 parameters. if (op->opcodeType == OPCODETYPE_FUNCX) { n_params=0; for (x=0;x<3;x++) { opcodeRec *prni=op->parms.parms[x]; while (prni) { const int isMP = prni->opcodeType == OPCODETYPE_MOREPARAMS; n_params++; if (!isMP||n_params>=max_params) break; prni = prni->parms.parms[1]; } } } restore_stack_amt = (sizeof(void *) * n_params + 15)&~15; if (restore_stack_amt) { int offs = restore_stack_amt; while (offs > 0) { int amt = offs; if (amt > 4096) amt=4096; if (bufOut_len < parm_size+GLUE_MOVE_STACK_SIZE) RET_MINUS1_FAIL("insufficient size for varparm") if (bufOut) GLUE_MOVE_STACK(bufOut+parm_size, - amt); parm_size += GLUE_MOVE_STACK_SIZE; offs -= amt; if (offs>0) // make sure this page is in memory { if (bufOut_len < parm_size+GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(0)) RET_MINUS1_FAIL("insufficient size for varparm stackchk") if (bufOut) GLUE_STORE_P1_TO_STACK_AT_OFFS(bufOut+parm_size,0); parm_size += GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(0); } } } if (op->opcodeType == OPCODETYPE_FUNCX) { n_params=0; for (x=0;x<3;x++) { opcodeRec *prni=op->parms.parms[x]; while (prni) { const int isMP = prni->opcodeType == OPCODETYPE_MOREPARAMS; opcodeRec *r = isMP ? prni->parms.parms[0] : prni; if (r) { int canHaveDenorm=0; int rvt=RETURNVALUE_NORMAL; int subfpstackuse=0, use_offs; int lsz = compileOpcodes(ctx,r,bufOut ? bufOut + parm_size : NULL,bufOut_len - parm_size, computTableSize, namespacePathToThis, rvt,&rvt, &subfpstackuse, &canHaveDenorm); if (canHaveDenorm && canHaveDenormalOutput) *canHaveDenormalOutput = 1; if (lsz<0) RET_MINUS1_FAIL("call coc for varparmX failed") if (rvt != RETURNVALUE_NORMAL) RET_MINUS1_FAIL("call coc for varparmX gave bad type back"); parm_size += lsz; use_offs = n_params*(int) sizeof(void *); if (bufOut_len < parm_size+GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(use_offs)) RET_MINUS1_FAIL("call coc for varparmX size"); if (bufOut) GLUE_STORE_P1_TO_STACK_AT_OFFS(bufOut + parm_size, use_offs); parm_size+=GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(use_offs); if (subfpstackuse+local_fpstack_use > *fpStackUsage) *fpStackUsage = subfpstackuse+local_fpstack_use; } else RET_MINUS1_FAIL("zero parameter varparmX") n_params++; if (!isMP||n_params>=max_params) break; prni = prni->parms.parms[1]; } } } else for (x=0;xparms.parms[x]; if (r) { int canHaveDenorm=0; int subfpstackuse=0; int rvt=RETURNVALUE_NORMAL; int use_offs; int lsz = compileOpcodes(ctx,r,bufOut ? bufOut + parm_size : NULL,bufOut_len - parm_size, computTableSize, namespacePathToThis, rvt,&rvt, &subfpstackuse, &canHaveDenorm); if (canHaveDenorm && canHaveDenormalOutput) *canHaveDenormalOutput = 1; if (lsz<0) RET_MINUS1_FAIL("call coc for varparm123 failed") if (rvt != RETURNVALUE_NORMAL) RET_MINUS1_FAIL("call coc for varparm123 gave bad type back"); parm_size += lsz; use_offs = x*(int)sizeof(void *); if (bufOut_len < parm_size+GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(use_offs)) RET_MINUS1_FAIL("call coc for varparm123 size"); if (bufOut) GLUE_STORE_P1_TO_STACK_AT_OFFS(bufOut + parm_size, use_offs); parm_size+=GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(use_offs); if (subfpstackuse+local_fpstack_use > *fpStackUsage) *fpStackUsage = subfpstackuse+local_fpstack_use; } else RET_MINUS1_FAIL("zero parameter for varparm123"); } if (bufOut_len < parm_size+GLUE_MOV_PX_DIRECTVALUE_SIZE+GLUE_MOVE_PX_STACKPTR_SIZE) RET_MINUS1_FAIL("insufficient size for varparm p1") if (bufOut) GLUE_MOV_PX_DIRECTVALUE_GEN(bufOut+parm_size, (INT_PTR)n_params,1); parm_size+=GLUE_MOV_PX_DIRECTVALUE_SIZE; if (bufOut) GLUE_MOVE_PX_STACKPTR_GEN(bufOut+parm_size, 0); parm_size+=GLUE_MOVE_PX_STACKPTR_SIZE; } else // not varparm { int pn; #ifdef GLUE_HAS_FXCH int need_fxch=0; #endif int last_nt_parm=-1, last_nt_parm_type=-1; if (op->opcodeType == OPCODETYPE_FUNCX) { // this is not yet supported (calling conventions will need to be sorted, among other things) RET_MINUS1_FAIL("funcx for native functions requires BIF_TAKES_VARPARM or BIF_TAKES_VARPARM_EX") } if (parm0_dv) { if (func == nseel_asm_stack_pop) { func = GLUE_realAddress(nseel_asm_stack_pop_fast,nseel_asm_stack_pop_fast_end,&func_size); if (!func || bufOut_len < func_size) RET_MINUS1_FAIL(func?"failed on popfast size":"failed on popfast addr") if (bufOut) { memcpy(bufOut,func,func_size); NSEEL_PProc_Stack(bufOut,func_size,ctx); } return func_size; } else if (func == nseel_asm_stack_peek) { int f = (int) op->parms.parms[0]->parms.dv.directValue; if (!f) { func = GLUE_realAddress(nseel_asm_stack_peek_top,nseel_asm_stack_peek_top_end,&func_size); if (!func || bufOut_len < func_size) RET_MINUS1_FAIL(func?"failed on peek size":"failed on peek addr") if (bufOut) { memcpy(bufOut,func,func_size); NSEEL_PProc_Stack_PeekTop(bufOut,func_size,ctx); } return func_size; } else { func = GLUE_realAddress(nseel_asm_stack_peek_int,nseel_asm_stack_peek_int_end,&func_size); if (!func || bufOut_len < func_size) RET_MINUS1_FAIL(func?"failed on peekint size":"failed on peekint addr") if (bufOut) { memcpy(bufOut,func,func_size); NSEEL_PProc_Stack_PeekInt(bufOut,func_size,ctx,f*sizeof(EEL_F)); } return func_size; } } } // end of built-in function specific special casing // first pass, calculate any non-trivial parameters for (pn=0; pn < n_params; pn++) { if (!OPCODE_IS_TRIVIAL(op->parms.parms[pn])) { int canHaveDenorm=0; int subfpstackuse=0; int lsz=0; int rvt=RETURNVALUE_NORMAL; int may_need_fppush=-1; if (last_nt_parm>=0) { if (last_nt_parm_type==RETURNVALUE_FPSTACK) { may_need_fppush= parm_size; } else { // push last result if (bufOut_len < parm_size + (int)sizeof(GLUE_PUSH_P1)) RET_MINUS1_FAIL("failed on size, pushp1") if (bufOut) memcpy(bufOut + parm_size, &GLUE_PUSH_P1, sizeof(GLUE_PUSH_P1)); parm_size += sizeof(GLUE_PUSH_P1); } } if (func == nseel_asm_bnot) rvt=RETURNVALUE_BOOL_REVERSED|RETURNVALUE_BOOL; else if (pn == n_params - 1) { if (cfunc_abiinfo&BIF_LASTPARMONSTACK) rvt=RETURNVALUE_FPSTACK; else if (cfunc_abiinfo&BIF_LASTPARM_ASBOOL) rvt=RETURNVALUE_BOOL; else if (func == nseel_asm_assign) rvt=RETURNVALUE_FPSTACK|RETURNVALUE_NORMAL; } else if (pn == n_params -2 && (cfunc_abiinfo&BIF_SECONDLASTPARMST)) { rvt=RETURNVALUE_FPSTACK; } lsz = compileOpcodes(ctx,op->parms.parms[pn],bufOut ? bufOut + parm_size : NULL,bufOut_len - parm_size, computTableSize, namespacePathToThis, rvt,&rvt, &subfpstackuse, &canHaveDenorm); if (lsz<0) RET_MINUS1_FAIL("call coc failed") if (func == nseel_asm_bnot && rvt==RETURNVALUE_BOOL_REVERSED) { // remove bnot, compileOpcodes() used fptobool_rev #ifndef EEL_TARGET_PORTABLE func = nseel_asm_uplus; func_e = nseel_asm_uplus_end; #else func = nseel_asm_bnotnot; func_e = nseel_asm_bnotnot_end; #endif rvt = RETURNVALUE_BOOL; } if (canHaveDenorm && canHaveDenormalOutput) *canHaveDenormalOutput = 1; parm_size += lsz; if (may_need_fppush>=0) { if (local_fpstack_use+subfpstackuse >= (GLUE_MAX_FPSTACK_SIZE-1) || (ctx->optimizeDisableFlags&OPTFLAG_NO_FPSTACK)) { if (bufOut_len < parm_size + (int)sizeof(GLUE_POP_FPSTACK_TOSTACK)) RET_MINUS1_FAIL("failed on size, popfpstacktostack") if (bufOut) { memmove(bufOut + may_need_fppush + sizeof(GLUE_POP_FPSTACK_TOSTACK), bufOut + may_need_fppush, parm_size - may_need_fppush); memcpy(bufOut + may_need_fppush, &GLUE_POP_FPSTACK_TOSTACK, sizeof(GLUE_POP_FPSTACK_TOSTACK)); } parm_size += sizeof(GLUE_POP_FPSTACK_TOSTACK); } else { local_fpstack_use++; } } if (subfpstackuse+local_fpstack_use > *fpStackUsage) *fpStackUsage = subfpstackuse+local_fpstack_use; last_nt_parm = pn; last_nt_parm_type = rvt; if (pn == n_params - 1 && func == nseel_asm_assign) { if (!(ctx->optimizeDisableFlags & OPTFLAG_FULL_DENORMAL_CHECKS) && (!canHaveDenorm || (ctx->optimizeDisableFlags & OPTFLAG_NO_DENORMAL_CHECKS))) { if (rvt == RETURNVALUE_FPSTACK) { cfunc_abiinfo |= BIF_LASTPARMONSTACK; func = nseel_asm_assign_fast_fromfp; func_e = nseel_asm_assign_fast_fromfp_end; } else { func = nseel_asm_assign_fast; func_e = nseel_asm_assign_fast_end; } } else { if (rvt == RETURNVALUE_FPSTACK) { cfunc_abiinfo |= BIF_LASTPARMONSTACK; func = nseel_asm_assign_fromfp; func_e = nseel_asm_assign_fromfp_end; } } } } } pn = last_nt_parm; if (pn >= 0) // if the last thing executed doesn't go to the last parameter, move it there { if ((cfunc_abiinfo&BIF_SECONDLASTPARMST) && pn == n_params-2) { // do nothing, things are in the right place } else if (pn != n_params-1) { // generate mov p1->pX if (bufOut_len < parm_size + GLUE_SET_PX_FROM_P1_SIZE) RET_MINUS1_FAIL("size, pxfromp1") if (bufOut) GLUE_SET_PX_FROM_P1(bufOut + parm_size,n_params - 1 - pn); parm_size += GLUE_SET_PX_FROM_P1_SIZE; } } // pop any pushed parameters while (--pn >= 0) { if (!OPCODE_IS_TRIVIAL(op->parms.parms[pn])) { if ((cfunc_abiinfo&BIF_SECONDLASTPARMST) && pn == n_params-2) { if (!local_fpstack_use) { if (bufOut_len < parm_size + (int)sizeof(GLUE_POP_STACK_TO_FPSTACK)) RET_MINUS1_FAIL("size, popstacktofpstack 2") if (bufOut) memcpy(bufOut+parm_size,GLUE_POP_STACK_TO_FPSTACK,sizeof(GLUE_POP_STACK_TO_FPSTACK)); parm_size += sizeof(GLUE_POP_STACK_TO_FPSTACK); #ifdef GLUE_HAS_FXCH need_fxch = 1; #endif } else { local_fpstack_use--; } } else { if (bufOut_len < parm_size + GLUE_POP_PX_SIZE) RET_MINUS1_FAIL("size, poppx") if (bufOut) GLUE_POP_PX(bufOut + parm_size,n_params - 1 - pn); parm_size += GLUE_POP_PX_SIZE; } } } // finally, set trivial pointers for (pn=0; pn < n_params; pn++) { if (OPCODE_IS_TRIVIAL(op->parms.parms[pn])) { if (pn == n_params-2 && (cfunc_abiinfo&(BIF_SECONDLASTPARMST))) // second to last parameter { int a = compileOpcodes(ctx,op->parms.parms[pn],bufOut ? bufOut+parm_size : NULL,bufOut_len - parm_size,computTableSize,namespacePathToThis, RETURNVALUE_FPSTACK,NULL,NULL,canHaveDenormalOutput); if (a<0) RET_MINUS1_FAIL("coc call here 2") parm_size+=a; #ifdef GLUE_HAS_FXCH need_fxch = 1; #endif } else if (pn == n_params-1) // last parameter, but we should call compileOpcodes to get it in the right format (compileOpcodes can optimize that process if it needs to) { int rvt=0, a; int wantFpStack = func == nseel_asm_assign; #ifdef GLUE_PREFER_NONFP_DV_ASSIGNS // x86-64, and maybe others, prefer to avoid the fp stack for a simple copy if (wantFpStack && (op->parms.parms[pn]->opcodeType != OPCODETYPE_DIRECTVALUE || (op->parms.parms[pn]->parms.dv.directValue != 1.0 && op->parms.parms[pn]->parms.dv.directValue != 0.0))) { wantFpStack=-1; // cacheable but non-FP stack } #endif a = compileOpcodes(ctx,op->parms.parms[pn],bufOut ? bufOut+parm_size : NULL,bufOut_len - parm_size,computTableSize,namespacePathToThis, func == nseel_asm_bnot ? (RETURNVALUE_BOOL_REVERSED|RETURNVALUE_BOOL) : (cfunc_abiinfo & BIF_LASTPARMONSTACK) ? RETURNVALUE_FPSTACK : (cfunc_abiinfo & BIF_LASTPARM_ASBOOL) ? RETURNVALUE_BOOL : wantFpStack < 0 ? (RETURNVALUE_CACHEABLE|RETURNVALUE_NORMAL) : wantFpStack ? (RETURNVALUE_FPSTACK|RETURNVALUE_NORMAL) : RETURNVALUE_NORMAL, &rvt, NULL,canHaveDenormalOutput); if (a<0) RET_MINUS1_FAIL("coc call here 3") if (func == nseel_asm_bnot && rvt == RETURNVALUE_BOOL_REVERSED) { // remove bnot, compileOpcodes() used fptobool_rev #ifndef EEL_TARGET_PORTABLE func = nseel_asm_uplus; func_e = nseel_asm_uplus_end; #else func = nseel_asm_bnotnot; func_e = nseel_asm_bnotnot_end; #endif rvt = RETURNVALUE_BOOL; } parm_size+=a; #ifdef GLUE_HAS_FXCH need_fxch = 0; #endif if (func == nseel_asm_assign) { if (rvt == RETURNVALUE_FPSTACK) { if (!(ctx->optimizeDisableFlags & OPTFLAG_FULL_DENORMAL_CHECKS)) { func = nseel_asm_assign_fast_fromfp; func_e = nseel_asm_assign_fast_fromfp_end; } else { func = nseel_asm_assign_fromfp; func_e = nseel_asm_assign_fromfp_end; } } else if (!(ctx->optimizeDisableFlags & OPTFLAG_FULL_DENORMAL_CHECKS)) { // assigning a value (from a variable or other non-computer), can use a fast assign (no denormal/result checking) func = nseel_asm_assign_fast; func_e = nseel_asm_assign_fast_end; } } } else { if (bufOut_len < parm_size + GLUE_MOV_PX_DIRECTVALUE_SIZE) RET_MINUS1_FAIL("size, pxdvsz") if (bufOut) { if (generateValueToReg(ctx,op->parms.parms[pn],bufOut + parm_size,n_params - 1 - pn,namespacePathToThis, 0/*nocaching, function gets pointer*/)<0) RET_MINUS1_FAIL("gvtr") } parm_size += GLUE_MOV_PX_DIRECTVALUE_SIZE; } } } #ifdef GLUE_HAS_FXCH if ((cfunc_abiinfo&(BIF_SECONDLASTPARMST)) && !(cfunc_abiinfo&(BIF_LAZYPARMORDERING))&& ((!!need_fxch)^!!(cfunc_abiinfo&BIF_REVERSEFPORDER)) ) { // emit fxch if (bufOut_len < sizeof(GLUE_FXCH)) RET_MINUS1_FAIL("len,fxch") if (bufOut) { memcpy(bufOut+parm_size,GLUE_FXCH,sizeof(GLUE_FXCH)); } parm_size+=sizeof(GLUE_FXCH); } #endif if (!*canHaveDenormalOutput) { // if add_op or sub_op, and constant non-denormal input, safe to omit denormal checks if (func == (void*)nseel_asm_add_op && parm1_dv && fabs(op->parms.parms[1]->parms.dv.directValue) >= DENORMAL_CLEARING_THRESHOLD) { func = nseel_asm_add_op_fast; func_e = nseel_asm_add_op_fast_end; } else if (func == (void*)nseel_asm_sub_op && parm1_dv && fabs(op->parms.parms[1]->parms.dv.directValue) >= DENORMAL_CLEARING_THRESHOLD) { func = nseel_asm_sub_op_fast; func_e = nseel_asm_sub_op_fast_end; } // or if mul/div by a fixed value of >= or <= 1.0 else if (func == (void *)nseel_asm_mul_op && parm1_dv && fabs(op->parms.parms[1]->parms.dv.directValue) >= 1.0) { func = nseel_asm_mul_op_fast; func_e = nseel_asm_mul_op_fast_end; } else if (func == (void *)nseel_asm_div_op && parm1_dv && fabs(op->parms.parms[1]->parms.dv.directValue) <= 1.0) { func = nseel_asm_div_op_fast; func_e = nseel_asm_div_op_fast_end; } } } // not varparm if (cfunc_abiinfo & (BIF_CLEARDENORMAL | BIF_RETURNSBOOL) ) *canHaveDenormalOutput=0; else if (!(cfunc_abiinfo & BIF_WONTMAKEDENORMAL)) *canHaveDenormalOutput=1; func = GLUE_realAddress(func,func_e,&func_size); if (!func) RET_MINUS1_FAIL("failrealladdrfunc") if (bufOut_len < parm_size + func_size) RET_MINUS1_FAIL("funcsz") if (bufOut) { unsigned char *p=bufOut + parm_size; memcpy(p, func, func_size); if (preProc) p=preProc(p,func_size,ctx); if (repl) { if (repl[0]) p=EEL_GLUE_set_immediate(p,(INT_PTR)repl[0]); if (repl[1]) p=EEL_GLUE_set_immediate(p,(INT_PTR)repl[1]); if (repl[2]) p=EEL_GLUE_set_immediate(p,(INT_PTR)repl[2]); if (repl[3]) p=EEL_GLUE_set_immediate(p,(INT_PTR)repl[3]); } } if (restore_stack_amt) { int rem = restore_stack_amt; while (rem > 0) { int amt = rem; if (amt > 4096) amt=4096; rem -= amt; if (bufOut_len < parm_size + func_size + GLUE_MOVE_STACK_SIZE) RET_MINUS1_FAIL("insufficient size for varparm") if (bufOut) GLUE_MOVE_STACK(bufOut + parm_size + func_size, amt); parm_size += GLUE_MOVE_STACK_SIZE; } } if (cfunc_abiinfo&BIF_RETURNSONSTACK) *rvMode = RETURNVALUE_FPSTACK; else if (cfunc_abiinfo&BIF_RETURNSBOOL) *rvMode=RETURNVALUE_BOOL; return parm_size + func_size; } static int compileEelFunctionCall(compileContext *ctx, opcodeRec *op, unsigned char *bufOut, int bufOut_len, int *computTableSize, const namespaceInformation *namespacePathToThis, int *rvMode, int *fpStackUse, int *canHaveDenormalOutput) { int func_size=0, parm_size=0; int pn; int last_nt_parm=-1,last_nt_parm_mode=0; void *func_e=NULL; int n_params; opcodeRec *parmptrs[NSEEL_MAX_EELFUNC_PARAMETERS]; int cfp_numparams=-1; int cfp_statesize=0; EEL_F **cfp_ptrs=NULL; int func_raw=0; int do_parms; int x; void *func; for (x=0; x < 3; x ++) parmptrs[x] = op->parms.parms[x]; if (op->opcodeType == OPCODETYPE_FUNCX) { n_params=0; for (x=0;x<3;x++) { opcodeRec *prni=op->parms.parms[x]; while (prni && n_params < NSEEL_MAX_EELFUNC_PARAMETERS) { const int isMP = prni->opcodeType == OPCODETYPE_MOREPARAMS; parmptrs[n_params++] = isMP ? prni->parms.parms[0] : prni; if (!isMP) break; prni = prni->parms.parms[1]; } } } else { n_params = 1 + op->opcodeType - OPCODETYPE_FUNC1; } *fpStackUse = 0; func = nseel_getEELFunctionAddress(ctx, op, &cfp_numparams,&cfp_statesize,&cfp_ptrs, computTableSize, &func_e, &func_raw, !!bufOut,namespacePathToThis,rvMode,fpStackUse,canHaveDenormalOutput, parmptrs, n_params); if (func_raw) func_size = (int) ((char*)func_e - (char*)func); else if (func) func = GLUE_realAddress(func,func_e,&func_size); if (!func) RET_MINUS1_FAIL("eelfuncaddr") *fpStackUse += 1; if (cfp_numparams>0 && n_params != cfp_numparams) { RET_MINUS1_FAIL("eelfuncnp") } // user defined function do_parms = cfp_numparams>0 && cfp_ptrs && cfp_statesize>0; // if function local/parameter state is zero, we need to allocate storage for it if (cfp_statesize>0 && cfp_ptrs && !cfp_ptrs[0]) { EEL_F *pstate = newDataBlock(sizeof(EEL_F)*cfp_statesize,8); if (!pstate) RET_MINUS1_FAIL("eelfuncdb") for (pn=0;pn= 0 && do_parms) { if (last_nt_parm_mode == RETURNVALUE_FPSTACK) { if (bufOut_len < parm_size + (int)sizeof(GLUE_POP_FPSTACK_TOSTACK)) RET_MINUS1_FAIL("eelfunc_size popfpstacktostack") if (bufOut) memcpy(bufOut + parm_size,GLUE_POP_FPSTACK_TOSTACK,sizeof(GLUE_POP_FPSTACK_TOSTACK)); parm_size+=sizeof(GLUE_POP_FPSTACK_TOSTACK); } else { if (bufOut_len < parm_size + (int)sizeof(GLUE_PUSH_P1PTR_AS_VALUE)) RET_MINUS1_FAIL("eelfunc_size pushp1ptrasval") // push if (bufOut) memcpy(bufOut + parm_size,&GLUE_PUSH_P1PTR_AS_VALUE,sizeof(GLUE_PUSH_P1PTR_AS_VALUE)); parm_size+=sizeof(GLUE_PUSH_P1PTR_AS_VALUE); } } last_nt_parm_mode=0; lsz = compileOpcodes(ctx,parmptrs[pn],bufOut ? bufOut + parm_size : NULL,bufOut_len - parm_size, computTableSize, namespacePathToThis, do_parms ? (RETURNVALUE_FPSTACK|RETURNVALUE_NORMAL) : RETURNVALUE_IGNORE,&last_nt_parm_mode,&sUse, &needDenorm); // todo: if needDenorm, denorm convert when copying parameter if (lsz<0) RET_MINUS1_FAIL("eelfunc, coc fail") if (last_nt_parm_mode == RETURNVALUE_FPSTACK) sUse++; if (sUse > *fpStackUse) *fpStackUse=sUse; parm_size += lsz; last_nt_parm = pn; } // pop non-trivial results into place if (last_nt_parm >=0 && do_parms) { while (--pn >= 0) { if (!parmptrs[pn] || OPCODE_IS_TRIVIAL(parmptrs[pn])) continue; // skip and process after if (pn == last_nt_parm) { if (last_nt_parm_mode == RETURNVALUE_FPSTACK) { // pop to memory directly const int cpsize = GLUE_POP_FPSTACK_TO_PTR(NULL,NULL); if (bufOut_len < parm_size + cpsize) RET_MINUS1_FAIL("eelfunc size popfpstacktoptr") if (bufOut) GLUE_POP_FPSTACK_TO_PTR((unsigned char *)bufOut + parm_size,cfp_ptrs[pn]); parm_size += cpsize; } else { // copy direct p1ptr to mem const int cpsize = GLUE_COPY_VALUE_AT_P1_TO_PTR(NULL,NULL); if (bufOut_len < parm_size + cpsize) RET_MINUS1_FAIL("eelfunc size copyvalueatp1toptr") if (bufOut) GLUE_COPY_VALUE_AT_P1_TO_PTR((unsigned char *)bufOut + parm_size,cfp_ptrs[pn]); parm_size += cpsize; } } else { const int popsize = GLUE_POP_VALUE_TO_ADDR(NULL,NULL); if (bufOut_len < parm_size + popsize) RET_MINUS1_FAIL("eelfunc size pop value to addr") if (bufOut) GLUE_POP_VALUE_TO_ADDR((unsigned char *)bufOut + parm_size,cfp_ptrs[pn]); parm_size+=popsize; } } } // finally, set any trivial parameters if (do_parms) { const int cpsize = GLUE_MOV_PX_DIRECTVALUE_SIZE + GLUE_COPY_VALUE_AT_P1_TO_PTR(NULL,NULL); for (pn=0; pn < n_params; pn++) { if (!parmptrs[pn] || !OPCODE_IS_TRIVIAL(parmptrs[pn])) continue; // set trivial values, we already set nontrivials if (bufOut_len < parm_size + cpsize) RET_MINUS1_FAIL("eelfunc size trivial set") if (bufOut) { if (generateValueToReg(ctx,parmptrs[pn],bufOut + parm_size,0,namespacePathToThis, 1)<0) RET_MINUS1_FAIL("eelfunc gvr fail") GLUE_COPY_VALUE_AT_P1_TO_PTR(bufOut + parm_size + GLUE_MOV_PX_DIRECTVALUE_SIZE,cfp_ptrs[pn]); } parm_size += cpsize; } } if (bufOut_len < parm_size + func_size) RET_MINUS1_FAIL("eelfunc size combined") if (bufOut) memcpy(bufOut + parm_size, func, func_size); return parm_size + func_size; // end of EEL function generation } #ifdef DUMP_OPS_DURING_COMPILE void dumpOp(compileContext *ctx, opcodeRec *op, int start); #endif #ifdef EEL_DUMP_OPS void dumpOpcodeTree(compileContext *ctx, FILE *fp, opcodeRec *op, int indent_amt) { const char *fname=""; fprintf(fp,"%*sOP TYPE %d", indent_amt, "", op->opcodeType==OPCODETYPE_DIRECTVALUE_TEMPSTRING ? 10000 : // remap around OPCODETYPE_DIRECTVALUE_TEMPSTRING op->opcodeType > OPCODETYPE_DIRECTVALUE_TEMPSTRING ? op->opcodeType - 1 : op->opcodeType); if ((op->opcodeType == OPCODETYPE_FUNC1 || op->opcodeType == OPCODETYPE_FUNC2 || op->opcodeType == OPCODETYPE_FUNC3 || op->opcodeType == OPCODETYPE_FUNCX)) { if (op->fntype == FUNCTYPE_FUNCTIONTYPEREC) { functionType *fn_ptr = (functionType *)op->fn; fname = fn_ptr->name; } else if (op->fntype == FUNCTYPE_EELFUNC) { fname = op->relname; } if (!fname) fname =""; } switch (op->opcodeType) { case OPCODETYPE_DIRECTVALUE: fprintf(fp," DV=%f\r\n",op->parms.dv.directValue); break; case OPCODETYPE_VALUE_FROM_NAMESPACENAME: // this.* or namespace.* are encoded this way fprintf(fp," NSN=%s(%d)\r\n",op->relname?op->relname : "(null)",op->namespaceidx); break; case OPCODETYPE_VARPTR: { const char *nm = op->relname; if (!nm || !*nm) { int wb; for (wb = 0; wb < ctx->varTable_numBlocks; wb ++) { char **plist=ctx->varTable_Names[wb]; if (!plist) break; if (op->parms.dv.valuePtr >= ctx->varTable_Values[wb] && op->parms.dv.valuePtr < ctx->varTable_Values[wb] + NSEEL_VARS_PER_BLOCK) { nm = plist[op->parms.dv.valuePtr - ctx->varTable_Values[wb]]; break; } } } fprintf(fp," VP=%s\r\n", nm?nm : "(null)"); } break; case OPCODETYPE_VARPTRPTR: fprintf(fp, " VPP?\r\n"); break; case OPCODETYPE_FUNC1: if (op->fntype == FN_NOT) fprintf(fp," FUNC1 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_not"); else if (op->fntype == FN_NOTNOT) fprintf(fp," FUNC1 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_notnot"); else if (op->fntype == FN_MEMORY) fprintf(fp," FUNC1 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_mem"); else if (op->fntype == FN_GMEMORY) fprintf(fp," FUNC1 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_gmem"); else if (op->fntype == FN_WHILE) fprintf(fp," FUNC1 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "while"); else fprintf(fp," FUNC1 %d %s {\r\n",op->fntype, fname); if (op->parms.parms[0]) dumpOpcodeTree(ctx,fp,op->parms.parms[0],indent_amt+2); else fprintf(fp,"%*sINVALID PARM\r\n",indent_amt+2,""); fprintf(fp,"%*s}\r\n", indent_amt, ""); break; case OPCODETYPE_MOREPARAMS: case OPCODETYPE_FUNC2: if (op->opcodeType == OPCODETYPE_MOREPARAMS) fprintf(fp," MOREPARAMS {\r\n"); else { if (op->fntype == FN_POW) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "pow"); else if (op->fntype == FN_MOD) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_mod"); else if (op->fntype == FN_XOR) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_xor"); else if (op->fntype == FN_SHL) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_shl"); else if (op->fntype == FN_SHR) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_shr"); else if (op->fntype == FN_LT) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_below"); else if (op->fntype == FN_GT) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_above"); else if (op->fntype == FN_LTE) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_beleq"); else if (op->fntype == FN_GTE) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_aboeq"); else if (op->fntype == FN_EQ) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_equal"); else if (op->fntype == FN_NE) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_noteq"); else if (op->fntype == FN_EQ_EXACT) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_equal_exact"); else if (op->fntype == FN_NE_EXACT) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_noteq_exact"); else if (op->fntype == FN_LOGICAL_AND) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_and"); else if (op->fntype == FN_LOGICAL_OR) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_or"); else if (op->fntype == FN_ASSIGN) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_set"); else if (op->fntype == FN_ADD_OP) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_addop"); else if (op->fntype == FN_SUB_OP) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_subop"); else if (op->fntype == FN_MUL_OP) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_mulop"); else if (op->fntype == FN_DIV_OP) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_divop"); else if (op->fntype == FN_OR_OP) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_orop"); else if (op->fntype == FN_AND_OP) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_andop"); else if (op->fntype == FN_XOR_OP) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_xorop"); else if (op->fntype == FN_MOD_OP) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_modop"); else if (op->fntype == FN_POW_OP) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_powop"); else if (op->fntype == FN_LOOP) fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "loop"); else fprintf(fp," FUNC2 %d %s {\r\n",op->fntype, fname); } if (op->parms.parms[0]) dumpOpcodeTree(ctx,fp,op->parms.parms[0],indent_amt+2); else fprintf(fp,"%*sINVALID PARM\r\n",indent_amt+2,""); if (op->parms.parms[1]) dumpOpcodeTree(ctx,fp,op->parms.parms[1],indent_amt+2); else fprintf(fp,"%*sINVALID PARM\r\n",indent_amt+2,""); fprintf(fp,"%*s}\r\n", indent_amt, ""); break; case OPCODETYPE_FUNCX: case OPCODETYPE_FUNC3: if (op->opcodeType == OPCODETYPE_FUNCX) fprintf(fp," FUNCX %d %s {\r\n",op->fntype, fname); else if (op->fntype == FN_IF_ELSE) fprintf(fp," FUNC3 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_if"); else fprintf(fp," FUNC3 %d %s {\r\n",op->fntype, fname); if (op->parms.parms[0]) dumpOpcodeTree(ctx,fp,op->parms.parms[0],indent_amt+2); else fprintf(fp,"%*sINVALID PARM\r\n",indent_amt+2,""); if (op->parms.parms[1]) dumpOpcodeTree(ctx,fp,op->parms.parms[1],indent_amt+2); else fprintf(fp,"%*sINVALID PARM\r\n",indent_amt+2,""); if (op->parms.parms[2]) dumpOpcodeTree(ctx,fp,op->parms.parms[2],indent_amt+2); else fprintf(fp,"%*sINVALID PARM\r\n",indent_amt+2,""); fprintf(fp,"%*s}\r\n", indent_amt, ""); break; } } #endif #ifdef GLUE_MAX_JMPSIZE #define CHECK_SIZE_FORJMP(x,y) if ((x)<0 || (x)>=GLUE_MAX_JMPSIZE) goto y; #define RET_MINUS1_FAIL_FALLBACK(err,j) goto j; #else #define CHECK_SIZE_FORJMP(x,y) #define RET_MINUS1_FAIL_FALLBACK(err,j) RET_MINUS1_FAIL(err) #endif static int compileOpcodesInternal(compileContext *ctx, opcodeRec *op, unsigned char *bufOut, int bufOut_len, int *computTableSize, const namespaceInformation *namespacePathToThis, int *calledRvType, int preferredReturnValues, int *fpStackUse, int *canHaveDenormalOutput) { int rv_offset=0, denormal_force=-1; if (!op) RET_MINUS1_FAIL("coi !op") *fpStackUse=0; for (;;) { // special case: statement delimiting means we can process the left side into place, and iteratively do the second parameter without recursing // also we don't need to save/restore anything to the stack (which the normal 2 parameter function processing does) if (op->opcodeType == OPCODETYPE_FUNC2 && op->fntype == FN_JOIN_STATEMENTS) { int fUse1; int parm_size = compileOpcodes(ctx,op->parms.parms[0],bufOut,bufOut_len, computTableSize, namespacePathToThis, RETURNVALUE_IGNORE, NULL,&fUse1,NULL); if (parm_size < 0) RET_MINUS1_FAIL("coc join fail") op = op->parms.parms[1]; if (!op) RET_MINUS1_FAIL("join got to null") if (fUse1>*fpStackUse) *fpStackUse=fUse1; if (bufOut) bufOut += parm_size; bufOut_len -= parm_size; rv_offset += parm_size; #ifdef DUMP_OPS_DURING_COMPILE if (op->opcodeType != OPCODETYPE_FUNC2 || op->fntype != FN_JOIN_STATEMENTS) dumpOp(ctx,op,0); #endif denormal_force=-1; } // special case: __denormal_likely(), __denormal_unlikely() else if (op->opcodeType == OPCODETYPE_FUNC1 && (op->fntype == FN_DENORMAL_LIKELY || op->fntype == FN_DENORMAL_UNLIKELY)) { denormal_force = op->fntype == FN_DENORMAL_LIKELY; op = op->parms.parms[0]; } else { break; } } if (denormal_force >= 0 && canHaveDenormalOutput) { *canHaveDenormalOutput = denormal_force; canHaveDenormalOutput = &denormal_force; // prevent it from being changed by functions below } // special case: BAND/BOR if (op->opcodeType == OPCODETYPE_FUNC2 && (op->fntype == FN_LOGICAL_AND || op->fntype == FN_LOGICAL_OR)) { int fUse1=0; int parm_size; #ifdef GLUE_MAX_JMPSIZE int parm_size_pre; #endif int retType=RETURNVALUE_IGNORE; if (preferredReturnValues != RETURNVALUE_IGNORE) retType = RETURNVALUE_BOOL; *calledRvType = retType; parm_size = compileOpcodes(ctx,op->parms.parms[0],bufOut,bufOut_len, computTableSize, namespacePathToThis, RETURNVALUE_BOOL, NULL, &fUse1, NULL); if (parm_size < 0) RET_MINUS1_FAIL("loop band/bor coc fail") if (fUse1 > *fpStackUse) *fpStackUse=fUse1; #ifdef GLUE_MAX_JMPSIZE parm_size_pre=parm_size; #endif { int sz2, fUse2=0; unsigned char *destbuf; const int testsz=op->fntype == FN_LOGICAL_OR ? sizeof(GLUE_JMP_IF_P1_NZ) : sizeof(GLUE_JMP_IF_P1_Z); if (bufOut_len < parm_size+testsz) RET_MINUS1_FAIL_FALLBACK("band/bor size fail",doNonInlinedAndOr_) if (bufOut) memcpy(bufOut+parm_size,op->fntype == FN_LOGICAL_OR ? GLUE_JMP_IF_P1_NZ : GLUE_JMP_IF_P1_Z,testsz); parm_size += testsz; destbuf = bufOut + parm_size; sz2= compileOpcodes(ctx,op->parms.parms[1],bufOut?bufOut+parm_size:NULL,bufOut_len-parm_size, computTableSize, namespacePathToThis, retType, NULL,&fUse2, NULL); CHECK_SIZE_FORJMP(sz2,doNonInlinedAndOr_) if (sz2<0) RET_MINUS1_FAIL("band/bor coc fail") parm_size+=sz2; if (bufOut) GLUE_JMP_SET_OFFSET(destbuf, (bufOut + parm_size) - destbuf); if (fUse2 > *fpStackUse) *fpStackUse=fUse2; return rv_offset + parm_size; } #ifdef GLUE_MAX_JMPSIZE if (0) { void *stub; int stubsize; unsigned char *newblock2, *p; // encode as function call doNonInlinedAndOr_: parm_size = parm_size_pre; if (op->fntype == FN_LOGICAL_AND) { stub = GLUE_realAddress(nseel_asm_band,nseel_asm_band_end,&stubsize); } else { stub = GLUE_realAddress(nseel_asm_bor,nseel_asm_bor_end,&stubsize); } if (bufOut_len < parm_size + stubsize) RET_MINUS1_FAIL("band/bor len fail") if (bufOut) { int fUse2=0; newblock2 = compileCodeBlockWithRet(ctx,op->parms.parms[1],computTableSize,namespacePathToThis, retType, NULL, &fUse2, NULL); if (!newblock2) RET_MINUS1_FAIL("band/bor ccbwr fail") if (fUse2 > *fpStackUse) *fpStackUse=fUse2; p = bufOut + parm_size; memcpy(p, stub, stubsize); p=EEL_GLUE_set_immediate(p,(INT_PTR)newblock2); } return rv_offset + parm_size + stubsize; } #endif } if (op->opcodeType == OPCODETYPE_FUNC3 && op->fntype == FN_IF_ELSE) // special case: IF { int fUse1=0; #ifdef GLUE_MAX_JMPSIZE int parm_size_pre; #endif int use_rv = RETURNVALUE_IGNORE; int rvMode=0; int parm_size = compileOpcodes(ctx,op->parms.parms[0],bufOut,bufOut_len, computTableSize, namespacePathToThis, RETURNVALUE_BOOL|RETURNVALUE_BOOL_REVERSED, &rvMode,&fUse1, NULL); if (parm_size < 0) RET_MINUS1_FAIL("if coc fail") if (fUse1 > *fpStackUse) *fpStackUse=fUse1; if (preferredReturnValues & RETURNVALUE_NORMAL) use_rv=RETURNVALUE_NORMAL; else if (preferredReturnValues & RETURNVALUE_FPSTACK) use_rv=RETURNVALUE_FPSTACK; else if (preferredReturnValues & RETURNVALUE_BOOL) use_rv=RETURNVALUE_BOOL; *calledRvType = use_rv; #ifdef GLUE_MAX_JMPSIZE parm_size_pre = parm_size; #endif { int csz,hasSecondHalf; if (rvMode & RETURNVALUE_BOOL_REVERSED) { if (bufOut_len < parm_size + (int)sizeof(GLUE_JMP_IF_P1_NZ)) RET_MINUS1_FAIL_FALLBACK("if size fail",doNonInlineIf_) if (bufOut) memcpy(bufOut+parm_size,GLUE_JMP_IF_P1_NZ,sizeof(GLUE_JMP_IF_P1_NZ)); parm_size += sizeof(GLUE_JMP_IF_P1_NZ); } else { if (bufOut_len < parm_size + (int)sizeof(GLUE_JMP_IF_P1_Z)) RET_MINUS1_FAIL_FALLBACK("if size fail",doNonInlineIf_) if (bufOut) memcpy(bufOut+parm_size,GLUE_JMP_IF_P1_Z,sizeof(GLUE_JMP_IF_P1_Z)); parm_size += sizeof(GLUE_JMP_IF_P1_Z); } csz=compileOpcodes(ctx,op->parms.parms[1],bufOut ? bufOut+parm_size : NULL,bufOut_len - parm_size, computTableSize, namespacePathToThis, use_rv, NULL,&fUse1, canHaveDenormalOutput); if (fUse1 > *fpStackUse) *fpStackUse=fUse1; hasSecondHalf = preferredReturnValues || !OPCODE_IS_TRIVIAL(op->parms.parms[2]); CHECK_SIZE_FORJMP(csz,doNonInlineIf_) if (csz<0) RET_MINUS1_FAIL("if coc fial") if (bufOut) GLUE_JMP_SET_OFFSET(bufOut + parm_size, csz + (hasSecondHalf?sizeof(GLUE_JMP_NC):0)); parm_size+=csz; if (hasSecondHalf) { if (bufOut_len < parm_size + (int)sizeof(GLUE_JMP_NC)) RET_MINUS1_FAIL_FALLBACK("if len fail",doNonInlineIf_) if (bufOut) memcpy(bufOut+parm_size,GLUE_JMP_NC,sizeof(GLUE_JMP_NC)); parm_size+=sizeof(GLUE_JMP_NC); csz=compileOpcodes(ctx,op->parms.parms[2],bufOut ? bufOut+parm_size : NULL,bufOut_len - parm_size, computTableSize, namespacePathToThis, use_rv, NULL, &fUse1, canHaveDenormalOutput); CHECK_SIZE_FORJMP(csz,doNonInlineIf_) if (csz<0) RET_MINUS1_FAIL("if coc 2 fail") // update jump address if (bufOut) GLUE_JMP_SET_OFFSET(bufOut + parm_size,csz); parm_size+=csz; if (fUse1 > *fpStackUse) *fpStackUse=fUse1; } return rv_offset + parm_size; } #ifdef GLUE_MAX_JMPSIZE if (0) { unsigned char *newblock2,*newblock3,*ptr; void *stub; int stubsize; doNonInlineIf_: parm_size = parm_size_pre; stub = GLUE_realAddress(nseel_asm_if,nseel_asm_if_end,&stubsize); if (!stub || bufOut_len < parm_size + stubsize) RET_MINUS1_FAIL(stub ? "if sz fail" : "if addr fail") if (bufOut) { int fUse2=0; newblock2 = compileCodeBlockWithRet(ctx,op->parms.parms[1],computTableSize,namespacePathToThis, use_rv, NULL,&fUse2, canHaveDenormalOutput); if (fUse2 > *fpStackUse) *fpStackUse=fUse2; newblock3 = compileCodeBlockWithRet(ctx,op->parms.parms[2],computTableSize,namespacePathToThis, use_rv, NULL,&fUse2, canHaveDenormalOutput); if (fUse2 > *fpStackUse) *fpStackUse=fUse2; if (!newblock2 || !newblock3) RET_MINUS1_FAIL("if subblock gen fail") ptr = bufOut + parm_size; memcpy(ptr, stub, stubsize); ptr=EEL_GLUE_set_immediate(ptr,(INT_PTR)newblock2); EEL_GLUE_set_immediate(ptr,(INT_PTR)newblock3); } return rv_offset + parm_size + stubsize; } #endif } { // special case: while if (op->opcodeType == OPCODETYPE_FUNC1 && op->fntype == FN_WHILE) { *calledRvType = RETURNVALUE_BOOL; #ifndef GLUE_INLINE_LOOPS // todo: PPC looping support when loop length is small enough { unsigned char *pwr=bufOut; unsigned char *newblock2; int stubsz; void *stubfunc = GLUE_realAddress(nseel_asm_repeatwhile,nseel_asm_repeatwhile_end,&stubsz); if (!stubfunc || bufOut_len < stubsz) RET_MINUS1_FAIL(stubfunc ? "repeatwhile size fail" :"repeatwhile addr fail") if (bufOut) { newblock2=compileCodeBlockWithRet(ctx,op->parms.parms[0],computTableSize,namespacePathToThis, RETURNVALUE_BOOL, NULL, fpStackUse, NULL); if (!newblock2) RET_MINUS1_FAIL("repeatwhile ccbwr fail") memcpy(pwr,stubfunc,stubsz); pwr=EEL_GLUE_set_immediate(pwr,(INT_PTR)newblock2); } return rv_offset+stubsz; } #else { #ifndef GLUE_WHILE_END_NOJUMP unsigned char *jzoutpt; #endif unsigned char *looppt; int parm_size=0,subsz; if (bufOut_len < parm_size + (int)(GLUE_WHILE_SETUP_SIZE + sizeof(GLUE_WHILE_BEGIN))) RET_MINUS1_FAIL("while size fail 1") if (bufOut) memcpy(bufOut + parm_size,GLUE_WHILE_SETUP,GLUE_WHILE_SETUP_SIZE); parm_size+=GLUE_WHILE_SETUP_SIZE; looppt = bufOut + parm_size; if (bufOut) memcpy(bufOut + parm_size,GLUE_WHILE_BEGIN,sizeof(GLUE_WHILE_BEGIN)); parm_size+=sizeof(GLUE_WHILE_BEGIN); subsz = compileOpcodes(ctx,op->parms.parms[0],bufOut ? (bufOut + parm_size) : NULL,bufOut_len - parm_size, computTableSize, namespacePathToThis, RETURNVALUE_BOOL, NULL,fpStackUse, NULL); if (subsz<0) RET_MINUS1_FAIL("while coc fail") if (bufOut_len < parm_size + (int)(sizeof(GLUE_WHILE_END) + sizeof(GLUE_WHILE_CHECK_RV))) RET_MINUS1_FAIL("which size fial 2") parm_size+=subsz; if (bufOut) memcpy(bufOut + parm_size, GLUE_WHILE_END, sizeof(GLUE_WHILE_END)); parm_size+=sizeof(GLUE_WHILE_END); #ifndef GLUE_WHILE_END_NOJUMP jzoutpt = bufOut + parm_size; #endif if (bufOut) memcpy(bufOut + parm_size, GLUE_WHILE_CHECK_RV, sizeof(GLUE_WHILE_CHECK_RV)); parm_size+=sizeof(GLUE_WHILE_CHECK_RV); if (bufOut) { GLUE_JMP_SET_OFFSET(bufOut + parm_size,(looppt - (bufOut+parm_size)) ); #ifndef GLUE_WHILE_END_NOJUMP GLUE_JMP_SET_OFFSET(jzoutpt, (bufOut + parm_size) - jzoutpt); #endif } return rv_offset+parm_size; } #endif } // special case: loop if (op->opcodeType == OPCODETYPE_FUNC2 && op->fntype == FN_LOOP) { int fUse1; int parm_size = compileOpcodes(ctx,op->parms.parms[0],bufOut,bufOut_len, computTableSize, namespacePathToThis, RETURNVALUE_FPSTACK, NULL,&fUse1, NULL); if (parm_size < 0) RET_MINUS1_FAIL("loop coc fail") *calledRvType = RETURNVALUE_BOOL; if (fUse1 > *fpStackUse) *fpStackUse=fUse1; #ifndef GLUE_INLINE_LOOPS // todo: PPC looping support when loop length is small enough { void *stub; int stubsize; unsigned char *newblock2, *p; stub = GLUE_realAddress(nseel_asm_repeat,nseel_asm_repeat_end,&stubsize); if (bufOut_len < parm_size + stubsize) RET_MINUS1_FAIL("loop size fail") if (bufOut) { newblock2 = compileCodeBlockWithRet(ctx,op->parms.parms[1],computTableSize,namespacePathToThis, RETURNVALUE_IGNORE, NULL,fpStackUse, NULL); p = bufOut + parm_size; memcpy(p, stub, stubsize); p=EEL_GLUE_set_immediate(p,(INT_PTR)newblock2); } return rv_offset + parm_size + stubsize; } #else { int subsz; int fUse2=0; unsigned char *skipptr1,*loopdest; if (bufOut_len < parm_size + (int)(sizeof(GLUE_LOOP_LOADCNT) + GLUE_LOOP_CLAMPCNT_SIZE + GLUE_LOOP_BEGIN_SIZE)) RET_MINUS1_FAIL("loop size fail") // store, convert to int, compare against 1, if less than, skip to end if (bufOut) memcpy(bufOut+parm_size,GLUE_LOOP_LOADCNT,sizeof(GLUE_LOOP_LOADCNT)); parm_size += sizeof(GLUE_LOOP_LOADCNT); skipptr1 = bufOut+parm_size; // compare aginst max loop length, jump to loop start if not above it if (bufOut) memcpy(bufOut+parm_size,GLUE_LOOP_CLAMPCNT,GLUE_LOOP_CLAMPCNT_SIZE); parm_size += GLUE_LOOP_CLAMPCNT_SIZE; // loop code: loopdest = bufOut + parm_size; if (bufOut) memcpy(bufOut+parm_size,GLUE_LOOP_BEGIN,GLUE_LOOP_BEGIN_SIZE); parm_size += GLUE_LOOP_BEGIN_SIZE; subsz = compileOpcodes(ctx,op->parms.parms[1],bufOut ? (bufOut + parm_size) : NULL,bufOut_len - parm_size, computTableSize, namespacePathToThis, RETURNVALUE_IGNORE, NULL, &fUse2, NULL); if (subsz<0) RET_MINUS1_FAIL("loop coc fail") if (fUse2 > *fpStackUse) *fpStackUse=fUse2; parm_size += subsz; if (bufOut_len < parm_size + (int)sizeof(GLUE_LOOP_END)) RET_MINUS1_FAIL("loop size fail 2") if (bufOut) memcpy(bufOut+parm_size,GLUE_LOOP_END,sizeof(GLUE_LOOP_END)); parm_size += sizeof(GLUE_LOOP_END); if (bufOut) { GLUE_JMP_SET_OFFSET(bufOut + parm_size,loopdest - (bufOut+parm_size)); GLUE_JMP_SET_OFFSET(skipptr1, (bufOut+parm_size) - skipptr1); } return rv_offset + parm_size; } #endif } } switch (op->opcodeType) { case OPCODETYPE_DIRECTVALUE: if (preferredReturnValues == RETURNVALUE_BOOL) { int w = fabs(op->parms.dv.directValue) >= NSEEL_CLOSEFACTOR; int wsz=(w?sizeof(GLUE_SET_P1_NZ):sizeof(GLUE_SET_P1_Z)); *calledRvType = RETURNVALUE_BOOL; if (bufOut_len < wsz) RET_MINUS1_FAIL("direct bool size fail3") if (bufOut) memcpy(bufOut,w?GLUE_SET_P1_NZ:GLUE_SET_P1_Z,wsz); return rv_offset+wsz; } else if (preferredReturnValues & RETURNVALUE_FPSTACK) { #ifdef GLUE_HAS_FLDZ if (op->parms.dv.directValue == 0.0) { *fpStackUse = 1; *calledRvType = RETURNVALUE_FPSTACK; if (bufOut_len < sizeof(GLUE_FLDZ)) RET_MINUS1_FAIL("direct fp fail 1") if (bufOut) memcpy(bufOut,GLUE_FLDZ,sizeof(GLUE_FLDZ)); return rv_offset+sizeof(GLUE_FLDZ); } #endif #ifdef GLUE_HAS_FLD1 if (op->parms.dv.directValue == 1.0) { *fpStackUse = 1; *calledRvType = RETURNVALUE_FPSTACK; if (bufOut_len < sizeof(GLUE_FLD1)) RET_MINUS1_FAIL("direct fp fail 1") if (bufOut) memcpy(bufOut,GLUE_FLD1,sizeof(GLUE_FLD1)); return rv_offset+sizeof(GLUE_FLD1); } #endif } // fall through case OPCODETYPE_DIRECTVALUE_TEMPSTRING: case OPCODETYPE_VALUE_FROM_NAMESPACENAME: case OPCODETYPE_VARPTR: case OPCODETYPE_VARPTRPTR: #ifdef GLUE_MOV_PX_DIRECTVALUE_TOSTACK_SIZE if (OPCODE_IS_TRIVIAL(op)) { if (preferredReturnValues & RETURNVALUE_FPSTACK) { *fpStackUse = 1; if (bufOut_len < GLUE_MOV_PX_DIRECTVALUE_TOSTACK_SIZE) RET_MINUS1_FAIL("direct fp fail 2") if (bufOut) { if (generateValueToReg(ctx,op,bufOut,-1,namespacePathToThis, 1 /*allow caching*/)<0) RET_MINUS1_FAIL("direct fp fail gvr") } *calledRvType = RETURNVALUE_FPSTACK; return rv_offset+GLUE_MOV_PX_DIRECTVALUE_TOSTACK_SIZE; } } #endif if (bufOut_len < GLUE_MOV_PX_DIRECTVALUE_SIZE) { RET_MINUS1_FAIL("direct value fail 1") } if (bufOut) { if (generateValueToReg(ctx,op,bufOut,0,namespacePathToThis, (preferredReturnValues&(RETURNVALUE_FPSTACK|RETURNVALUE_CACHEABLE))!=0)<0) { RET_MINUS1_FAIL("direct value gvr fail3") } } return rv_offset + GLUE_MOV_PX_DIRECTVALUE_SIZE; case OPCODETYPE_FUNCX: case OPCODETYPE_FUNC1: case OPCODETYPE_FUNC2: case OPCODETYPE_FUNC3: if (op->fntype == FUNCTYPE_EELFUNC) { int a; a = compileEelFunctionCall(ctx,op,bufOut,bufOut_len,computTableSize,namespacePathToThis, calledRvType,fpStackUse,canHaveDenormalOutput); if (a<0) return a; rv_offset += a; } else { int a; a = compileNativeFunctionCall(ctx,op,bufOut,bufOut_len,computTableSize,namespacePathToThis, calledRvType,fpStackUse,preferredReturnValues,canHaveDenormalOutput); if (a<0)return a; rv_offset += a; } return rv_offset; } RET_MINUS1_FAIL("default opcode fail") } #ifdef DUMP_OPS_DURING_COMPILE FILE *g_debugfp; int g_debugfp_indent; int g_debugfp_histsz=0; void dumpOp(compileContext *ctx, opcodeRec *op, int start) { if (start>=0) { if (g_debugfp) { static opcodeRec **hist; int x; int hit=0; if (!hist) hist = (opcodeRec**) calloc(1024,1024*sizeof(opcodeRec*)); for(x=0;x=100) *(char *)1=0; fprintf(g_debugfp,"%*s{ %p : %d%s: ",g_debugfp_indent," ",op,op->opcodeType, hit ? " -- DUPLICATE" : ""); switch (op->opcodeType) { case OPCODETYPE_DIRECTVALUE: fprintf(g_debugfp,"dv %f",op->parms.dv.directValue); break; case OPCODETYPE_VARPTR: if (op->relname && op->relname[0]) { fprintf(g_debugfp,"var %s",op->relname); } else { int wb; for (wb = 0; wb < ctx->varTable_numBlocks; wb ++) { char **plist=ctx->varTable_Names[wb]; if (!plist) break; if (op->parms.dv.valuePtr >= ctx->varTable_Values[wb] && op->parms.dv.valuePtr < ctx->varTable_Values[wb] + NSEEL_VARS_PER_BLOCK) { fprintf(g_debugfp,"var %s",plist[op->parms.dv.valuePtr - ctx->varTable_Values[wb]]); break; } } } break; case OPCODETYPE_FUNC1: case OPCODETYPE_FUNC2: case OPCODETYPE_FUNC3: case OPCODETYPE_FUNCX: if (op->fntype == FUNCTYPE_FUNCTIONTYPEREC) { functionType *p=(functionType*)op->fn; fprintf(g_debugfp,"func %d: %s",p->nParams&0xff,p->name); } else fprintf(g_debugfp,"sf %d",op->fntype); break; } fprintf(g_debugfp,"\n"); g_debugfp_indent+=2; } } else { if (g_debugfp) { g_debugfp_indent-=2; fprintf(g_debugfp,"%*s}%p\n",g_debugfp_indent," ",op); } } } #endif int compileOpcodes(compileContext *ctx, opcodeRec *op, unsigned char *bufOut, int bufOut_len, int *computTableSize, const namespaceInformation *namespacePathToThis, int supportedReturnValues, int *rvType, int *fpStackUse, int *canHaveDenormalOutput) { int code_returns=RETURNVALUE_NORMAL; int fpsu=0; int codesz; int denorm=0; #ifdef DUMP_OPS_DURING_COMPILE dumpOp(ctx,op,1); #endif codesz = compileOpcodesInternal(ctx,op,bufOut,bufOut_len,computTableSize,namespacePathToThis,&code_returns, supportedReturnValues,&fpsu,&denorm); if (denorm && canHaveDenormalOutput) *canHaveDenormalOutput=1; #ifdef DUMP_OPS_DURING_COMPILE dumpOp(ctx,op,-1); #endif #ifdef EEL_DUMP_OPS // dump opcode trees for verification, after optimizing if (g_eel_dump_fp2) { fprintf(g_eel_dump_fp2,"-- compileOpcodes generated %d bytes of code!\r\n",codesz); } #endif if (codesz < 0) return codesz; /* { char buf[512]; sprintf(buf,"opcode %d %d (%s): fpu use: %d\n",op->opcodeType,op->fntype, op->opcodeType >= OPCODETYPE_FUNC1 && op->fntype == FUNCTYPE_FUNCTIONTYPEREC ? ( ((functionType *)op->fn)->name ) : "", fpsu); OutputDebugString(buf); } */ if (fpStackUse) *fpStackUse=fpsu; if (bufOut) bufOut += codesz; bufOut_len -= codesz; if (code_returns == RETURNVALUE_BOOL && !(supportedReturnValues & RETURNVALUE_BOOL) && supportedReturnValues) { int stubsize; void *stub = GLUE_realAddress(nseel_asm_booltofp,nseel_asm_booltofp_end,&stubsize); if (!stub || bufOut_len < stubsize) RET_MINUS1_FAIL(stub?"booltofp size":"booltfp addr") if (bufOut) { memcpy(bufOut,stub,stubsize); bufOut += stubsize; } codesz+=stubsize; bufOut_len -= stubsize; code_returns = RETURNVALUE_FPSTACK; } // default processing of code_returns to meet return value requirements if (supportedReturnValues & code_returns) { if (rvType) *rvType = code_returns; return codesz; } if (rvType) *rvType = RETURNVALUE_IGNORE; if (code_returns == RETURNVALUE_NORMAL) { if (supportedReturnValues & (RETURNVALUE_FPSTACK|RETURNVALUE_BOOL)) { if (bufOut_len < GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE) RET_MINUS1_FAIL("pushvalatpxtofpstack,size") if (bufOut) { GLUE_PUSH_VAL_AT_PX_TO_FPSTACK(bufOut,0); // always fld qword [eax] but we might change that later bufOut += GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE; } codesz += GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE; bufOut_len -= GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE; if (supportedReturnValues & RETURNVALUE_BOOL) { code_returns = RETURNVALUE_FPSTACK; } else { if (rvType) *rvType = RETURNVALUE_FPSTACK; } } } if (code_returns == RETURNVALUE_FPSTACK) { if (supportedReturnValues & (RETURNVALUE_BOOL|RETURNVALUE_BOOL_REVERSED)) { int stubsize; void *stub; if (supportedReturnValues & RETURNVALUE_BOOL_REVERSED) { if (rvType) *rvType = RETURNVALUE_BOOL_REVERSED; stub = GLUE_realAddress(nseel_asm_fptobool_rev,nseel_asm_fptobool_rev_end,&stubsize); } else { if (rvType) *rvType = RETURNVALUE_BOOL; stub = GLUE_realAddress(nseel_asm_fptobool,nseel_asm_fptobool_end,&stubsize); } if (!stub || bufOut_len < stubsize) RET_MINUS1_FAIL(stub?"fptobool size":"fptobool addr") if (bufOut) { memcpy(bufOut,stub,stubsize); bufOut += stubsize; } codesz+=stubsize; bufOut_len -= stubsize; } else if (supportedReturnValues & RETURNVALUE_NORMAL) { if (computTableSize) (*computTableSize) ++; if (bufOut_len < GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE) RET_MINUS1_FAIL("popfpstacktowtptopxsize") // generate fp-pop to temp space if (bufOut) GLUE_POP_FPSTACK_TO_WTP_TO_PX(bufOut,0); codesz+=GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE; if (rvType) *rvType = RETURNVALUE_NORMAL; } else { // toss return value that will be ignored if (bufOut_len < GLUE_POP_FPSTACK_SIZE) RET_MINUS1_FAIL("popfpstack size") if (bufOut) memcpy(bufOut,GLUE_POP_FPSTACK,GLUE_POP_FPSTACK_SIZE); codesz+=GLUE_POP_FPSTACK_SIZE; } } return codesz; } #if 0 static void movestringover(char *str, int amount) { char tmp[1024+8]; int l=(int)strlen(str); l=wdl_min(1024-amount-1,l); memcpy(tmp,str,l+1); while (l >= 0 && tmp[l]!='\n') l--; l++; tmp[l]=0;//ensure we null terminate memcpy(str+amount,tmp,l+1); } #endif //------------------------------------------------------------------------------ NSEEL_CODEHANDLE NSEEL_code_compile(NSEEL_VMCTX _ctx, const char *_expression, int lineoffs) { return NSEEL_code_compile_ex(_ctx,_expression,lineoffs,0); } typedef struct topLevelCodeSegmentRec { struct topLevelCodeSegmentRec *_next; void *code; int codesz; int tmptable_use; } topLevelCodeSegmentRec; NSEEL_CODEHANDLE NSEEL_code_compile_ex(NSEEL_VMCTX _ctx, const char *_expression, int lineoffs, int compile_flags) { compileContext *ctx = (compileContext *)_ctx; const char *endptr; const char *_expression_end; codeHandleType *handle; topLevelCodeSegmentRec *startpts_tail=NULL; topLevelCodeSegmentRec *startpts=NULL; _codeHandleFunctionRec *oldCommonFunctionList; int curtabptr_sz=0; void *curtabptr=NULL; int had_err=0; if (!ctx) return 0; ctx->directValueCache=0; ctx->optimizeDisableFlags=0; ctx->gotEndOfInput=0; ctx->current_compile_flags = compile_flags; if (compile_flags & NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS_RESET) { ctx->functions_common=NULL; // reset common function list } else { // reset common compiled function code, forcing a recompile if shared _codeHandleFunctionRec *a = ctx->functions_common; while (a) { _codeHandleFunctionRec *b = a->derivedCopies; if (a->localstorage) { // force local storage actual values to be reallocated if used again memset(a->localstorage,0,sizeof(EEL_F *) * a->localstorage_size); } a->startptr = NULL; // force this copy to be recompiled a->startptr_size = -1; while (b) { b->startptr = NULL; // force derived copies to get recompiled b->startptr_size = -1; // no need to reset b->localstorage, since it points to a->localstorage b=b->derivedCopies; } a=a->next; } } ctx->last_error_string[0]=0; if (!_expression || !*_expression) return 0; _expression_end = _expression + strlen(_expression); oldCommonFunctionList = ctx->functions_common; ctx->isGeneratingCommonFunction=0; ctx->isSharedFunctions = !!(compile_flags & NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS); ctx->functions_local = NULL; freeBlocks(&ctx->tmpblocks_head); // free blocks freeBlocks(&ctx->blocks_head); // free blocks freeBlocks(&ctx->blocks_head_data); // free blocks memset(ctx->l_stats,0,sizeof(ctx->l_stats)); handle = (codeHandleType*)newDataBlock(sizeof(codeHandleType),8); if (!handle) { return 0; } memset(handle,0,sizeof(codeHandleType)); ctx->l_stats[0] += (int)(_expression_end - _expression); ctx->tmpCodeHandle = handle; endptr=_expression; while (*endptr) { int computTableTop = 0; int startptr_size=0; void *startptr=NULL; opcodeRec *start_opcode=NULL; const char *expr=endptr; int function_numparms=0; char is_fname[NSEEL_MAX_VARIABLE_NAMELEN+1]; is_fname[0]=0; memset(ctx->function_localTable_Size,0,sizeof(ctx->function_localTable_Size)); memset(ctx->function_localTable_Names,0,sizeof(ctx->function_localTable_Names)); ctx->function_localTable_ValuePtrs=0; ctx->function_usesNamespaces=0; ctx->function_curName=NULL; ctx->function_globalFlag=0; ctx->errVar=0; // single out top level segment { int had_something = 0, pcnt=0, pcnt2=0; int state=0; for (;;) { int l; const char *p=nseel_simple_tokenizer(&endptr,_expression_end,&l,&state); if (!p) { if (pcnt || pcnt2) ctx->gotEndOfInput|=4; break; } if (*p == ';') { if (had_something && !pcnt && !pcnt2) break; } else if (*p == '/' && l > 1 && (p[1] == '/' || p[1] == '*')) { if (l > 19 && !strnicmp(p,"//#eel-no-optimize:",19)) ctx->optimizeDisableFlags = atoi(p+19); } else { if (!had_something) { expr = p; had_something = 1; } if (*p == '(') pcnt++; else if (*p == ')') { if (--pcnt<0) pcnt=0; } else if (*p == '[') pcnt2++; else if (*p == ']') { if (--pcnt2<0) pcnt2=0; } } } if (!*expr || !had_something) break; } // parse { int tmplen,funcname_len; const char *p = expr; const char *tok1 = nseel_simple_tokenizer(&p,endptr,&tmplen,NULL); const char *funcname = nseel_simple_tokenizer(&p,endptr,&funcname_len,NULL); if (tok1 && funcname && tmplen == 8 && !strnicmp(tok1,"function",8) && (isalpha(funcname[0]) || funcname[0] == '_')) { int had_parms_locals=0; if (funcname_len > sizeof(is_fname)-1) funcname_len=sizeof(is_fname)-1; memcpy(is_fname, funcname, funcname_len); is_fname[funcname_len]=0; ctx->function_curName = is_fname; // only assigned for the duration of the loop, cleared later //-V507 while (NULL != (tok1 = nseel_simple_tokenizer(&p,endptr,&tmplen,NULL))) { int is_parms = 0, localTableContext = 0; int maxcnt=0; const char *sp_save; if (tok1[0] == '(') { if (had_parms_locals) { expr = p-1; // begin compilation at this code! break; } is_parms = 1; } else { if (tmplen == 5 && !strnicmp(tok1,"local",tmplen)) localTableContext=0; else if (tmplen == 6 && !strnicmp(tok1,"static",tmplen)) localTableContext=0; else if (tmplen == 8 && !strnicmp(tok1,"instance",tmplen)) localTableContext=1; else if ((tmplen == 7 && !strnicmp(tok1,"globals",tmplen)) || (tmplen == 6 && !strnicmp(tok1,"global",tmplen))) { ctx->function_globalFlag = 1; localTableContext=2; } else break; // unknown token! tok1 = nseel_simple_tokenizer(&p,endptr,&tmplen,NULL); if (!tok1 || tok1[0] != '(') break; } had_parms_locals = 1; sp_save=p; while (NULL != (tok1 = nseel_simple_tokenizer(&p,endptr,&tmplen,NULL))) { if (tok1[0] == ')') break; if (*tok1 == '#' && localTableContext!=1 && localTableContext!=2) { ctx->errVar = (int) (tok1 - _expression); lstrcpyn_safe(ctx->last_error_string,"#string can only be in instance() or globals()",sizeof(ctx->last_error_string)); goto had_error; } if (isalpha(*tok1) || *tok1 == '_' || *tok1 == '#') { maxcnt++; if (p < endptr && *p == '*') { if (!is_parms && localTableContext!=2) { ctx->errVar = (int) (p - _expression); lstrcpyn_safe(ctx->last_error_string,"namespace* can only be used in parameters or globals()",sizeof(ctx->last_error_string)); goto had_error; } p++; } } else if (*tok1 != ',') { ctx->errVar = (int)(tok1 - _expression); lstrcpyn_safe(ctx->last_error_string,"unknown character in function parameters",sizeof(ctx->last_error_string)); goto had_error; } } if (tok1 && maxcnt > 0) { char **ot = ctx->function_localTable_Names[localTableContext]; const int osz = ctx->function_localTable_Size[localTableContext]; maxcnt += osz; ctx->function_localTable_Names[localTableContext] = (char **)newTmpBlock(ctx,sizeof(char *) * maxcnt); if (ctx->function_localTable_Names[localTableContext]) { int i=osz; if (osz && ot) memcpy(ctx->function_localTable_Names[localTableContext],ot,sizeof(char *) * osz); p=sp_save; while (NULL != (tok1 = nseel_simple_tokenizer(&p,endptr,&tmplen,NULL))) { if (tok1[0] == ')') break; if (isalpha(*tok1) || *tok1 == '_' || *tok1 == '#') { char *newstr; int l = tmplen; if (*p == '*') // xyz* for namespace { p++; l++; } if (l > NSEEL_MAX_VARIABLE_NAMELEN) l = NSEEL_MAX_VARIABLE_NAMELEN; newstr = newTmpBlock(ctx,l+1); if (newstr) { memcpy(newstr,tok1,l); newstr[l]=0; ctx->function_localTable_Names[localTableContext][i++] = newstr; } } } ctx->function_localTable_Size[localTableContext]=i; if (is_parms) function_numparms = i; } } } } } if (ctx->function_localTable_Size[0]>0) { ctx->function_localTable_ValuePtrs = ctx->isSharedFunctions ? newDataBlock(ctx->function_localTable_Size[0] * sizeof(EEL_F *),8) : newTmpBlock(ctx,ctx->function_localTable_Size[0] * sizeof(EEL_F *)); if (!ctx->function_localTable_ValuePtrs) { ctx->function_localTable_Size[0]=0; function_numparms=0; } else { memset(ctx->function_localTable_ValuePtrs,0,sizeof(EEL_F *) * ctx->function_localTable_Size[0]); // force values to be allocated } } { int nseelparse(compileContext* context); void nseelrestart (void *input_file ,void *yyscanner ); ctx->rdbuf_start = _expression; #ifdef NSEEL_SUPER_MINIMAL_LEXER ctx->rdbuf = expr; ctx->rdbuf_end = endptr; if (!nseelparse(ctx) && !ctx->errVar) { start_opcode = ctx->result; } #else nseelrestart(NULL,ctx->scanner); ctx->rdbuf = expr; ctx->rdbuf_end = endptr; if (!nseelparse(ctx) && !ctx->errVar) { start_opcode = ctx->result; } if (ctx->errVar) { const char *p=expr; ctx->errVar += expr-_expression; } #endif ctx->rdbuf = NULL; } if (start_opcode) { int rvMode=0, fUse=0; #ifdef LOG_OPT char buf[512]; int sd=0; sprintf(buf,"pre opt sz=%d (tsackDepth=%d)\n",compileOpcodes(ctx,start_opcode,NULL,1024*1024*256,NULL, NULL,RETURNVALUE_IGNORE,NULL,&sd,NULL),sd); #ifdef _WIN32 OutputDebugString(buf); #else printf("%s\n",buf); #endif #endif #ifdef EEL_DUMP_OPS // dump opcode trees for verification, before optimizing if (g_eel_dump_fp) { fprintf(g_eel_dump_fp,"-- opcode chunk --\r\n"); dumpOpcodeTree(ctx,g_eel_dump_fp,start_opcode,2); } #endif if (!(ctx->optimizeDisableFlags&OPTFLAG_NO_OPTIMIZE)) optimizeOpcodes(ctx,start_opcode,is_fname[0] ? 1 : 0); #ifdef LOG_OPT sprintf(buf,"post opt sz=%d, stack depth=%d\n",compileOpcodes(ctx,start_opcode,NULL,1024*1024*256,NULL,NULL, RETURNVALUE_IGNORE,NULL,&sd,NULL),sd); #ifdef _WIN32 OutputDebugString(buf); #else printf("%s\n",buf); #endif #endif #ifdef EEL_DUMP_OPS // dump opcode trees for verification, after optimizing if (g_eel_dump_fp2) { fprintf(g_eel_dump_fp2,"-- POST-OPTIMIZED opcode chunk --\r\n"); dumpOpcodeTree(ctx,g_eel_dump_fp2,start_opcode,2); } #endif if (is_fname[0]) { _codeHandleFunctionRec *fr = ctx->isSharedFunctions ? newDataBlock(sizeof(_codeHandleFunctionRec),8) : newTmpBlock(ctx,sizeof(_codeHandleFunctionRec)); if (fr) { memset(fr,0,sizeof(_codeHandleFunctionRec)); fr->startptr_size = -1; fr->opcodes = start_opcode; if (ctx->function_localTable_Size[0] > 0 && ctx->function_localTable_ValuePtrs) { if (ctx->function_localTable_Names[0]) { int i; for(i=0;ifunction_localTable_Names[0][i]; if (nptr && *nptr && nptr[strlen(nptr)-1] == '*') { fr->parameterAsNamespaceMask |= ((unsigned int)1)<num_params=function_numparms; fr->localstorage = ctx->function_localTable_ValuePtrs; fr->localstorage_size = ctx->function_localTable_Size[0]; } fr->usesNamespaces = ctx->function_usesNamespaces; fr->isCommonFunction = ctx->isSharedFunctions; lstrcpyn_safe(fr->fname,is_fname,sizeof(fr->fname)); if (ctx->isSharedFunctions) { fr->next = ctx->functions_common; ctx->functions_common = fr; } else { fr->next = ctx->functions_local; ctx->functions_local = fr; } } continue; } #ifdef DUMP_OPS_DURING_COMPILE g_debugfp_indent=0; g_debugfp_histsz=0; g_debugfp = fopen("C:/temp/foo.txt","w"); #endif startptr_size = compileOpcodes(ctx,start_opcode,NULL,1024*1024*256,NULL, NULL, is_fname[0] ? (RETURNVALUE_NORMAL|RETURNVALUE_FPSTACK) : RETURNVALUE_IGNORE, &rvMode, &fUse, NULL); // if not a function, force return value as address (avoid having to pop it ourselves // if a function, allow the code to decide how return values are generated #ifdef DUMP_OPS_DURING_COMPILE if (g_debugfp) fclose(g_debugfp); g_debugfp=0; #endif if (!startptr_size) continue; // optimized away if (startptr_size>0) { startptr = newTmpBlock(ctx,startptr_size); if (startptr) { startptr_size=compileOpcodes(ctx,start_opcode,(unsigned char*)startptr,startptr_size,&computTableTop, NULL, RETURNVALUE_IGNORE, NULL,NULL, NULL); if (startptr_size<=0) startptr = NULL; } } } if (!startptr) { had_error: #ifdef NSEEL_EEL1_COMPAT_MODE continue; #else //if (!ctx->last_error_string[0]) { int byteoffs = ctx->errVar; int linenumber; char cur_err[sizeof(ctx->last_error_string)]; lstrcpyn_safe(cur_err,ctx->last_error_string,sizeof(cur_err)); if (cur_err[0]) lstrcatn(cur_err,": ",sizeof(cur_err)); else lstrcpyn_safe(cur_err,"syntax error: ",sizeof(cur_err)); if (_expression + byteoffs >= _expression_end) { if (ctx->gotEndOfInput&4) byteoffs = (int)(expr-_expression); else byteoffs=(int)(_expression_end-_expression); } if (byteoffs < 0) byteoffs=0; linenumber=findLineNumber(_expression,byteoffs)+1; if (ctx->gotEndOfInput&4) { snprintf(ctx->last_error_string,sizeof(ctx->last_error_string),"%d: %smissing ) or ]",linenumber+lineoffs,cur_err); } else { const char *p = _expression + byteoffs; int x=0, right_amt_nospace=0, left_amt_nospace=0; while (x < 32 && p-x > _expression && p[-x] != '\r' && p[-x] != '\n') { if (!isspace(p[-x])) left_amt_nospace=x; x++; } x=0; while (x < 60 && p[x] && p[x] != '\r' && p[x] != '\n') { if (!isspace(p[x])) right_amt_nospace=x; x++; } if (right_amt_nospace<1) right_amt_nospace=1; // display left_amt >>>> right_amt_nospace if (left_amt_nospace > 0) snprintf(ctx->last_error_string,sizeof(ctx->last_error_string),"%d: %s'%.*s %.*s'",linenumber+lineoffs,cur_err, left_amt_nospace,p-left_amt_nospace, right_amt_nospace,p); else snprintf(ctx->last_error_string,sizeof(ctx->last_error_string),"%d: %s'%.*s'",linenumber+lineoffs,cur_err,right_amt_nospace,p); } } startpts=NULL; startpts_tail=NULL; had_err=1; break; #endif } if (!is_fname[0]) // redundant check (if is_fname[0] is set and we succeeded, it should continue) // but we'll be on the safe side { topLevelCodeSegmentRec *p = newTmpBlock(ctx,sizeof(topLevelCodeSegmentRec)); p->_next=0; p->code = startptr; p->codesz = startptr_size; p->tmptable_use = computTableTop; if (!startpts_tail) startpts_tail=startpts=p; else { startpts_tail->_next=p; startpts_tail=p; } if (curtabptr_sz < computTableTop) { curtabptr_sz=computTableTop; } } } memset(ctx->function_localTable_Size,0,sizeof(ctx->function_localTable_Size)); memset(ctx->function_localTable_Names,0,sizeof(ctx->function_localTable_Names)); ctx->function_localTable_ValuePtrs=0; ctx->function_usesNamespaces=0; ctx->function_curName=NULL; ctx->function_globalFlag=0; ctx->tmpCodeHandle = NULL; if (handle->want_stack) { if (!handle->stack) startpts=NULL; } if (startpts) { curtabptr_sz += 2; // many functions use the worktable for temporary storage of up to 2 EEL_F's handle->workTable_size = curtabptr_sz; handle->workTable = curtabptr = newDataBlock((curtabptr_sz+MIN_COMPUTABLE_SIZE + COMPUTABLE_EXTRA_SPACE) * sizeof(EEL_F),32); #ifdef EEL_VALIDATE_WORKTABLE_USE if (curtabptr) memset(curtabptr,0x3a,(curtabptr_sz+MIN_COMPUTABLE_SIZE + COMPUTABLE_EXTRA_SPACE) * sizeof(EEL_F)); #endif if (!curtabptr) startpts=NULL; } if (startpts || (!had_err && (compile_flags & NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS))) { unsigned char *writeptr; topLevelCodeSegmentRec *p=startpts; int size=sizeof(GLUE_RET)+GLUE_FUNC_ENTER_SIZE+GLUE_FUNC_LEAVE_SIZE; // for ret at end :) int wtpos=0; // now we build one big code segment out of our list of them, inserting a mov esi, computable before each item as necessary while (p) { if (wtpos <= 0) { wtpos=MIN_COMPUTABLE_SIZE; size += GLUE_RESET_WTP(NULL,0); } size+=p->codesz; wtpos -= p->tmptable_use; p=p->_next; } handle->code = newCodeBlock(size,32); if (handle->code) { writeptr=(unsigned char *)handle->code; #if GLUE_FUNC_ENTER_SIZE > 0 memcpy(writeptr,&GLUE_FUNC_ENTER,GLUE_FUNC_ENTER_SIZE); writeptr += GLUE_FUNC_ENTER_SIZE; #endif p=startpts; wtpos=0; while (p) { if (wtpos <= 0) { wtpos=MIN_COMPUTABLE_SIZE; writeptr+=GLUE_RESET_WTP(writeptr,curtabptr); } memcpy(writeptr,(char*)p->code,p->codesz); writeptr += p->codesz; wtpos -= p->tmptable_use; p=p->_next; } #if GLUE_FUNC_LEAVE_SIZE > 0 memcpy(writeptr,&GLUE_FUNC_LEAVE,GLUE_FUNC_LEAVE_SIZE); writeptr += GLUE_FUNC_LEAVE_SIZE; #endif memcpy(writeptr,&GLUE_RET,sizeof(GLUE_RET)); writeptr += sizeof(GLUE_RET); ctx->l_stats[1]=size; handle->code_size = (int) (writeptr - (unsigned char *)handle->code); #if defined(__arm__) || defined(__aarch64__) __clear_cache(handle->code,writeptr); #endif } handle->blocks = ctx->blocks_head; handle->blocks_data = ctx->blocks_head_data; ctx->blocks_head=0; ctx->blocks_head_data=0; } else { // failed compiling, or failed calloc() handle=NULL; // return NULL (after resetting blocks_head) } ctx->directValueCache=0; ctx->functions_local = NULL; ctx->isGeneratingCommonFunction=0; ctx->isSharedFunctions=0; freeBlocks(&ctx->tmpblocks_head); // free blocks freeBlocks(&ctx->blocks_head); // free blocks of code (will be nonzero only on error) freeBlocks(&ctx->blocks_head_data); // free blocks of data (will be nonzero only on error) if (handle) { handle->compile_flags = compile_flags; handle->ramPtr = ctx->ram_state.blocks; memcpy(handle->code_stats,ctx->l_stats,sizeof(ctx->l_stats)); nseel_evallib_stats[0]+=ctx->l_stats[0]; nseel_evallib_stats[1]+=ctx->l_stats[1]; nseel_evallib_stats[2]+=ctx->l_stats[2]; nseel_evallib_stats[3]+=ctx->l_stats[3]; nseel_evallib_stats[4]++; } else { ctx->functions_common = oldCommonFunctionList; // failed compiling, remove any added common functions from the list // remove any derived copies of functions due to error, since we may have added some that have been freed while (oldCommonFunctionList) { oldCommonFunctionList->derivedCopies=NULL; oldCommonFunctionList=oldCommonFunctionList->next; } } memset(ctx->l_stats,0,sizeof(ctx->l_stats)); return (NSEEL_CODEHANDLE)handle; } //------------------------------------------------------------------------------ void NSEEL_code_execute(NSEEL_CODEHANDLE code) { #ifndef GLUE_TABPTR_IGNORED INT_PTR tabptr; #endif INT_PTR codeptr; codeHandleType *h = (codeHandleType *)code; if (!h || !h->code) return; codeptr = (INT_PTR) h->code; #if 0 { unsigned int *p=(unsigned int *)codeptr; while (*p != GLUE_RET[0]) { printf("instr:%04X:%04X\n",*p>>16,*p&0xffff); p++; } } #endif #ifndef GLUE_TABPTR_IGNORED tabptr=(INT_PTR)h->workTable; #endif //printf("calling code!\n"); GLUE_CALL_CODE(tabptr,codeptr,(INT_PTR)h->ramPtr); } int NSEEL_code_geterror_flag(NSEEL_VMCTX ctx) { compileContext *c=(compileContext *)ctx; if (c) return (c->gotEndOfInput ? 1 : 0); return 0; } char *NSEEL_code_getcodeerror(NSEEL_VMCTX ctx) { compileContext *c=(compileContext *)ctx; if (ctx && c->last_error_string[0]) return c->last_error_string; return 0; } //------------------------------------------------------------------------------ void NSEEL_code_free(NSEEL_CODEHANDLE code) { codeHandleType *h = (codeHandleType *)code; if (h != NULL) { #ifdef EEL_VALIDATE_WORKTABLE_USE if (h->workTable) { char *p = ((char*)h->workTable) + h->workTable_size*sizeof(EEL_F); int x; for(x=COMPUTABLE_EXTRA_SPACE*sizeof(EEL_F) - 1;x >= 0; x --) if (p[x] != 0x3a) { char buf[512]; snprintf(buf,sizeof(buf),"worktable overrun at byte %d (wts=%d), value = %f\n",x,h->workTable_size, *(EEL_F*)(p+(x&~(sizeof(EEL_F)-1)))); #ifdef _WIN32 OutputDebugString(buf); #else printf("%s",buf); #endif break; } } #endif nseel_evallib_stats[0]-=h->code_stats[0]; nseel_evallib_stats[1]-=h->code_stats[1]; nseel_evallib_stats[2]-=h->code_stats[2]; nseel_evallib_stats[3]-=h->code_stats[3]; nseel_evallib_stats[4]--; #if defined(__ppc__) && defined(__APPLE__) { FILE *fp = fopen("/var/db/receipts/com.apple.pkg.Rosetta.plist","r"); if (fp) { fclose(fp); // on PPC, but rosetta installed, do not free h->blocks, as rosetta won't detect changes to these pages } else { freeBlocks(&h->blocks); } } #else freeBlocks(&h->blocks); #endif freeBlocks(&h->blocks_data); } } //------------------------------------------------------------------------------ NSEEL_VMCTX NSEEL_VM_alloc() // return a handle { compileContext *ctx=calloc(1,sizeof(compileContext)); #ifdef NSEEL_SUPER_MINIMAL_LEXER if (ctx) ctx->scanner = ctx; #else if (ctx) { int nseellex_init(void ** ptr_yy_globals); void nseelset_extra(void *user_defined , void *yyscanner); if (nseellex_init(&ctx->scanner)) { free(ctx); return NULL; } nseelset_extra(ctx,ctx->scanner); } #endif if (ctx) { ctx->ram_state.maxblocks = NSEEL_RAM_BLOCKS_DEFAULTMAX; ctx->ram_state.closefact = NSEEL_CLOSEFACTOR; } return ctx; } int NSEEL_VM_setramsize(NSEEL_VMCTX _ctx, int maxent) { compileContext *ctx = (compileContext *)_ctx; if (!ctx) return 0; if (maxent > 0) { maxent = (maxent + NSEEL_RAM_ITEMSPERBLOCK - 1)/NSEEL_RAM_ITEMSPERBLOCK; if (maxent > NSEEL_RAM_BLOCKS) maxent = NSEEL_RAM_BLOCKS; ctx->ram_state.maxblocks = maxent; } return ctx->ram_state.maxblocks * NSEEL_RAM_ITEMSPERBLOCK; } void NSEEL_VM_SetFunctionValidator(NSEEL_VMCTX _ctx, const char * (*validateFunc)(const char *fn_name, void *user), void *user) { if (_ctx) { compileContext *ctx = (compileContext *)_ctx; ctx->func_check = validateFunc; ctx->func_check_user = user; } } void NSEEL_VM_SetFunctionTable(NSEEL_VMCTX _ctx, eel_function_table *tab) { if (_ctx) { compileContext *ctx = (compileContext *)_ctx; ctx->registered_func_tab = tab; } } void NSEEL_VM_free(NSEEL_VMCTX _ctx) // free when done with a VM and ALL of its code have been freed, as well { if (_ctx) { compileContext *ctx=(compileContext *)_ctx; EEL_GROWBUF_RESIZE(&ctx->varNameList,-1); NSEEL_VM_freeRAM(_ctx); freeBlocks(&ctx->pblocks); // these should be 0 normally but just in case freeBlocks(&ctx->tmpblocks_head); // free blocks freeBlocks(&ctx->blocks_head); // free blocks freeBlocks(&ctx->blocks_head_data); // free blocks #ifndef NSEEL_SUPER_MINIMAL_LEXER if (ctx->scanner) { int nseellex_destroy(void *yyscanner); nseellex_destroy(ctx->scanner); } #endif ctx->scanner=0; if (ctx->has_used_global_vars) { nseel_globalVarItem *p = NULL; NSEEL_HOSTSTUB_EnterMutex(); if (--nseel_vms_referencing_globallist_cnt == 0) { // clear and free globals p = nseel_globalreg_list; nseel_globalreg_list=0; } NSEEL_HOSTSTUB_LeaveMutex(); while (p) { nseel_globalVarItem *op = p; p=p->_next; free(op); } } free(ctx); } } int *NSEEL_code_getstats(NSEEL_CODEHANDLE code) { codeHandleType *h = (codeHandleType *)code; if (h) { return h->code_stats; } return 0; } void NSEEL_VM_SetStringFunc(NSEEL_VMCTX ctx, EEL_F (*onString)(void *caller_this, struct eelStringSegmentRec *list), EEL_F (*onNamedString)(void *caller_this, const char *name)) { if (ctx) { compileContext *c=(compileContext*)ctx; c->onString = onString; c->onNamedString = onNamedString; } } void NSEEL_VM_SetCustomFuncThis(NSEEL_VMCTX ctx, void *thisptr) { if (ctx) { compileContext *c=(compileContext*)ctx; c->caller_this=thisptr; } } void *NSEEL_PProc_RAM(void *data, int data_size, compileContext *ctx) { if (data_size>0) data=EEL_GLUE_set_immediate(data, (INT_PTR)ctx->ram_state.blocks); return data; } void *NSEEL_PProc_THIS(void *data, int data_size, compileContext *ctx) { if (data_size>0) data=EEL_GLUE_set_immediate(data, (INT_PTR)ctx->caller_this); return data; } static int vartable_lowerbound(compileContext *ctx, const char *name, int *ismatch) { int a = 0, c = EEL_GROWBUF_GET_SIZE(&ctx->varNameList); varNameRec **list = EEL_GROWBUF_GET(&ctx->varNameList); while (a != c) { const int b = (a+c)/2; const int cmp = strnicmp(name,list[b]->str,NSEEL_MAX_VARIABLE_NAMELEN); if (cmp > 0) a = b+1; else if (cmp < 0) c = b; else { *ismatch = 1; return b; } } *ismatch = 0; return a; } static void vartable_cull_list(compileContext *ctx, int refcnt_chk) { const int ni = EEL_GROWBUF_GET_SIZE(&ctx->varNameList); int i = ni, ndel = 0; varNameRec **rd = EEL_GROWBUF_GET(&ctx->varNameList), **wr=rd; while (i--) { varNameRec *v = rd[0]; if ((!refcnt_chk || !v->refcnt) && !v->isreg) { ndel++; } else { if (wr != rd) *wr = *rd; wr++; } rd++; } if (ndel) EEL_GROWBUF_RESIZE(&ctx->varNameList,ni - ndel); } void NSEEL_VM_remove_unused_vars(NSEEL_VMCTX _ctx) { compileContext *ctx = (compileContext *)_ctx; if (ctx) vartable_cull_list(ctx,1); } void NSEEL_VM_remove_all_nonreg_vars(NSEEL_VMCTX _ctx) { compileContext *ctx = (compileContext *)_ctx; if (ctx) vartable_cull_list(ctx,0); } void NSEEL_VM_clear_var_refcnts(NSEEL_VMCTX _ctx) { compileContext *ctx = (compileContext *)_ctx; if (ctx) { int i = EEL_GROWBUF_GET_SIZE(&ctx->varNameList); varNameRec **rd = EEL_GROWBUF_GET(&ctx->varNameList); while (i--) { rd[0]->refcnt=0; rd++; } } } #ifdef NSEEL_EEL1_COMPAT_MODE static EEL_F __nseel_global_regs[100]; double *NSEEL_getglobalregs() { return __nseel_global_regs; } #endif EEL_F *get_global_var(compileContext *ctx, const char *gv, int addIfNotPresent) { nseel_globalVarItem *p; #ifdef NSEEL_EEL1_COMPAT_MODE if (!strnicmp(gv,"reg",3) && gv[3]>='0' && gv[3] <= '9' && gv[4] >= '0' && gv[4] <= '9' && !gv[5]) { return __nseel_global_regs + atoi(gv+3); } #endif NSEEL_HOSTSTUB_EnterMutex(); if (!ctx->has_used_global_vars) { ctx->has_used_global_vars++; nseel_vms_referencing_globallist_cnt++; } p = nseel_globalreg_list; while (p) { if (!stricmp(p->name,gv)) break; p=p->_next; } if (!p && addIfNotPresent) { size_t gvl = strlen(gv); p = (nseel_globalVarItem*)malloc(sizeof(nseel_globalVarItem) + gvl); if (p) { p->data=0.0; strcpy(p->name,gv); p->_next = nseel_globalreg_list; nseel_globalreg_list=p; } } NSEEL_HOSTSTUB_LeaveMutex(); return p ? &p->data : NULL; } EEL_F *nseel_int_register_var(compileContext *ctx, const char *name, int isReg, const char **namePtrOut) { int slot, match; if (isReg == 0 && ctx->getVariable) { EEL_F *ret = ctx->getVariable(ctx->getVariable_userctx, name); if (ret) return ret; } if (!strnicmp(name,"_global.",8) && name[8]) { EEL_F *a=get_global_var(ctx,name+8,isReg >= 0); if (a) return a; } slot = vartable_lowerbound(ctx,name, &match); if (match) { varNameRec *v = EEL_GROWBUF_GET(&ctx->varNameList)[slot]; if (isReg >= 0) { v->refcnt++; if (isReg) v->isreg=isReg; if (namePtrOut) *namePtrOut = v->str; } return v->value; } if (isReg < 0) return NULL; if (ctx->varValueStore_left<1) { const int sz=500; ctx->varValueStore_left = sz; ctx->varValueStore = (EEL_F *)newCtxDataBlock((int)sizeof(EEL_F)*sz,8); } if (ctx->varValueStore) { int listsz = EEL_GROWBUF_GET_SIZE(&ctx->varNameList); size_t l = strlen(name); varNameRec *vh; if (l > NSEEL_MAX_VARIABLE_NAMELEN) l = NSEEL_MAX_VARIABLE_NAMELEN; vh = (varNameRec*) newCtxDataBlock( (int) (sizeof(varNameRec) + l),8); if (!vh || EEL_GROWBUF_RESIZE(&ctx->varNameList, (listsz+1))) return NULL; // alloc fail (vh->value = ctx->varValueStore++)[0]=0.0; ctx->varValueStore_left--; vh->refcnt=1; vh->isreg=isReg; memcpy(vh->str,name,l); vh->str[l] = 0; if (namePtrOut) *namePtrOut = vh->str; if (slot < listsz) { memmove(EEL_GROWBUF_GET(&ctx->varNameList) + slot+1, EEL_GROWBUF_GET(&ctx->varNameList) + slot, (listsz - slot) * sizeof(EEL_GROWBUF_GET(&ctx->varNameList)[0])); } EEL_GROWBUF_GET(&ctx->varNameList)[slot] = vh; return vh->value; } return NULL; } //------------------------------------------------------------------------------ void NSEEL_VM_enumallvars(NSEEL_VMCTX ctx, int (*func)(const char *name, EEL_F *val, void *ctx), void *userctx) { compileContext *tctx = (compileContext *) ctx; int ni; varNameRec **rd; if (!tctx) return; ni = EEL_GROWBUF_GET_SIZE(&tctx->varNameList); rd = EEL_GROWBUF_GET(&tctx->varNameList); while (ni--) { if (!func(rd[0]->str,rd[0]->value,userctx)) break; rd++; } } //------------------------------------------------------------------------------ EEL_F *NSEEL_VM_regvar(NSEEL_VMCTX _ctx, const char *var) { compileContext *ctx = (compileContext *)_ctx; if (!ctx) return 0; if (!strnicmp(var,"reg",3) && strlen(var) == 5 && isdigit(var[3]) && isdigit(var[4])) { EEL_F *a=get_global_var(ctx,var,1); if (a) return a; } return nseel_int_register_var(ctx,var,1,NULL); } EEL_F *NSEEL_VM_getvar(NSEEL_VMCTX _ctx, const char *var) { compileContext *ctx = (compileContext *)_ctx; if (!ctx) return 0; if (!strnicmp(var,"reg",3) && strlen(var) == 5 && isdigit(var[3]) && isdigit(var[4])) { EEL_F *a=get_global_var(ctx,var,0); if (a) return a; } return nseel_int_register_var(ctx,var,-1,NULL); } int NSEEL_VM_get_var_refcnt(NSEEL_VMCTX _ctx, const char *name) { compileContext *ctx = (compileContext *)_ctx; int slot,match; if (!ctx) return -1; slot = vartable_lowerbound(ctx,name, &match); return match ? EEL_GROWBUF_GET(&ctx->varNameList)[slot]->refcnt : -1; } opcodeRec *nseel_createFunctionByName(compileContext *ctx, const char *name, int np, opcodeRec *code1, opcodeRec *code2, opcodeRec *code3) { int chkamt=0; functionType *f=nseel_getFunctionByName(ctx,name,&chkamt); if (f) while (chkamt-->=0) { if ((f->nParams&FUNCTIONTYPE_PARAMETERCOUNTMASK) == np) { opcodeRec *o=newOpCode(ctx,NULL, np==3?OPCODETYPE_FUNC3:np==2?OPCODETYPE_FUNC2:OPCODETYPE_FUNC1); if (o) { o->fntype = FUNCTYPE_FUNCTIONTYPEREC; o->fn = f; o->parms.parms[0]=code1; o->parms.parms[1]=code2; o->parms.parms[2]=code3; } return o; } f++; if (stricmp(f->name,name)) break; } return NULL; } //------------------------------------------------------------------------------ opcodeRec *nseel_translate(compileContext *ctx, const char *tmp, size_t tmplen) // tmplen 0 = null term { // this depends on the string being nul terminated eventually, tmplen is used more as a hint than anything else if ((tmp[0] == '0' || tmp[0] == '$') && toupper(tmp[1])=='X') { char *p; return nseel_createCompiledValue(ctx,(EEL_F)strtoul(tmp+2,&p,16)); } else if (tmp[0] == '$') { if (tmp[1] == '~') { char *p=(char*)tmp+2; unsigned int v=strtoul(tmp+2,&p,10); if (v>53) v=53; return nseel_createCompiledValue(ctx,(EEL_F)((((WDL_INT64)1) << v) - 1)); } else if (!tmplen ? !stricmp(tmp,"$E") : (tmplen == 2 && !strnicmp(tmp,"$E",2))) return nseel_createCompiledValue(ctx,(EEL_F)2.71828183); else if (!tmplen ? !stricmp(tmp, "$PI") : (tmplen == 3 && !strnicmp(tmp, "$PI", 3))) return nseel_createCompiledValue(ctx,(EEL_F)3.141592653589793); else if (!tmplen ? !stricmp(tmp, "$PHI") : (tmplen == 4 && !strnicmp(tmp, "$PHI", 4))) return nseel_createCompiledValue(ctx,(EEL_F)1.61803399); else if ((!tmplen || tmplen == 4) && tmp[1] == '\'' && tmp[2] && tmp[3] == '\'') return nseel_createCompiledValue(ctx,(EEL_F)tmp[2]); else return NULL; } else if (tmp[0] == '\'') { char b[64]; int x,sz; unsigned int rv=0; if (!tmplen) // nul terminated tmplen, calculate a workable length { // faster than strlen(tmp) if tmp is large, we'll never need more than ~18 chars anyway while (tmplen < 32 && tmp[tmplen]) tmplen++; } sz = tmplen > 0 ? nseel_filter_escaped_string(b,sizeof(b),tmp+1, tmplen - 1, '\'') : 0; if (sz > 4) { if (ctx->last_error_string[0]) lstrcatn(ctx->last_error_string, ", ", sizeof(ctx->last_error_string)); snprintf_append(ctx->last_error_string,sizeof(ctx->last_error_string),"multi-byte character '%.5s...' too long",b); return NULL; // do not allow 'xyzxy', limit to 4 bytes } for (x=0;x sizeof(buf)-1) tmplen = sizeof(buf)-1; memcpy(buf,tmp,tmplen); buf[tmplen]=0; if (ctx->onNamedString) { if (tmplen>0 && buf[1]&&ctx->function_curName) { int err=0; opcodeRec *r = nseel_resolve_named_symbol(ctx,nseel_createCompiledValuePtr(ctx,NULL,buf),-1, &err); if (r) { if (r->opcodeType!=OPCODETYPE_VALUE_FROM_NAMESPACENAME) { r->opcodeType = OPCODETYPE_DIRECTVALUE; r->parms.dv.directValue = ctx->onNamedString(ctx->caller_this,buf+1); r->parms.dv.valuePtr=NULL; } return r; } if (err) return NULL; } // if not namespaced symbol, return directly if (!buf[1]) { opcodeRec *r=newOpCode(ctx,NULL,OPCODETYPE_DIRECTVALUE_TEMPSTRING); if (r) r->parms.dv.directValue = -10000.0; return r; } return nseel_createCompiledValue(ctx,ctx->onNamedString(ctx->caller_this,buf+1)); } } return nseel_createCompiledValue(ctx,(EEL_F)atof(tmp)); } void NSEEL_VM_set_var_resolver(NSEEL_VMCTX _ctx, EEL_F *(*res)(void *userctx, const char *name), void *userctx) { compileContext *ctx = (compileContext *)_ctx; if (ctx) { ctx->getVariable = res; ctx->getVariable_userctx = userctx; } } #if defined(__ppc__) || defined(EEL_TARGET_PORTABLE) // blank stubs void eel_setfp_round() { } void eel_setfp_trunc() { } void eel_enterfp(int s[2]) {} void eel_leavefp(int s[2]) {} #endif