diff options
author | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
---|---|---|
committer | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
commit | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/ns-eel2/nseel-compiler.c | |
parent | 537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff) | |
download | winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz |
Initial community commit
Diffstat (limited to 'Src/ns-eel2/nseel-compiler.c')
-rw-r--r-- | Src/ns-eel2/nseel-compiler.c | 5700 |
1 files changed, 5700 insertions, 0 deletions
diff --git a/Src/ns-eel2/nseel-compiler.c b/Src/ns-eel2/nseel-compiler.c new file mode 100644 index 00000000..69b08ae6 --- /dev/null +++ b/Src/ns-eel2/nseel-compiler.c @@ -0,0 +1,5700 @@ +/* + 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 <string.h> +#include <math.h> +#include <stdio.h> +#include <ctype.h> + +#if !defined(EEL_TARGET_PORTABLE) && !defined(_WIN32) +#include <sys/mman.h> +#include <stdint.h> +#include <unistd.h> +#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<namelist_sz;i++) + { + const char *nmchk=namelist[i]; + const size_t l = strlen(nmchk); + if (l > 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 ? parmcnt<match_parmcnt[0]?2:(match_parmcnt[0] < 2 ? 4:1) : 0; + return NULL; +} + +opcodeRec *nseel_setCompiledFunctionCallParameters(compileContext *ctx, opcodeRec *fn, opcodeRec *code1, opcodeRec *code2, opcodeRec *code3, opcodeRec *postCode, int *errOut) +{ + opcodeRec *r; + int np=0,x; + if (!fn || fn->opcodeType != 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;x<MAX_SUB_NAMESPACES && x < fn->num_params;x++) + { + if (fn->parameterAsNamespaceMask & (((unsigned int)1)<<x)) + { + if (wantCodeGenerated) + { + const char *rn=NULL; + char tmp[NSEEL_MAX_VARIABLE_NAMELEN+1]; + if (x < num_ordered_parmptrs && ordered_parmptrs[x]) + { + if (ordered_parmptrs[x]->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;x<n_params;x++) + { + opcodeRec *r = op->parms.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<cfp_statesize;pn++) + { + pstate[pn]=0; + cfp_ptrs[pn] = pstate + pn; + } + } + + + // first process parameters that are non-trivial + for (pn=0; pn < n_params; pn++) + { + int needDenorm=0; + int lsz,sUse=0; + + if (!parmptrs[pn] || OPCODE_IS_TRIVIAL(parmptrs[pn])) continue; // skip and process after + + if (last_nt_parm >= 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<g_debugfp_histsz;x++) + { + if (hist[x] == op) { hit=1; break; } + } + if (x ==g_debugfp_histsz && g_debugfp_histsz<1024*1024) hist[g_debugfp_histsz++] = op; + + if (!start) + { + g_debugfp_indent-=2; + fprintf(g_debugfp,"%*s}(join)\n",g_debugfp_indent," "); + } + if (g_debugfp_indent>=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;i<function_numparms;i++) + { + const char *nptr = ctx->function_localTable_Names[0][i]; + if (nptr && *nptr && nptr[strlen(nptr)-1] == '*') + { + fr->parameterAsNamespaceMask |= ((unsigned int)1)<<i; + } + } + } + fr->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<sz;x++) rv = (rv<<8) + ((unsigned char*)b)[x]; + return nseel_createCompiledValue(ctx,(EEL_F)rv); + } + else if (tmp[0] == '#') + { + char buf[2048]; + if (!tmplen) while (tmplen < sizeof(buf)-1 && tmp[tmplen]) tmplen++; + else if (tmplen > 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 |