aboutsummaryrefslogtreecommitdiff
path: root/Src/ns-eel2/nseel-compiler.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/ns-eel2/nseel-compiler.c')
-rw-r--r--Src/ns-eel2/nseel-compiler.c5700
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