+ JSFX/ReaScript/Video: fix potential incorrect memory read when parsing tan(x,y)
[wdl.git] / WDL / eel2 / nseel-compiler.c
blobd6894632194ef3ad4dc83d8688ff450c9cb1865f
1 /*
2 Expression Evaluator Library (NS-EEL) v2
3 Copyright (C) 2004-2013 Cockos Incorporated
4 Copyright (C) 1999-2003 Nullsoft, Inc.
6 nseel-compiler.c
8 This software is provided 'as-is', without any express or implied
9 warranty. In no event will the authors be held liable for any damages
10 arising from the use of this software.
12 Permission is granted to anyone to use this software for any purpose,
13 including commercial applications, and to alter it and redistribute it
14 freely, subject to the following restrictions:
16 1. The origin of this software must not be misrepresented; you must not
17 claim that you wrote the original software. If you use this software
18 in a product, an acknowledgment in the product documentation would be
19 appreciated but is not required.
20 2. Altered source versions must be plainly marked as such, and must not be
21 misrepresented as being the original software.
22 3. This notice may not be removed or altered from any source distribution.
25 #include "ns-eel-int.h"
27 #include "../denormal.h"
29 #include <string.h>
30 #include <math.h>
31 #include <stdio.h>
32 #include <ctype.h>
34 #include "../wdlcstring.h"
36 #if !defined(EEL_TARGET_PORTABLE) && !defined(_WIN32)
37 #include <sys/mman.h>
38 #include <stdint.h>
39 #include <unistd.h>
40 #endif
42 #ifdef __APPLE__
43 #include <libkern/OSCacheControl.h>
44 #endif
46 #define NSEEL_VARS_MALLOC_CHUNKSIZE 8
48 //#define LOG_OPT
49 //#define EEL_PRINT_FAILS
50 //#define EEL_VALIDATE_WORKTABLE_USE
53 #ifdef EEL_PRINT_FAILS
54 #define RET_MINUS1_FAIL(x) { wdl_log("%s\n",x); return -1; }
55 #else
56 #define RET_MINUS1_FAIL(x) return -1;
57 #endif
59 #ifdef EEL_DUMP_OPS
60 FILE *g_eel_dump_fp, *g_eel_dump_fp2;
61 #endif
63 #ifdef EEL_VALIDATE_WORKTABLE_USE
64 #define MIN_COMPUTABLE_SIZE 0
65 #define COMPUTABLE_EXTRA_SPACE 64 // safety buffer, if EEL_VALIDATE_WORKTABLE_USE set, used for magic-value-checking
66 #else
67 #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)
68 #define COMPUTABLE_EXTRA_SPACE 16 // safety buffer, if EEL_VALIDATE_WORKTABLE_USE set, used for magic-value-checking
69 #endif
73 P1 is rightmost parameter
74 P2 is second rightmost, if any
75 P3 is third rightmost, if any
76 registers on x86 are (RAX etc on x86-64)
77 P1(ret) EAX
78 P2 EDI
79 P3 ECX
80 WTP RSI
81 x86_64: r12 is a pointer to ram_state->blocks
82 x86_64: r13 is a pointer to closenessfactor
84 registers on PPC are:
85 P1(ret) r3
86 P2 r14
87 P3 r15
88 WTP r16 (r17 has the original value)
89 r13 is a pointer to ram_state->blocks
91 ppc uses f31 and f30 and others for certain constants
96 #ifdef EEL_TARGET_PORTABLE
98 #define EEL_DOESNT_NEED_EXEC_PERMS
100 #ifdef EEL_PORTABLE_TAILCALL
101 #include "glue_port_new.h"
102 #else
103 #include "glue_port.h"
104 #endif
106 #elif defined(__ppc__)
108 #include "glue_ppc.h"
110 #elif defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
112 #include "glue_aarch64.h"
114 #elif defined(__arm__) || (defined (_M_ARM) && _M_ARM == 7)
116 #include "glue_arm.h"
118 #elif defined(_WIN64) || defined(__LP64__)
120 #include "glue_x86_64_sse.h"
122 #else
124 #include "glue_x86.h"
126 #endif
128 #ifndef GLUE_INVSQRT_NEEDREPL
129 #define GLUE_INVSQRT_NEEDREPL 0
130 #endif
133 // used by //#eel-no-optimize:xxx, in ctx->optimizeDisableFlags
134 #define OPTFLAG_NO_OPTIMIZE 1
135 #define OPTFLAG_NO_FPSTACK 2
136 #define OPTFLAG_NO_INLINEFUNC 4
137 #define OPTFLAG_FULL_DENORMAL_CHECKS 8 // if set, denormals/NaN are always filtered on assign
138 #define OPTFLAG_NO_DENORMAL_CHECKS 16 // if set and FULL not set, denormals/NaN are never filtered on assign
141 #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...)
144 #define MAX_SUB_NAMESPACES 32
145 typedef struct
147 const char *namespacePathToThis;
148 const char *subParmInfo[MAX_SUB_NAMESPACES];
149 } namespaceInformation;
154 static int nseel_evallib_stats[5]; // source bytes, static code bytes, call code bytes, data bytes, segments
155 int *NSEEL_getstats()
157 return nseel_evallib_stats;
160 static int findLineNumber(const char *exp, int byteoffs)
162 int lc=0;
163 while (byteoffs-->0 && *exp) if (*exp++ =='\n') lc++;
164 return lc;
168 static int nseel_vms_referencing_globallist_cnt;
169 nseel_globalVarItem *nseel_globalreg_list;
170 static EEL_F *get_global_var(compileContext *ctx, const char *gv, int addIfNotPresent);
172 static void *__newBlock_align(llBlock **start,int size, int align, int code_page_size);
174 #define OPCODE_IS_TRIVIAL(x) ((x)->opcodeType <= OPCODETYPE_VARPTRPTR)
175 enum {
176 OPCODETYPE_DIRECTVALUE=0,
177 OPCODETYPE_DIRECTVALUE_TEMPSTRING, // like directvalue, but will generate a new tempstring value on generate
178 OPCODETYPE_VALUE_FROM_NAMESPACENAME, // this.* or namespace.* are encoded this way
179 OPCODETYPE_VARPTR,
180 OPCODETYPE_VARPTRPTR,
181 OPCODETYPE_FUNC1,
182 OPCODETYPE_FUNC2,
183 OPCODETYPE_FUNC3,
184 OPCODETYPE_FUNCX,
186 OPCODETYPE_MOREPARAMS,
188 OPCODETYPE_INVALID,
191 struct opcodeRec
193 int opcodeType;
194 int fntype;
195 void *fn;
197 union {
198 struct opcodeRec *parms[3];
199 struct {
200 double directValue;
201 EEL_F *valuePtr; // if direct value, valuePtr can be cached
202 } dv;
203 } parms;
205 int namespaceidx;
207 // OPCODETYPE_VALUE_FROM_NAMESPACENAME (relname is either empty or blah)
208 // OPCODETYPE_VARPTR if it represents a global variable, will be nonempty
209 // OPCODETYPE_FUNC* with fntype=FUNCTYPE_EELFUNC
210 const char *relname;
217 static opcodeRec *newOpCode(compileContext *ctx, const char *str, int opType)
219 const size_t strszfull = str ? strlen(str) : 0;
220 const size_t str_sz = wdl_min(NSEEL_MAX_VARIABLE_NAMELEN, strszfull);
222 opcodeRec *rec = (opcodeRec*)__newBlock_align(ctx->isSharedFunctions ? &ctx->blocks_head_data : &ctx->tmpblocks,
223 (int) (sizeof(opcodeRec) + (str_sz>0 ? str_sz+1 : 0)),
224 8, 0);
225 if (rec)
227 memset(rec,0,sizeof(*rec));
228 rec->opcodeType = opType;
230 if (str_sz > 0)
232 char *p = (char *)(rec+1);
233 memcpy(p,str,str_sz);
234 p[str_sz]=0;
236 rec->relname = p;
238 else
240 rec->relname = "";
244 return rec;
247 #ifndef EEL_DOESNT_NEED_EXEC_PERMS
248 static int eel_get_page_size(void)
250 static int pagesize;
251 if (!pagesize)
253 #ifndef _WIN32
254 const int ps = (int)sysconf(_SC_PAGESIZE);
255 pagesize = wdl_max(ps, 4096);
256 #else
257 SYSTEM_INFO inf = { 0 };
258 GetSystemInfo(&inf);
259 pagesize = wdl_max(inf.dwPageSize, 4096);
260 #endif
262 return pagesize;
264 #endif
266 #define newCodeBlock(x,a) __newBlock_align(&ctx->blocks_head_code,x,a, 1)
267 #define newDataBlock(x,a) __newBlock_align(&ctx->blocks_head_data,x,a,0)
268 #define newCtxDataBlock(x,a) __newBlock_align(&ctx->ctx_pblocks,x,a,0)
269 #define newTmpBlock(ctx, size) __newBlock_align(&(ctx)->tmpblocks, size, 8,0)
271 static char *eel_get_llblock_buffer(llBlock *llb) { return (char *) (llb+1); }
273 #ifndef EEL_DOESNT_NEED_EXEC_PERMS
274 static void eel_set_blocks_allow_execute(llBlock *llb, int exec)
276 while (llb)
278 const size_t sz = sizeof(*llb) + llb->sizealloc;
279 #ifdef _WIN32
280 DWORD ov;
281 VirtualProtect(llb,sz,exec ? (PAGE_EXECUTE_READ) : (PAGE_READWRITE),&ov);
282 FlushInstructionCache(GetCurrentProcess(),llb,sz);
283 #else
284 mprotect(llb,sz,exec ? (PROT_READ|PROT_EXEC) : (PROT_READ|PROT_WRITE));
285 #ifdef __APPLE__
286 if (exec) sys_icache_invalidate(llb,sz);
287 #endif
288 #endif
289 WDL_ASSERT((((INT_PTR)llb) & (eel_get_page_size()-1)) == 0);
290 WDL_ASSERT((sz & (eel_get_page_size()-1)) == 0);
291 llb = llb->next;
294 #endif
296 static void freeBlocks(llBlock **start, int is_code);
298 static int __growbuf_resize(eel_growbuf *buf, int newsize)
300 if (newsize<0)
302 free(buf->ptr);
303 buf->ptr=NULL;
304 buf->alloc=buf->size=0;
305 return 0;
308 if (newsize > buf->alloc)
310 const int newalloc = newsize + 4096 + newsize/2;
311 void *newptr = realloc(buf->ptr,newalloc);
312 if (!newptr)
314 newptr = malloc(newalloc);
315 if (!newptr) return 1;
316 if (buf->ptr && buf->size) memcpy(newptr,buf->ptr,buf->size);
317 free(buf->ptr);
318 buf->ptr=newptr;
320 else
321 buf->ptr = newptr;
323 buf->alloc=newalloc;
325 buf->size = newsize;
326 return 0;
330 #ifndef DECL_ASMFUNC
331 #define DECL_ASMFUNC(x) void nseel_asm_##x(void);
334 void _asm_megabuf(void);
335 void _asm_gmegabuf(void);
337 #endif
340 DECL_ASMFUNC(booltofp)
341 DECL_ASMFUNC(fptobool)
342 DECL_ASMFUNC(fptobool_rev)
343 DECL_ASMFUNC(sin)
344 DECL_ASMFUNC(cos)
345 DECL_ASMFUNC(tan)
346 DECL_ASMFUNC(1pdd)
347 DECL_ASMFUNC(2pdd)
348 DECL_ASMFUNC(2pdds)
349 DECL_ASMFUNC(1pp)
350 DECL_ASMFUNC(2pp)
351 DECL_ASMFUNC(sqr)
352 DECL_ASMFUNC(sqrt)
353 DECL_ASMFUNC(log)
354 DECL_ASMFUNC(log10)
355 DECL_ASMFUNC(abs)
356 DECL_ASMFUNC(min)
357 DECL_ASMFUNC(max)
358 DECL_ASMFUNC(min_fp)
359 DECL_ASMFUNC(max_fp)
360 DECL_ASMFUNC(sig)
361 DECL_ASMFUNC(sign)
362 DECL_ASMFUNC(band)
363 DECL_ASMFUNC(bor)
364 DECL_ASMFUNC(bnot)
365 DECL_ASMFUNC(if)
366 DECL_ASMFUNC(fcall)
367 DECL_ASMFUNC(repeat)
368 DECL_ASMFUNC(repeatwhile)
369 DECL_ASMFUNC(equal)
370 DECL_ASMFUNC(equal_exact)
371 DECL_ASMFUNC(notequal_exact)
372 DECL_ASMFUNC(notequal)
373 DECL_ASMFUNC(below)
374 DECL_ASMFUNC(above)
375 DECL_ASMFUNC(beloweq)
376 DECL_ASMFUNC(aboveeq)
377 DECL_ASMFUNC(assign)
378 DECL_ASMFUNC(assign_fromfp)
379 DECL_ASMFUNC(assign_fast)
380 DECL_ASMFUNC(assign_fast_fromfp)
381 DECL_ASMFUNC(add)
382 DECL_ASMFUNC(sub)
383 DECL_ASMFUNC(add_op)
384 DECL_ASMFUNC(sub_op)
385 DECL_ASMFUNC(add_op_fast)
386 DECL_ASMFUNC(sub_op_fast)
387 DECL_ASMFUNC(mul)
388 DECL_ASMFUNC(div)
389 DECL_ASMFUNC(mul_op)
390 DECL_ASMFUNC(div_op)
391 DECL_ASMFUNC(mul_op_fast)
392 DECL_ASMFUNC(div_op_fast)
393 DECL_ASMFUNC(mod)
394 DECL_ASMFUNC(shl)
395 DECL_ASMFUNC(shr)
396 DECL_ASMFUNC(mod_op)
397 DECL_ASMFUNC(or)
398 DECL_ASMFUNC(or0)
399 DECL_ASMFUNC(xor)
400 DECL_ASMFUNC(xor_op)
401 DECL_ASMFUNC(and)
402 DECL_ASMFUNC(or_op)
403 DECL_ASMFUNC(and_op)
404 DECL_ASMFUNC(uminus)
405 DECL_ASMFUNC(invsqrt)
406 DECL_ASMFUNC(dbg_getstackptr)
408 DECL_ASMFUNC(stack_push)
409 DECL_ASMFUNC(stack_pop)
410 DECL_ASMFUNC(stack_pop_fast) // just returns value, doesn't mod param
411 DECL_ASMFUNC(stack_peek)
412 DECL_ASMFUNC(stack_peek_int)
413 DECL_ASMFUNC(stack_peek_top)
414 DECL_ASMFUNC(stack_exch)
416 static void *NSEEL_PProc_GRAM(void *data, int data_size, compileContext *ctx)
418 if (data_size>0) data=EEL_GLUE_set_immediate(data, (INT_PTR)ctx->gram_blocks);
419 return data;
422 static void *NSEEL_PProc_Stack(void *data, int data_size, compileContext *ctx)
424 codeHandleType *ch=ctx->tmpCodeHandle;
426 if (data_size>0)
428 UINT_PTR m1=(UINT_PTR)(NSEEL_STACK_SIZE * sizeof(EEL_F) - 1);
429 UINT_PTR stackptr = ((UINT_PTR) (&ch->stack));
431 ch->want_stack=1;
432 if (!ch->stack) ch->stack = newDataBlock(NSEEL_STACK_SIZE*sizeof(EEL_F),NSEEL_STACK_SIZE*sizeof(EEL_F)); // stack functions need this alignment
434 data=EEL_GLUE_set_immediate(data, stackptr);
435 data=EEL_GLUE_set_immediate(data, m1); // and
436 data=EEL_GLUE_set_immediate(data, ((UINT_PTR)ch->stack&~m1)); //or
438 return data;
441 static void *NSEEL_PProc_Stack_PeekInt(void *data, int data_size, compileContext *ctx, INT_PTR offs)
443 codeHandleType *ch=ctx->tmpCodeHandle;
445 if (data_size>0)
447 UINT_PTR m1=(UINT_PTR)(NSEEL_STACK_SIZE * sizeof(EEL_F) - 1);
448 UINT_PTR stackptr = ((UINT_PTR) (&ch->stack));
450 ch->want_stack=1;
451 if (!ch->stack) ch->stack = newDataBlock(NSEEL_STACK_SIZE*sizeof(EEL_F),NSEEL_STACK_SIZE*sizeof(EEL_F)); // stack functions need this alignment
453 data=EEL_GLUE_set_immediate(data, stackptr);
454 data=EEL_GLUE_set_immediate(data, offs);
455 data=EEL_GLUE_set_immediate(data, m1); // and
456 data=EEL_GLUE_set_immediate(data, ((UINT_PTR)ch->stack&~m1)); //or
458 return data;
460 static void *NSEEL_PProc_Stack_PeekTop(void *data, int data_size, compileContext *ctx)
462 codeHandleType *ch=ctx->tmpCodeHandle;
464 if (data_size>0)
466 UINT_PTR stackptr = ((UINT_PTR) (&ch->stack));
468 ch->want_stack=1;
469 if (!ch->stack) ch->stack = newDataBlock(NSEEL_STACK_SIZE*sizeof(EEL_F),NSEEL_STACK_SIZE*sizeof(EEL_F)); // stack functions need this alignment
471 data=EEL_GLUE_set_immediate(data, stackptr);
473 return data;
476 #if defined(_MSC_VER) && _MSC_VER >= 1400
477 static double eel__floor(double a) { return floor(a); }
478 static double eel__ceil(double a) { return ceil(a); }
479 #define floor eel__floor
480 #define ceil eel__ceil
481 #endif
484 #ifdef NSEEL_EEL1_COMPAT_MODE
485 static double eel1band(double a, double b)
487 return (fabs(a)>NSEEL_CLOSEFACTOR && fabs(b) > NSEEL_CLOSEFACTOR) ? 1.0 : 0.0;
489 static double eel1bor(double a, double b)
491 return (fabs(a)>NSEEL_CLOSEFACTOR || fabs(b) > NSEEL_CLOSEFACTOR) ? 1.0 : 0.0;
494 static double eel1sigmoid(double x, double constraint)
496 double t = (1+exp(-x * (constraint)));
497 return fabs(t)>NSEEL_CLOSEFACTOR ? 1.0/t : 0;
500 #endif
504 #define FUNCTIONTYPE_PARAMETERCOUNTMASK 0xff
506 #define BIF_NPARAMS_MASK 0x7ffff00
507 #define BIF_RETURNSONSTACK 0x0000100
508 #define BIF_LASTPARMONSTACK 0x0000200
509 #define BIF_RETURNSBOOL 0x0000400
510 #define BIF_LASTPARM_ASBOOL 0x0000800
511 // 0x00?0000 -- taken by FP stack flags
512 #define BIF_TAKES_VARPARM 0x0400000
513 #define BIF_TAKES_VARPARM_EX 0x0C00000 // this is like varparm but check count exactly
514 #define BIF_WONTMAKEDENORMAL 0x0100000
515 #define BIF_CLEARDENORMAL 0x0200000
517 #if GLUE_HAS_FPREG2 > 0 && GLUE_MAX_FPSTACK_SIZE > 0
518 #error GLUE_HAS_FPREG2 and GLUE_MAX_FPSTACK_SIZE are exclusive
519 #endif
521 #if GLUE_MAX_SPILL_REGS > 0 && GLUE_HAS_FPREG2 <= 0
522 #error GLUE_MAX_SPILL_REGS requires GLUE_HAS_FPREG2
523 #endif
525 #if GLUE_MAX_FPSTACK_SIZE > 0 || GLUE_HAS_FPREG2 > 0
526 #define BIF_SECONDLASTPARMST 0x0001000 // use with BIF_LASTPARMONSTACK only (last two parameters get passed on fp stack)
527 #define BIF_LAZYPARMORDERING 0x0002000 // allow optimizer to avoid fxch when using BIF_TWOPARMSONFPSTACK_LAZY etc
528 #else
529 #define BIF_SECONDLASTPARMST 0
530 #define BIF_LAZYPARMORDERING 0
531 #endif
533 #if GLUE_MAX_FPSTACK_SIZE > 0
534 #define BIF_REVERSEFPORDER 0x0004000 // force a fxch (reverse order of last two parameters on fp stack, used by comparison functions)
535 #ifndef BIF_FPSTACKUSE
536 #define BIF_FPSTACKUSE(x) (((x)>=0&&(x)<8) ? ((7-(x))<<16):0)
537 #endif
538 #ifndef BIF_GETFPSTACKUSE
539 #define BIF_GETFPSTACKUSE(x) (7 - (((x)>>16)&7))
540 #endif
541 #else
542 #define BIF_REVERSEFPORDER 0
543 #define BIF_FPSTACKUSE(x) 0
544 #define BIF_GETFPSTACKUSE(x) 0
545 #endif
547 #define BIF_TWOPARMSONFPSTACK (BIF_SECONDLASTPARMST|BIF_LASTPARMONSTACK)
548 #define BIF_TWOPARMSONFPSTACK_LAZY (BIF_LAZYPARMORDERING|BIF_SECONDLASTPARMST|BIF_LASTPARMONSTACK)
551 #ifndef GLUE_HAS_NATIVE_TRIGSQRTLOG
552 static double sqrt_fabs(double a) { return sqrt(fabs(a)); }
553 #endif
556 EEL_F NSEEL_CGEN_CALL nseel_int_rand(EEL_F f);
558 #define FNPTR_HAS_CONDITIONAL_EXEC(op) \
559 (op->fntype == FN_LOGICAL_AND || \
560 op->fntype == FN_LOGICAL_OR || \
561 op->fntype == FN_IF_ELSE || \
562 op->fntype == FN_WHILE || \
563 op->fntype == FN_LOOP)
565 static functionType fnTable1[] = {
566 #ifndef GLUE_HAS_NATIVE_TRIGSQRTLOG
567 { "sin", nseel_asm_1pdd, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_WONTMAKEDENORMAL, {&sin} },
568 { "cos", nseel_asm_1pdd, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_CLEARDENORMAL, {&cos} },
569 { "tan", nseel_asm_1pdd, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK, {&tan} },
570 { "sqrt", nseel_asm_1pdd, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_WONTMAKEDENORMAL, {&sqrt_fabs}, },
571 { "log", nseel_asm_1pdd, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK, {&log} },
572 { "log10", nseel_asm_1pdd, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK, {&log10} },
573 #else
574 { "sin", nseel_asm_sin, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_WONTMAKEDENORMAL|BIF_FPSTACKUSE(1) },
575 { "cos", nseel_asm_cos, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_CLEARDENORMAL|BIF_FPSTACKUSE(1) },
576 { "tan", nseel_asm_tan, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(1) },
577 { "sqrt", nseel_asm_sqrt, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(1)|BIF_WONTMAKEDENORMAL },
578 { "log", nseel_asm_log, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(3), },
579 { "log10", nseel_asm_log10, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(3), },
580 #endif
583 { "asin", nseel_asm_1pdd, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK, {&asin}, },
584 { "acos", nseel_asm_1pdd, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK, {&acos}, },
585 { "atan", nseel_asm_1pdd, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK, {&atan}, },
586 { "atan2", nseel_asm_2pdd, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK, {&atan2}, },
587 { "exp", nseel_asm_1pdd, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK, {&exp}, },
588 { "abs", nseel_asm_abs, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(0)|BIF_WONTMAKEDENORMAL },
589 { "sqr", nseel_asm_sqr, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(1) },
590 { "min", nseel_asm_min, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_FPSTACKUSE(3)|BIF_WONTMAKEDENORMAL },
591 { "max", nseel_asm_max, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_FPSTACKUSE(3)|BIF_WONTMAKEDENORMAL },
592 { "sign", nseel_asm_sign, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL, },
593 { "rand", nseel_asm_1pdd, 1|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_CLEARDENORMAL, {&nseel_int_rand}, },
595 { "floor", nseel_asm_1pdd, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_CLEARDENORMAL, {&floor} },
596 { "ceil", nseel_asm_1pdd, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_CLEARDENORMAL, {&ceil} },
598 { "invsqrt", nseel_asm_invsqrt, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(3), {GLUE_INVSQRT_NEEDREPL} },
600 { "__dbg_getstackptr", nseel_asm_dbg_getstackptr, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(1), },
602 #ifdef NSEEL_EEL1_COMPAT_MODE
603 { "sigmoid", nseel_asm_2pdd, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK, {&eel1sigmoid}, },
605 // these differ from _and/_or, they always evaluate both...
606 { "band", nseel_asm_2pdd, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK|BIF_CLEARDENORMAL , {&eel1band}, },
607 { "bor", nseel_asm_2pdd, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK|BIF_CLEARDENORMAL , {&eel1bor}, },
609 {"exec2", NULL, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_WONTMAKEDENORMAL},
610 {"exec3", NULL, 3|NSEEL_NPARAMS_FLAG_CONST|BIF_WONTMAKEDENORMAL},
611 #endif // end EEL1 compat
614 {"freembuf",_asm_generic1parm,1,{&__NSEEL_RAM_MemFree},NSEEL_PProc_RAM},
615 {"memcpy",_asm_generic3parm, 3,{&__NSEEL_RAM_MemCpy},NSEEL_PProc_RAM},
616 {"memset",_asm_generic3parm, 3,{&__NSEEL_RAM_MemSet},NSEEL_PProc_RAM},
617 {"__memtop",_asm_generic1parm,1,{&__NSEEL_RAM_MemTop},NSEEL_PProc_RAM},
618 {"mem_set_values",_asm_generic2parm_retd,2|BIF_TAKES_VARPARM|BIF_RETURNSONSTACK,{&__NSEEL_RAM_Mem_SetValues},NSEEL_PProc_RAM},
619 {"mem_get_values",_asm_generic2parm_retd,2|BIF_TAKES_VARPARM|BIF_RETURNSONSTACK,{&__NSEEL_RAM_Mem_GetValues},NSEEL_PProc_RAM},
620 {"mem_multiply_sum",_asm_generic3parm_retd, 3|BIF_RETURNSONSTACK,{&__NSEEL_RAM_MemSumProducts},NSEEL_PProc_RAM},
621 {"mem_insert_shuffle",_asm_generic3parm_retd, 3|BIF_RETURNSONSTACK, {&__NSEEL_RAM_MemInsertShuffle},NSEEL_PProc_RAM},
623 {"stack_push",nseel_asm_stack_push,1|BIF_FPSTACKUSE(0),{0,},NSEEL_PProc_Stack},
624 {"stack_pop",nseel_asm_stack_pop, 1|BIF_FPSTACKUSE(1),{0,},NSEEL_PProc_Stack},
625 {"stack_peek",nseel_asm_stack_peek,1|NSEEL_NPARAMS_FLAG_CONST|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(0),{0,},NSEEL_PProc_Stack},
626 {"stack_exch",nseel_asm_stack_exch,1|BIF_FPSTACKUSE(1), {0,},NSEEL_PProc_Stack_PeekTop},
628 static functionType fn_min2 = { "min2", nseel_asm_min_fp, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK_LAZY|BIF_FPSTACKUSE(2)|BIF_WONTMAKEDENORMAL };
629 static functionType fn_max2 = { "max2", nseel_asm_max_fp, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK_LAZY|BIF_FPSTACKUSE(2)|BIF_WONTMAKEDENORMAL };
630 static functionType fn_or0 = { "or0", nseel_asm_or0, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_LASTPARMONSTACK|BIF_RETURNSONSTACK|BIF_CLEARDENORMAL };
632 static eel_function_table default_user_funcs;
634 static int functable_lowerbound(functionType *list, int list_sz, const char *name, int *ismatch)
636 int a = 0, c = list_sz;
637 while (a != c)
639 const int b = (a+c)/2;
640 const int cmp = stricmp(name,list[b].name);
641 if (cmp > 0) a = b+1;
642 else if (cmp < 0) c = b;
643 else
645 *ismatch = 1;
646 return b;
649 *ismatch = 0;
650 return a;
653 static int funcTypeCmp(const void *a, const void *b) { return stricmp(((functionType*)a)->name,((functionType*)b)->name); }
655 functionType *nseel_getFunctionByName(compileContext *ctx, const char *name, int *mchk)
657 eel_function_table *tab = ctx && ctx->registered_func_tab ? ctx->registered_func_tab : &default_user_funcs;
658 static char sorted;
659 const int fn1size = (int) (sizeof(fnTable1)/sizeof(fnTable1[0]));
660 int idx,match;
661 if (!sorted)
663 NSEEL_HOSTSTUB_EnterMutex();
664 if (!sorted) qsort(fnTable1,fn1size,sizeof(fnTable1[0]),funcTypeCmp);
665 sorted=1;
666 NSEEL_HOSTSTUB_LeaveMutex();
668 idx=functable_lowerbound(fnTable1,fn1size,name,&match);
669 if (match)
671 if (mchk) *mchk = 0;
672 return fnTable1+idx;
675 if ((!ctx || !(ctx->current_compile_flags&NSEEL_CODE_COMPILE_FLAG_ONLY_BUILTIN_FUNCTIONS)) && tab->list)
677 idx=functable_lowerbound(tab->list,tab->list_size,name,&match);
678 if (match)
680 if (mchk)
682 while (idx>0 && !stricmp(tab->list[idx-1].name,name)) idx--;
683 *mchk = tab->list_size - 1 - idx;
685 return tab->list + idx;
689 return NULL;
692 functionType *nseel_enumFunctions(compileContext *ctx, int idx)
694 eel_function_table *tab = ctx && ctx->registered_func_tab ? ctx->registered_func_tab : &default_user_funcs;
695 const int fn1size = (int) (sizeof(fnTable1)/sizeof(fnTable1[0]));
696 if (idx >= 0 && idx < fn1size) return fnTable1 + idx;
697 if ((!ctx || !(ctx->current_compile_flags&NSEEL_CODE_COMPILE_FLAG_ONLY_BUILTIN_FUNCTIONS)) && tab->list)
699 idx -= fn1size;
700 if (idx>=0 && idx < tab->list_size)
701 return tab->list + idx;
704 return NULL;
707 int NSEEL_init() // returns 0 on success
709 NSEEL_quit();
710 return 0;
713 void NSEEL_quit()
715 free(default_user_funcs.list);
716 default_user_funcs.list = NULL;
717 default_user_funcs.list_size = 0;
720 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)
722 NSEEL_addfunctionex2(name,min_np|(want_exact?BIF_TAKES_VARPARM_EX:BIF_TAKES_VARPARM),(char *)_asm_generic2parm_retd,0,pproc,fptr,NULL,destination);
725 void NSEEL_addfunc_varparm_ctxptr(const char *name, int min_np, int want_exact, void *ctxptr, EEL_F (NSEEL_CGEN_CALL *fptr)(void *, INT_PTR, EEL_F **), eel_function_table *destination)
727 NSEEL_addfunctionex2(name,min_np|(want_exact?BIF_TAKES_VARPARM_EX:BIF_TAKES_VARPARM),(char *)_asm_generic2parm_retd,0,NULL,ctxptr,fptr,destination);
730 void NSEEL_addfunc_varparm_ctxptr2(const char *name, int min_np, int want_exact, NSEEL_PPPROC pproc, void *ctx, EEL_F (NSEEL_CGEN_CALL *fptr)(void *, void *, INT_PTR, EEL_F **), eel_function_table *destination)
732 NSEEL_addfunctionex2(name,min_np|(want_exact?BIF_TAKES_VARPARM_EX:BIF_TAKES_VARPARM),(char *)_asm_generic2xparm_retd,0,pproc,ctx,fptr,destination);
735 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
737 char *stub=NULL;
738 int stubsz=0;
739 #define DOSTUB(np) { \
740 stub = (ret_type == 1 ? (char*)_asm_generic##np##parm_retd : (char*)_asm_generic##np##parm); \
743 WDL_ASSERT(np >= 1 && np <= 3); // use np=1 if you want "zero" parameters
745 if (np == 1) DOSTUB(1)
746 else if (np == 2) DOSTUB(2)
747 else if (np == 3) DOSTUB(3)
748 #undef DOSTUB
750 if (stub) NSEEL_addfunctionex2(name,np|(ret_type == -1 ? BIF_RETURNSBOOL:0), stub, stubsz, pproc,fptr,NULL,destination);
753 void NSEEL_addfunctionex2(const char *name, int nparms, char *code_startaddr, int code_len /* ignored*/,
754 NSEEL_PPPROC pproc, void *fptr, void *fptr2, eel_function_table *destination)
756 const int list_size_chunk = 128;
757 functionType *r;
758 if (!destination) destination = &default_user_funcs;
760 if (!destination->list || !(destination->list_size & (list_size_chunk-1)))
762 void *nv = realloc(destination->list, (destination->list_size + list_size_chunk)*sizeof(functionType));
763 if (!nv) return;
764 destination->list = (functionType *)nv;
766 if (destination->list)
768 int match,idx;
770 idx=functable_lowerbound(destination->list,destination->list_size,name,&match);
772 r = destination->list + idx;
773 if (idx < destination->list_size)
774 memmove(r + 1, r, (destination->list_size - idx) * sizeof(functionType));
775 destination->list_size++;
777 memset(r, 0, sizeof(functionType));
779 if (!(nparms & BIF_RETURNSBOOL))
781 if (code_startaddr == (void *)&_asm_generic1parm_retd ||
782 code_startaddr == (void *)&_asm_generic2parm_retd ||
783 code_startaddr == (void *)&_asm_generic2xparm_retd ||
784 code_startaddr == (void *)&_asm_generic3parm_retd)
786 nparms |= BIF_RETURNSONSTACK;
789 r->nParams = nparms;
790 r->name = name;
791 r->afunc = code_startaddr;
792 r->pProc = pproc;
793 r->replptrs[0] = fptr;
794 r->replptrs[1] = fptr2;
799 //---------------------------------------------------------------------------------------------------------------
800 static void freeBlocks(llBlock **start, int is_code)
802 llBlock *s=*start;
803 *start=0;
804 while (s)
806 llBlock *llB = s->next;
807 #ifndef EEL_DOESNT_NEED_EXEC_PERMS
808 if (is_code)
810 #ifdef _WIN32
811 VirtualFree(s, 0, MEM_RELEASE);
812 #else
813 munmap(s,sizeof(*s) + s->sizealloc);
814 #endif
816 else
817 #endif
819 free(s);
821 s=llB;
826 //---------------------------------------------------------------------------------------------------------------
827 static void *__newBlock_align(llBlock **start, int size, int align, int is_for_code)
829 llBlock *llb = *start;
830 int alloc_amt, align_pos, scan_cnt=8;
831 if (WDL_NOT_NORMALLY(align < 1)) align = 1;
833 while (llb && --scan_cnt > 0)
835 const int sizeused = llb->sizeused;
836 if (sizeused + size <= llb->sizealloc)
838 align_pos = (int) (((INT_PTR)eel_get_llblock_buffer(llb) + sizeused)&(align-1));
839 if (align_pos) align_pos = align - align_pos;
841 if (sizeused + size + align_pos <= llb->sizealloc)
843 llb->sizeused += size + align_pos;
844 return eel_get_llblock_buffer(llb) + sizeused + align_pos;
847 llb = llb->next;
850 #ifndef EEL_DOESNT_NEED_EXEC_PERMS
851 if (is_for_code)
853 const int code_page_size = eel_get_page_size();
854 alloc_amt = (sizeof(*llb) + size + code_page_size - 1) & ~(code_page_size-1);
855 #ifdef _WIN32
856 #ifdef _M_ARM64EC
858 MEM_EXTENDED_PARAMETER ext;
859 memset(&ext,0,sizeof(ext));
860 ext.Type = MemExtendedParameterAttributeFlags;
861 ext.ULong64 = MEM_EXTENDED_PARAMETER_EC_CODE;
862 llb = (llBlock *)VirtualAlloc2(NULL,NULL,alloc_amt,MEM_COMMIT,PAGE_EXECUTE_READ,&ext,1);
863 if (WDL_NORMALLY(llb))
865 DWORD ov;
866 VirtualProtect(llb, alloc_amt, PAGE_READWRITE, &ov);
869 #else
870 llb = (llBlock *)VirtualAlloc(NULL,alloc_amt,MEM_COMMIT,PAGE_READWRITE);
871 #endif
872 if (llb == NULL) return NULL;
873 #else
874 #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
875 llb = (llBlock *)mmap(NULL,alloc_amt, PROT_READ|PROT_WRITE,MAP_ANON|MAP_PRIVATE,-1,0);
876 #else
877 llb = (llBlock *)mmap(NULL,alloc_amt, PROT_READ|PROT_WRITE,MAP_ANONYMOUS|MAP_PRIVATE,-1,0);
878 #endif
879 if (llb == MAP_FAILED) return NULL;
880 #endif
881 alloc_amt -= sizeof(*llb);
882 align_pos = 0;
884 WDL_ASSERT(((INT_PTR)llb & (code_page_size - 1))==0);
885 WDL_ASSERT(((INT_PTR)(eel_get_llblock_buffer(llb) + alloc_amt) & (code_page_size - 1))==0);
887 else
888 #endif
890 // data block, allocate in larger chunks
891 alloc_amt = (size + align - 1 + 31)&~31;
892 if (alloc_amt < 65536-64) alloc_amt = 65536-64;
894 llb = (llBlock *)malloc(sizeof(*llb) + alloc_amt);
895 if (!llb) return NULL;
896 align_pos = (int) (((INT_PTR)eel_get_llblock_buffer(llb))&(align-1));
897 if (align_pos) align_pos = align - align_pos;
900 WDL_ASSERT(size+align_pos <= alloc_amt);
901 llb->sizeused = size + align_pos;
902 llb->sizealloc = alloc_amt;
903 llb->next = *start;
904 *start = llb;
905 return eel_get_llblock_buffer(llb) + align_pos;
909 //---------------------------------------------------------------------------------------------------------------
910 opcodeRec *nseel_createCompiledValue(compileContext *ctx, EEL_F value)
912 opcodeRec *r=newOpCode(ctx,NULL,OPCODETYPE_DIRECTVALUE);
913 if (r)
915 r->parms.dv.directValue = value;
917 return r;
920 opcodeRec *nseel_createCompiledValuePtr(compileContext *ctx, EEL_F *addrValue, const char *namestr)
922 opcodeRec *r=newOpCode(ctx,namestr,OPCODETYPE_VARPTR);
923 if (!r) return 0;
925 r->parms.dv.valuePtr=addrValue;
927 return r;
930 static int validate_varname_for_function(compileContext *ctx, const char *name)
932 if (!ctx->function_curName || !ctx->function_globalFlag) return 1;
934 if (ctx->function_localTable_Size[2] > 0 && ctx->function_localTable_Names[2])
936 char * const * const namelist = ctx->function_localTable_Names[2];
937 const int namelist_sz = ctx->function_localTable_Size[2];
938 int i;
939 const size_t name_len = strlen(name);
941 for (i=0;i<namelist_sz;i++)
943 const char *nmchk=namelist[i];
944 const size_t l = strlen(nmchk);
945 if (l > 1 && nmchk[l-1] == '*')
947 if (name_len >= l && !strnicmp(nmchk,name,l-1) && name[l-1]=='.') return 1;
949 else
951 if (name_len == l && !stricmp(nmchk,name)) return 1;
956 return 0;
959 opcodeRec *nseel_resolve_named_symbol(compileContext *ctx, opcodeRec *rec, int parmcnt, int *errOut)
961 const int isFunctionMode = parmcnt >= 0;
962 int rel_prefix_len=0;
963 int rel_prefix_idx=-2;
964 int i;
965 char match_parmcnt[4]={-1,-1,-1,-1}; // [3] is guess
966 unsigned char match_parmcnt_pos=0;
967 char *sname = (char *)rec->relname;
968 int is_string_prefix = parmcnt < 0 && sname[0] == '#';
969 const char *prevent_function_calls = NULL;
971 if (errOut) *errOut = 0;
973 if (sname) sname += is_string_prefix;
975 if (rec->opcodeType != OPCODETYPE_VARPTR || !sname || !sname[0]) return NULL;
977 if (!isFunctionMode && !is_string_prefix && !strnicmp(sname,"reg",3) && isdigit_safe(sname[3]) && isdigit_safe(sname[4]) && !sname[5])
979 EEL_F *a=get_global_var(ctx,sname,1);
980 if (a)
982 rec->parms.dv.valuePtr = a;
983 sname[0]=0; // for dump_ops compat really, but this shouldn't be needed anyway
985 return rec;
988 if (ctx->function_curName)
990 if (!strnicmp(sname,"this.",5))
992 rel_prefix_len=5;
993 rel_prefix_idx=-1;
995 else if (!stricmp(sname,"this"))
997 rel_prefix_len=4;
998 rel_prefix_idx=-1;
1001 // scan for parameters/local variables before user functions
1002 if (rel_prefix_idx < -1 &&
1003 ctx->function_localTable_Size[0] > 0 &&
1004 ctx->function_localTable_Names[0] &&
1005 ctx->function_localTable_ValuePtrs)
1007 char * const * const namelist = ctx->function_localTable_Names[0];
1008 const int namelist_sz = ctx->function_localTable_Size[0];
1009 for (i=0; i < namelist_sz; i++)
1011 const char *p = namelist[i];
1012 if (p)
1014 if (!isFunctionMode && !is_string_prefix && !strnicmp(p,sname,NSEEL_MAX_VARIABLE_NAMELEN))
1016 rec->opcodeType = OPCODETYPE_VARPTRPTR;
1017 rec->parms.dv.valuePtr=(EEL_F *)(ctx->function_localTable_ValuePtrs+i);
1018 rec->parms.dv.directValue=0.0;
1019 return rec;
1021 else
1023 const size_t plen = strlen(p);
1024 if (plen > 1 && p[plen-1] == '*' && !strnicmp(p,sname,plen-1) && ((sname[plen-1] == '.'&&sname[plen]) || !sname[plen-1]))
1026 rel_prefix_len=(int) (sname[plen-1] ? plen : plen-1);
1027 rel_prefix_idx=i;
1028 break;
1034 // if instance name set, translate sname or sname.* into "this.sname.*"
1035 if (rel_prefix_idx < -1 &&
1036 ctx->function_localTable_Size[1] > 0 &&
1037 ctx->function_localTable_Names[1])
1039 char * const * const namelist = ctx->function_localTable_Names[1];
1040 const int namelist_sz = ctx->function_localTable_Size[1];
1041 const char *full_sname = rec->relname; // include # in checks
1042 for (i=0; i < namelist_sz; i++)
1044 const char *p = namelist[i];
1045 if (p && *p)
1047 const size_t tl = strlen(p);
1048 if (!strnicmp(p,full_sname,tl) && (full_sname[tl] == 0 || full_sname[tl] == '.'))
1050 rel_prefix_len=0; // treat as though this. prefixes is present
1051 rel_prefix_idx=-1;
1052 break;
1057 if (rel_prefix_idx >= -1)
1059 ctx->function_usesNamespaces=1;
1061 } // ctx->function_curName
1063 if (!isFunctionMode)
1065 // instance variables
1066 if (rel_prefix_idx >= -1)
1068 rec->opcodeType = OPCODETYPE_VALUE_FROM_NAMESPACENAME;
1069 rec->namespaceidx = rel_prefix_idx;
1070 if (rel_prefix_len > 0)
1072 if (is_string_prefix) sname[-1] = '#';
1073 memmove(sname, sname+rel_prefix_len, strlen(sname + rel_prefix_len) + 1);
1076 else
1078 // no namespace index, so it must be a global
1079 if (!validate_varname_for_function(ctx,rec->relname))
1081 if (errOut) *errOut = 1;
1082 if (ctx->last_error_string[0]) lstrcatn(ctx->last_error_string, ", ", sizeof(ctx->last_error_string));
1083 snprintf_append(ctx->last_error_string,sizeof(ctx->last_error_string),"global '%s' inaccessible",rec->relname);
1084 return NULL;
1088 return rec;
1091 if (ctx->func_check)
1092 prevent_function_calls = ctx->func_check(sname,ctx->func_check_user);
1094 ////////// function mode
1095 // first off, while() and loop() are special and can't be overridden
1097 if (parmcnt == 1 && !stricmp("while",sname) && !prevent_function_calls)
1099 rec->opcodeType = OPCODETYPE_FUNC1;
1100 rec->fntype = FN_WHILE;
1101 return rec;
1103 if (parmcnt == 2 && !stricmp("loop",sname) && !prevent_function_calls)
1105 rec->opcodeType = OPCODETYPE_FUNC2;
1106 rec->fntype = FN_LOOP;
1107 return rec;
1111 // resolve user function names before builtin functions -- this allows the user to override default functions
1112 if (!(ctx->current_compile_flags & NSEEL_CODE_COMPILE_FLAG_ONLY_BUILTIN_FUNCTIONS))
1114 _codeHandleFunctionRec *best=NULL;
1115 size_t bestlen=0;
1116 const char * const ourcall = sname+rel_prefix_len;
1117 const size_t ourcall_len = strlen(ourcall);
1118 int pass;
1119 for (pass=0;pass<2;pass++)
1121 _codeHandleFunctionRec *fr = pass ? ctx->functions_common : ctx->functions_local;
1122 // sname is [namespace.[ns.]]function, find best match of function that matches the right end
1123 while (fr)
1125 int this_np = fr->num_params;
1126 const char *thisfunc = fr->fname;
1127 const size_t thisfunc_len = strlen(thisfunc);
1128 if (this_np < 1) this_np=1;
1129 if (thisfunc_len == ourcall_len && !stricmp(thisfunc,ourcall))
1131 if (this_np == parmcnt)
1133 bestlen = thisfunc_len;
1134 best = fr;
1135 break; // found exact match, finished
1137 else
1139 if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = fr->num_params;
1143 if (thisfunc_len > bestlen && thisfunc_len < ourcall_len && ourcall[ourcall_len - thisfunc_len - 1] == '.' && !stricmp(thisfunc,ourcall + ourcall_len - thisfunc_len))
1145 if (this_np == parmcnt)
1147 bestlen = thisfunc_len;
1148 best = fr;
1150 else
1151 if (match_parmcnt[3]<0) match_parmcnt[3]=fr->num_params;
1153 fr=fr->next;
1155 if (fr) break; // found exact match, finished
1158 if (best)
1160 switch (parmcnt)
1162 case 0:
1163 case 1: rec->opcodeType = OPCODETYPE_FUNC1; break;
1164 case 2: rec->opcodeType = OPCODETYPE_FUNC2; break;
1165 case 3: rec->opcodeType = OPCODETYPE_FUNC3; break;
1166 default: rec->opcodeType = OPCODETYPE_FUNCX; break;
1168 if (ourcall != rec->relname) memmove((char *)rec->relname, ourcall, strlen(ourcall)+1);
1170 if (ctx->function_curName && rel_prefix_idx<0)
1172 // if no namespace specified, and this.commonprefix.func() called, remove common prefixes and set prefixidx to be this
1173 const char *p=ctx->function_curName;
1174 if (*p) p++;
1175 while (*p && *p != '.') p++;
1176 if (*p && p[1]) // we have a dot!
1178 while (p[1]) p++; // go to last char of string, which doesn't allow possible trailing dot to be checked
1180 while (--p > ctx->function_curName) // do not check possible leading dot
1182 if (*p == '.')
1184 const size_t cmplen = p+1-ctx->function_curName;
1185 if (!strnicmp(rec->relname,ctx->function_curName,cmplen) && rec->relname[cmplen])
1187 const char *src=rec->relname + cmplen;
1188 memmove((char *)rec->relname, src, strlen(src)+1);
1189 rel_prefix_idx=-1;
1190 ctx->function_usesNamespaces=1;
1191 break;
1198 if (ctx->function_curName && rel_prefix_idx < -1 &&
1199 strchr(rec->relname,'.') && !validate_varname_for_function(ctx,rec->relname))
1201 if (errOut) *errOut = 1;
1202 if (ctx->last_error_string[0]) lstrcatn(ctx->last_error_string, ", ", sizeof(ctx->last_error_string));
1203 snprintf_append(ctx->last_error_string,sizeof(ctx->last_error_string),"namespaced function '%s' inaccessible",rec->relname);
1204 return NULL;
1207 rec->namespaceidx = rel_prefix_idx;
1208 rec->fntype = FUNCTYPE_EELFUNC;
1209 rec->fn = best;
1210 return rec;
1214 if (prevent_function_calls)
1216 if (ctx->last_error_string[0]) lstrcatn(ctx->last_error_string, ", ", sizeof(ctx->last_error_string));
1217 snprintf_append(ctx->last_error_string,sizeof(ctx->last_error_string),"'%.30s': %s",sname, prevent_function_calls);
1218 if (errOut) *errOut = 0;
1219 return NULL;
1222 #ifdef NSEEL_EEL1_COMPAT_MODE
1223 if (!stricmp(sname,"assign"))
1225 if (parmcnt == 2)
1227 rec->opcodeType = OPCODETYPE_FUNC2;
1228 rec->fntype = FN_ASSIGN;
1229 return rec;
1231 if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 2;
1233 else if (!stricmp(sname,"if"))
1235 if (parmcnt == 3)
1237 rec->opcodeType = OPCODETYPE_FUNC3;
1238 rec->fntype = FN_IF_ELSE;
1239 return rec;
1241 if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 3;
1243 else if (!stricmp(sname,"equal"))
1245 if (parmcnt == 2)
1247 rec->opcodeType = OPCODETYPE_FUNC2;
1248 rec->fntype = FN_EQ;
1249 return rec;
1251 if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 2;
1253 else if (!stricmp(sname,"below"))
1255 if (parmcnt == 2)
1257 rec->opcodeType = OPCODETYPE_FUNC2;
1258 rec->fntype = FN_LT;
1259 return rec;
1261 if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 2;
1263 else if (!stricmp(sname,"above"))
1265 if (parmcnt == 2)
1267 rec->opcodeType = OPCODETYPE_FUNC2;
1268 rec->fntype = FN_GT;
1269 return rec;
1271 if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 2;
1273 else if (!stricmp(sname,"bnot"))
1275 if (parmcnt == 1)
1277 rec->opcodeType = OPCODETYPE_FUNC1;
1278 rec->fntype = FN_NOT;
1279 return rec;
1281 if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 1;
1283 else if (!stricmp(sname,"megabuf"))
1285 if (parmcnt == 1)
1287 rec->opcodeType = OPCODETYPE_FUNC1;
1288 rec->fntype = FN_MEMORY;
1289 return rec;
1291 if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 1;
1293 else if (!stricmp(sname,"gmegabuf"))
1295 if (parmcnt == 1)
1297 rec->opcodeType = OPCODETYPE_FUNC1;
1298 rec->fntype = FN_GMEMORY;
1299 return rec;
1301 if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 1;
1303 else
1304 #endif
1305 // convert legacy pow() to FN_POW
1306 if (!stricmp("pow",sname))
1308 if (parmcnt == 2)
1310 rec->opcodeType = OPCODETYPE_FUNC2;
1311 rec->fntype = FN_POW;
1312 return rec;
1314 if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 2;
1316 else if (!stricmp("__denormal_likely",sname) || !stricmp("__denormal_unlikely",sname))
1318 if (parmcnt == 1)
1320 rec->opcodeType = OPCODETYPE_FUNC1;
1321 rec->fntype = !stricmp("__denormal_likely",sname) ? FN_DENORMAL_LIKELY : FN_DENORMAL_UNLIKELY;
1322 return rec;
1327 int chkamt=0;
1328 functionType *f=nseel_getFunctionByName(ctx,sname,&chkamt);
1329 if (f) while (chkamt-->=0)
1331 const int pc_needed=(f->nParams&FUNCTIONTYPE_PARAMETERCOUNTMASK);
1332 if ((f->nParams&BIF_TAKES_VARPARM_EX)==BIF_TAKES_VARPARM ? (parmcnt >= pc_needed) : (parmcnt == pc_needed))
1334 rec->fntype = FUNCTYPE_FUNCTIONTYPEREC;
1335 rec->fn = (void *)f;
1336 switch (parmcnt)
1338 case 0:
1339 case 1: rec->opcodeType = OPCODETYPE_FUNC1; break;
1340 case 2: rec->opcodeType = OPCODETYPE_FUNC2; break;
1341 case 3: rec->opcodeType = OPCODETYPE_FUNC3; break;
1342 default: rec->opcodeType = OPCODETYPE_FUNCX; break;
1344 return rec;
1346 if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = (f->nParams&FUNCTIONTYPE_PARAMETERCOUNTMASK);
1347 f++;
1348 if (chkamt < 0 || stricmp(f->name,sname)) break;
1351 if (ctx->last_error_string[0]) lstrcatn(ctx->last_error_string, ", ", sizeof(ctx->last_error_string));
1352 if (match_parmcnt[3] >= 0)
1354 if (match_parmcnt_pos<3) match_parmcnt[match_parmcnt_pos] = match_parmcnt[3];
1355 match_parmcnt_pos++;
1358 if (!match_parmcnt_pos)
1359 snprintf_append(ctx->last_error_string,sizeof(ctx->last_error_string),"'%.30s' undefined",sname);
1360 else
1362 int x;
1363 snprintf_append(ctx->last_error_string,sizeof(ctx->last_error_string),"'%.30s' needs ",sname);
1364 for (x = 0; x < match_parmcnt_pos; x++)
1365 snprintf_append(ctx->last_error_string,sizeof(ctx->last_error_string),"%s%d",x==0?"" : x == match_parmcnt_pos-1?" or ":",",match_parmcnt[x]);
1366 lstrcatn(ctx->last_error_string," parms",sizeof(ctx->last_error_string));
1368 if (errOut) *errOut = match_parmcnt_pos > 0 ? parmcnt<match_parmcnt[0]?2:(match_parmcnt[0] < 2 ? 4:1) : 0;
1369 return NULL;
1372 opcodeRec *nseel_setCompiledFunctionCallParameters(compileContext *ctx, opcodeRec *fn, opcodeRec *code1, opcodeRec *code2, opcodeRec *code3, opcodeRec *postCode, int *errOut)
1374 opcodeRec *r;
1375 int np=0,x;
1376 if (!fn || fn->opcodeType != OPCODETYPE_VARPTR || !fn->relname || !fn->relname[0])
1378 return NULL;
1380 fn->parms.parms[0] = code1;
1381 fn->parms.parms[1] = code2;
1382 fn->parms.parms[2] = code3;
1384 for (x=0;x<3;x++)
1386 opcodeRec *prni=fn->parms.parms[x];
1387 while (prni && np < NSEEL_MAX_EELFUNC_PARAMETERS)
1389 const int isMP = prni->opcodeType == OPCODETYPE_MOREPARAMS;
1390 np++;
1391 if (!isMP) break;
1392 prni = prni->parms.parms[1];
1395 r = nseel_resolve_named_symbol(ctx, fn, np<1 ? 1 : np ,errOut);
1396 if (postCode && r)
1398 if (code1 && r->opcodeType == OPCODETYPE_FUNC1 && r->fntype == FN_WHILE)
1400 // change while(x) (postcode) to be
1401 // while ((x) ? (postcode;1) : 0);
1403 r->parms.parms[0] =
1404 nseel_createIfElse(ctx,r->parms.parms[0],
1405 nseel_createSimpleCompiledFunction(ctx,FN_JOIN_STATEMENTS,2,postCode,nseel_createCompiledValue(ctx,1.0f)),
1406 NULL); // NULL defaults to 0.0
1409 else
1411 snprintf_append(ctx->last_error_string,sizeof(ctx->last_error_string),"syntax error following function");
1412 *errOut = -1;
1413 return NULL;
1416 return r;
1420 struct eelStringSegmentRec *nseel_createStringSegmentRec(compileContext *ctx, const char *str, int len)
1422 struct eelStringSegmentRec *r = newTmpBlock(ctx,sizeof(struct eelStringSegmentRec));
1423 if (r)
1425 r->_next=0;
1426 r->str_start=str;
1427 r->str_len = len;
1429 return r;
1432 opcodeRec *nseel_eelMakeOpcodeFromStringSegments(compileContext *ctx, struct eelStringSegmentRec *rec)
1434 if (ctx && ctx->onString)
1436 return nseel_createCompiledValue(ctx, ctx->onString(ctx->caller_this,rec));
1439 return NULL;
1442 opcodeRec *nseel_createMoreParametersOpcode(compileContext *ctx, opcodeRec *code1, opcodeRec *code2)
1444 opcodeRec *r=code1 && code2 ? newOpCode(ctx,NULL,OPCODETYPE_MOREPARAMS) : NULL;
1445 if (r)
1447 r->parms.parms[0] = code1;
1448 r->parms.parms[1] = code2;
1450 return r;
1454 opcodeRec *nseel_createIfElse(compileContext *ctx, opcodeRec *code1, opcodeRec *code2, opcodeRec *code3)
1456 opcodeRec *r=code1 ? newOpCode(ctx,NULL,OPCODETYPE_FUNC3) : NULL;
1457 if (r)
1459 if (!code2) code2 = nseel_createCompiledValue(ctx,0.0);
1460 if (!code3) code3 = nseel_createCompiledValue(ctx,0.0);
1461 if (!code2||!code3) return NULL;
1463 r->fntype = FN_IF_ELSE;
1464 r->parms.parms[0] = code1;
1465 r->parms.parms[1] = code2;
1466 r->parms.parms[2] = code3;
1468 return r;
1472 opcodeRec *nseel_createMemoryAccess(compileContext *ctx, opcodeRec *code1, opcodeRec *code2)
1474 if (code1 && code1->opcodeType == OPCODETYPE_VARPTR && !stricmp(code1->relname,"gmem"))
1476 return nseel_createSimpleCompiledFunction(ctx, FN_GMEMORY,1,code2?code2:nseel_createCompiledValue(ctx,0.0),0);
1478 if (code2 && (code2->opcodeType != OPCODETYPE_DIRECTVALUE || code2->parms.dv.directValue != 0.0))
1480 code1 = nseel_createSimpleCompiledFunction(ctx,FN_ADD,2,code1,code2);
1482 return nseel_createSimpleCompiledFunction(ctx, FN_MEMORY,1,code1,0);
1485 opcodeRec *nseel_createSimpleCompiledFunction(compileContext *ctx, int fn, int np, opcodeRec *code1, opcodeRec *code2)
1487 opcodeRec *r=code1 && (np<2 || code2) ? newOpCode(ctx,NULL,np>=2 ? OPCODETYPE_FUNC2:OPCODETYPE_FUNC1) : NULL;
1488 if (r)
1490 r->fntype = fn;
1491 r->parms.parms[0] = code1;
1492 r->parms.parms[1] = code2;
1493 if (fn == FN_JOIN_STATEMENTS)
1495 r->fn = r; // for joins, fn is temporarily used for tail pointers
1496 if (code1 && code1->opcodeType == OPCODETYPE_FUNC2 && code1->fntype == fn)
1498 opcodeRec *t = (opcodeRec *)code1->fn;
1499 // keep joins in the form of dosomething->morestuff.
1500 // in this instance, code1 is previous stuff to do, code2 is new stuff to do
1501 r->parms.parms[0] = t->parms.parms[1];
1503 code1->fn = (t->parms.parms[1] = r);
1504 return code1;
1508 return r;
1512 // these are bitmasks; on request you can tell what is supported, and compileOpcodes will return one of them
1513 #define RETURNVALUE_IGNORE 0 // ignore return value
1514 #define RETURNVALUE_NORMAL 1 // pointer
1515 #define RETURNVALUE_FPSTACK 2
1516 #define RETURNVALUE_BOOL 4 // P1 is nonzero if true
1517 #define RETURNVALUE_BOOL_REVERSED 8 // P1 is zero if true
1518 #define RETURNVALUE_CACHEABLE 16 // only to be used when (at least) RETURNVALUE_NORMAL is set
1519 #if GLUE_HAS_FPREG2 > 0
1520 #define RETURNVALUE_FPREG2 32 // only usable for compileOpcodes() on a trivial opcode
1521 #endif
1525 static int compileOpcodes(compileContext *ctx, opcodeRec *op, unsigned char *bufOut, int bufOut_len, int *computTable, const namespaceInformation *namespacePathToThis,
1526 int supportedReturnValues, int *rvType, int *fpStackUsage, int *canHaveDenormalOutput);
1529 static unsigned char *compileCodeBlockWithRet(compileContext *ctx, opcodeRec *rec, int *computTableSize, const namespaceInformation *namespacePathToThis,
1530 int supportedReturnValues, int *rvType, int *fpStackUse, int *canHaveDenormalOutput);
1532 _codeHandleFunctionRec *eel_createFunctionNamespacedInstance(compileContext *ctx, _codeHandleFunctionRec *fr, const char *nameptr)
1534 size_t n;
1535 _codeHandleFunctionRec *subfr =
1536 fr->isCommonFunction ?
1537 ctx->isSharedFunctions ? newDataBlock(sizeof(_codeHandleFunctionRec),8) :
1538 newCtxDataBlock(sizeof(_codeHandleFunctionRec),8) : // if common function, but derived version is in non-common context, set ownership to VM rather than us
1539 newTmpBlock(ctx,sizeof(_codeHandleFunctionRec));
1541 if (!subfr) return 0;
1542 // fr points to functionname()'s rec, nameptr to blah.functionname()
1544 *subfr = *fr;
1545 n = strlen(nameptr);
1546 if (n > sizeof(subfr->fname)-1) n=sizeof(subfr->fname)-1;
1547 memcpy(subfr->fname,nameptr,n);
1548 subfr->fname[n]=0;
1550 subfr->next = NULL;
1551 subfr->startptr=0; // make sure this code gets recompiled (with correct member ptrs) for this instance!
1552 subfr->startptr_size=-1;
1554 // subfr->derivedCopies already points to the right place
1555 fr->derivedCopies = subfr;
1557 return subfr;
1560 static void combineNamespaceFields(char *nm, const namespaceInformation *namespaceInfo, const char *relname, int thisctx) // nm must be NSEEL_MAX_VARIABLE_NAMELEN+1 bytes
1562 const char *prefix = namespaceInfo ?
1563 thisctx<0 ? (thisctx == -1 ? namespaceInfo->namespacePathToThis : NULL) : (thisctx < MAX_SUB_NAMESPACES ? namespaceInfo->subParmInfo[thisctx] : NULL)
1564 : NULL;
1565 int lfp = 0, lrn=relname ? (int)strlen(relname) : 0;
1566 if (prefix) while (prefix[lfp] && prefix[lfp] != ':' && lfp < NSEEL_MAX_VARIABLE_NAMELEN) lfp++;
1567 if (!relname) relname = "";
1569 while (*relname == '.') // if relname begins with ., then remove a chunk of context from prefix
1571 relname++;
1572 while (lfp>0 && prefix[lfp-1] != '.') lfp--;
1573 if (lfp>0) lfp--;
1576 if (lfp > NSEEL_MAX_VARIABLE_NAMELEN-3) lfp=NSEEL_MAX_VARIABLE_NAMELEN-3;
1577 if (lfp>0) memcpy(nm,prefix,lfp);
1579 if (lrn > NSEEL_MAX_VARIABLE_NAMELEN - lfp - (lfp>0)) lrn=NSEEL_MAX_VARIABLE_NAMELEN - lfp - (lfp>0);
1580 if (lrn > 0)
1582 if (lfp>0) nm[lfp++] = '.';
1583 memcpy(nm+lfp,relname,lrn);
1584 lfp+=lrn;
1586 nm[lfp++]=0;
1590 //---------------------------------------------------------------------------------------------------------------
1591 static void *nseel_getBuiltinFunctionAddress(compileContext *ctx,
1592 int fntype, void *fn,
1593 NSEEL_PPPROC *pProc, void ***replList,
1594 int *abiInfo, int preferredReturnValues, const EEL_F *hasConstParm1, const EEL_F *hasConstParm2)
1596 const EEL_F *firstConstParm = hasConstParm1 ? hasConstParm1 : hasConstParm2;
1597 static void *pow_replptrs[4]={&pow,};
1599 switch (fntype)
1601 #define RF(x) return (void*)nseel_asm_##x
1603 case FN_MUL_OP:
1604 *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL;
1605 RF(mul_op);
1606 case FN_DIV_OP:
1607 *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL;
1608 RF(div_op);
1609 case FN_OR_OP:
1610 *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL;
1611 RF(or_op);
1612 case FN_XOR_OP:
1613 *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL;
1614 RF(xor_op);
1615 case FN_AND_OP:
1616 *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL;
1617 RF(and_op);
1618 case FN_MOD_OP:
1619 *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL;
1620 RF(mod_op);
1621 case FN_ADD_OP:
1622 *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL;
1623 RF(add_op);
1624 case FN_SUB_OP:
1625 *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL;
1626 RF(sub_op);
1627 case FN_POW_OP:
1628 *abiInfo=BIF_LASTPARMONSTACK|BIF_CLEARDENORMAL;
1629 *replList = pow_replptrs;
1630 RF(2pdds);
1631 case FN_POW:
1632 *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
1633 *replList = pow_replptrs;
1634 RF(2pdd);
1635 case FN_ADD:
1636 *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK_LAZY|BIF_FPSTACKUSE(2);
1637 // for x +- non-denormal-constant, we can set BIF_CLEARDENORMAL
1638 if (firstConstParm && fabs(*firstConstParm) > DENORMAL_CLEARING_THRESHOLD) *abiInfo |= BIF_CLEARDENORMAL;
1639 RF(add);
1640 case FN_SUB:
1641 *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK|BIF_FPSTACKUSE(2);
1642 // for x +- non-denormal-constant, we can set BIF_CLEARDENORMAL
1643 if (firstConstParm && fabs(*firstConstParm) > DENORMAL_CLEARING_THRESHOLD) *abiInfo |= BIF_CLEARDENORMAL;
1644 RF(sub);
1645 case FN_MULTIPLY:
1646 *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK_LAZY|BIF_FPSTACKUSE(2);
1647 // for x*constant-greater-than-eq-1, we can set BIF_WONTMAKEDENORMAL
1648 if (firstConstParm && fabs(*firstConstParm) >= 1.0) *abiInfo |= BIF_WONTMAKEDENORMAL;
1649 RF(mul);
1650 case FN_DIVIDE:
1651 *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK|BIF_FPSTACKUSE(2);
1652 // for x/constant-less-than-eq-1, we can set BIF_WONTMAKEDENORMAL
1653 if (firstConstParm && fabs(*firstConstParm) <= 1.0) *abiInfo |= BIF_WONTMAKEDENORMAL;
1654 RF(div);
1655 case FN_MOD:
1656 *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK|BIF_FPSTACKUSE(1)|BIF_CLEARDENORMAL;
1657 RF(mod);
1658 case FN_ASSIGN:
1659 *abiInfo = BIF_FPSTACKUSE(1)|BIF_CLEARDENORMAL;
1660 RF(assign);
1661 case FN_AND: *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK_LAZY|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; RF(and);
1662 case FN_OR: *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK_LAZY|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; RF(or);
1663 case FN_XOR:
1664 *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK_LAZY|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL;
1665 RF(xor);
1666 case FN_SHR:
1667 *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL;
1668 RF(shr);
1669 case FN_SHL:
1670 *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL;
1671 RF(shl);
1672 case FN_NOTNOT: *abiInfo = BIF_LASTPARM_ASBOOL|BIF_RETURNSBOOL|BIF_FPSTACKUSE(1); break;
1673 case FN_UMINUS: *abiInfo = BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_WONTMAKEDENORMAL; RF(uminus);
1674 case FN_NOT: *abiInfo = BIF_LASTPARM_ASBOOL|BIF_RETURNSBOOL|BIF_FPSTACKUSE(1); RF(bnot);
1676 case FN_EQ:
1677 *abiInfo = BIF_TWOPARMSONFPSTACK_LAZY|BIF_RETURNSBOOL|BIF_FPSTACKUSE(2);
1678 RF(equal);
1679 case FN_EQ_EXACT:
1680 *abiInfo=BIF_TWOPARMSONFPSTACK_LAZY|BIF_RETURNSBOOL|BIF_FPSTACKUSE(2);
1681 RF(equal_exact);
1682 case FN_NE:
1683 *abiInfo=BIF_TWOPARMSONFPSTACK_LAZY|BIF_RETURNSBOOL|BIF_FPSTACKUSE(2);
1684 RF(notequal);
1685 case FN_NE_EXACT:
1686 *abiInfo=BIF_TWOPARMSONFPSTACK_LAZY|BIF_RETURNSBOOL|BIF_FPSTACKUSE(2);
1687 RF(notequal_exact);
1688 case FN_LOGICAL_AND:
1689 *abiInfo = BIF_RETURNSBOOL;
1690 RF(band);
1691 case FN_LOGICAL_OR:
1692 *abiInfo = BIF_RETURNSBOOL;
1693 RF(bor);
1695 case FN_GT:
1696 *abiInfo = BIF_TWOPARMSONFPSTACK|BIF_RETURNSBOOL|BIF_FPSTACKUSE(2);
1697 RF(above);
1698 case FN_GTE:
1699 *abiInfo = BIF_TWOPARMSONFPSTACK|BIF_RETURNSBOOL|BIF_REVERSEFPORDER|BIF_FPSTACKUSE(2);
1700 #if BIF_REVERSEFPORDER > 0
1701 RF(beloweq);
1702 #else
1703 RF(aboveeq);
1704 #endif
1705 case FN_LT:
1706 *abiInfo = BIF_TWOPARMSONFPSTACK|BIF_RETURNSBOOL|BIF_REVERSEFPORDER|BIF_FPSTACKUSE(2);
1707 #if BIF_REVERSEFPORDER > 0
1708 RF(above);
1709 #else
1710 RF(below);
1711 #endif
1712 case FN_LTE:
1713 *abiInfo = BIF_TWOPARMSONFPSTACK|BIF_RETURNSBOOL|BIF_FPSTACKUSE(2);
1714 RF(beloweq);
1716 #undef RF
1717 #define RF(x) return (void*)_asm_##x
1719 case FN_MEMORY:
1721 static void *replptrs[4]={&__NSEEL_RAMAlloc,};
1722 *replList = replptrs;
1723 *abiInfo = BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(1)|BIF_CLEARDENORMAL;
1724 #ifdef GLUE_MEM_NEEDS_PPROC
1725 *pProc = NSEEL_PProc_RAM;
1726 #endif
1727 RF(megabuf);
1729 break;
1730 case FN_GMEMORY:
1732 static void *replptrs[4]={&__NSEEL_RAMAllocGMEM,};
1733 *replList = replptrs;
1734 *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(1)|BIF_CLEARDENORMAL;
1735 *pProc=NSEEL_PProc_GRAM;
1736 RF(gmegabuf);
1738 break;
1739 #undef RF
1741 case FUNCTYPE_FUNCTIONTYPEREC:
1742 if (fn)
1744 functionType *p=(functionType *)fn;
1746 // if prefers fpstack or bool, or ignoring value, then use fp-stack versions
1747 if ((preferredReturnValues&(RETURNVALUE_BOOL|RETURNVALUE_FPSTACK)) || !preferredReturnValues)
1749 if (p->afunc == (void*)nseel_asm_min) p = &fn_min2;
1750 else if (p->afunc == (void*)nseel_asm_max) p = &fn_max2;
1753 *replList=p->replptrs;
1754 *pProc=p->pProc;
1755 *abiInfo = p->nParams & BIF_NPARAMS_MASK;
1756 if (firstConstParm)
1758 const char *name=p->name;
1759 if (!strcmp(name,"min") && *firstConstParm < -1.0e-10) *abiInfo |= BIF_CLEARDENORMAL;
1760 else if (!strcmp(name,"max") && *firstConstParm > 1.0e-10) *abiInfo |= BIF_CLEARDENORMAL;
1762 return p->afunc;
1764 break;
1767 return NULL;
1772 static void *nseel_getEELFunctionAddress(compileContext *ctx,
1773 opcodeRec *op,
1774 int *customFuncParmSize, int *customFuncLocalStorageSize,
1775 EEL_F ***customFuncLocalStorage, int *computTableTop,
1776 void **endP, int *isRaw, int wantCodeGenerated,
1777 const namespaceInformation *namespacePathToThis, int *rvMode, int *fpStackUse, int *canHaveDenormalOutput,
1778 opcodeRec **ordered_parmptrs, int num_ordered_parmptrs
1779 ) // if wantCodeGenerated is false, can return bogus pointers in raw mode
1781 _codeHandleFunctionRec *fn = (_codeHandleFunctionRec*)op->fn;
1783 namespaceInformation local_namespace={NULL};
1784 char prefix_buf[NSEEL_MAX_VARIABLE_NAMELEN+1], nm[NSEEL_MAX_FUNCSIG_NAME+1];
1785 if (!fn) return NULL;
1787 // op->relname ptr is [whatever.]funcname
1788 if (fn->parameterAsNamespaceMask || fn->usesNamespaces)
1790 if (wantCodeGenerated)
1792 char *p = prefix_buf;
1793 combineNamespaceFields(nm,namespacePathToThis,op->relname,op->namespaceidx);
1794 lstrcpyn_safe(prefix_buf,nm,sizeof(prefix_buf));
1795 local_namespace.namespacePathToThis = prefix_buf;
1796 // nm is full path of function, prefix_buf will be the path not including function name (unless function name only)
1797 while (*p) p++;
1798 while (p >= prefix_buf && *p != '.') p--;
1799 if (p > prefix_buf) *p=0;
1801 if (fn->parameterAsNamespaceMask)
1803 int x;
1804 for(x=0;x<MAX_SUB_NAMESPACES && x < fn->num_params;x++)
1806 if (fn->parameterAsNamespaceMask & (((unsigned int)1)<<x))
1808 if (wantCodeGenerated)
1810 const char *rn=NULL;
1811 char tmp[NSEEL_MAX_VARIABLE_NAMELEN+1];
1812 if (x < num_ordered_parmptrs && ordered_parmptrs[x])
1814 if (ordered_parmptrs[x]->opcodeType == OPCODETYPE_VARPTR)
1816 rn=ordered_parmptrs[x]->relname;
1818 else if (ordered_parmptrs[x]->opcodeType == OPCODETYPE_VALUE_FROM_NAMESPACENAME)
1820 const char *p=ordered_parmptrs[x]->relname;
1821 if (*p == '#') p++;
1822 combineNamespaceFields(tmp,namespacePathToThis,p,ordered_parmptrs[x]->namespaceidx);
1823 rn = tmp;
1827 if (!rn)
1829 // todo: figure out how to give correct line number/offset (ugh)
1830 snprintf(ctx->last_error_string,sizeof(ctx->last_error_string),"parameter %d to %.120s() must be namespace",x+1,fn->fname);
1831 return NULL;
1834 lstrcatn(nm,":",sizeof(nm));
1836 local_namespace.subParmInfo[x] = nm+strlen(nm);
1837 lstrcatn(nm,rn,sizeof(nm));
1839 ordered_parmptrs[x] = NULL; // prevent caller from bothering generating parameters
1843 if (wantCodeGenerated)
1845 _codeHandleFunctionRec *fr = fn;
1846 // find namespace-adjusted function (if generating code, otherwise assume size is the same)
1847 fn = 0; // if this gets re-set, it will be the new function
1848 while (fr && !fn)
1850 if (!stricmp(fr->fname,nm)) fn = fr;
1851 fr=fr->derivedCopies;
1853 if (!fn) // generate copy of function
1855 fn = eel_createFunctionNamespacedInstance(ctx,(_codeHandleFunctionRec*)op->fn,nm);
1859 if (!fn) return NULL;
1861 if (!fn->startptr && fn->opcodes && fn->startptr_size != 0)
1863 int sz = fn->startptr_size;
1865 if (sz < 0)
1867 fn->tmpspace_req=0;
1868 fn->rvMode = RETURNVALUE_IGNORE;
1869 fn->canHaveDenormalOutput=0;
1871 sz = compileOpcodes(ctx,fn->opcodes,NULL,128*1024*1024,&fn->tmpspace_req,
1872 wantCodeGenerated ? &local_namespace : NULL,RETURNVALUE_FPSTACK,
1873 &fn->rvMode,&fn->fpStackUsage,&fn->canHaveDenormalOutput);
1874 if (sz<0) return NULL;
1876 fn->startptr_base_size = fn->startptr_size = sz;
1879 if (!wantCodeGenerated)
1881 // don't compile anything for now, just give stats
1882 if (computTableTop) *computTableTop += fn->tmpspace_req;
1883 *customFuncParmSize = fn->num_params;
1884 *customFuncLocalStorage = fn->localstorage;
1885 *customFuncLocalStorageSize = fn->localstorage_size;
1886 *rvMode = fn->rvMode;
1887 *fpStackUse = fn->fpStackUsage;
1888 if (canHaveDenormalOutput) *canHaveDenormalOutput=fn->canHaveDenormalOutput;
1890 if (fn->startptr_base_size <= NSEEL_MAX_FUNCTION_SIZE_FOR_INLINE &&
1891 !(ctx->optimizeDisableFlags&OPTFLAG_NO_INLINEFUNC))
1893 *isRaw = 1;
1894 *endP = ((char *)1) + fn->startptr_base_size;
1895 return (char *)1;
1897 return (void*)nseel_asm_fcall;
1900 if (fn->startptr_base_size <= NSEEL_MAX_FUNCTION_SIZE_FOR_INLINE &&
1901 !(ctx->optimizeDisableFlags&OPTFLAG_NO_INLINEFUNC))
1903 void *p=newTmpBlock(ctx,sz);
1904 fn->tmpspace_req=0;
1905 if (p)
1907 fn->canHaveDenormalOutput=0;
1908 if (fn->isCommonFunction) ctx->isGeneratingCommonFunction++;
1909 sz=compileOpcodes(ctx,fn->opcodes,(unsigned char*)p,sz,&fn->tmpspace_req,&local_namespace,RETURNVALUE_FPSTACK,&fn->rvMode,&fn->fpStackUsage,&fn->canHaveDenormalOutput);
1910 if (fn->isCommonFunction) ctx->isGeneratingCommonFunction--;
1911 // recompile function with native context pointers
1912 if (sz>0)
1914 fn->startptr_size=sz;
1915 fn->startptr=p;
1919 else
1921 unsigned char *codeCall;
1922 fn->tmpspace_req=0;
1923 fn->fpStackUsage=0;
1924 fn->canHaveDenormalOutput=0;
1925 if (fn->isCommonFunction) ctx->isGeneratingCommonFunction++;
1926 codeCall=compileCodeBlockWithRet(ctx,fn->opcodes,&fn->tmpspace_req,&local_namespace,RETURNVALUE_FPSTACK,&fn->rvMode,&fn->fpStackUsage,&fn->canHaveDenormalOutput);
1927 if (fn->isCommonFunction) ctx->isGeneratingCommonFunction--;
1928 if (codeCall)
1930 void *f=GLUE_realAddress(nseel_asm_fcall,&sz);
1931 fn->startptr = newTmpBlock(ctx,sz);
1932 if (fn->startptr)
1934 memcpy(fn->startptr,f,sz);
1935 EEL_GLUE_set_immediate(fn->startptr,(INT_PTR)codeCall);
1936 fn->startptr_size = sz;
1942 if (fn->startptr)
1944 if (computTableTop) *computTableTop += fn->tmpspace_req;
1945 *customFuncParmSize = fn->num_params;
1946 *customFuncLocalStorage = fn->localstorage;
1947 *customFuncLocalStorageSize = fn->localstorage_size;
1948 *rvMode = fn->rvMode;
1949 *fpStackUse = fn->fpStackUsage;
1950 if (canHaveDenormalOutput) *canHaveDenormalOutput= fn->canHaveDenormalOutput;
1951 *endP = (char*)fn->startptr + fn->startptr_size;
1952 if (!wantCodeGenerated &&
1953 fn->startptr_base_size <= NSEEL_MAX_FUNCTION_SIZE_FOR_INLINE &&
1954 !(ctx->optimizeDisableFlags&OPTFLAG_NO_INLINEFUNC))
1956 // report the correct maximum base length for the calculation pass
1957 *endP = (char*)fn->startptr + fn->startptr_base_size;
1959 *isRaw=1;
1960 return fn->startptr;
1963 return 0;
1968 // returns true if does something (other than calculating and throwing away a value)
1969 static char optimizeOpcodes(compileContext *ctx, opcodeRec *op, int needsResult)
1971 opcodeRec *lastJoinOp=NULL;
1972 char retv, retv_parm[3], joined_retv=0;
1973 while (op && op->opcodeType == OPCODETYPE_FUNC2 && op->fntype == FN_JOIN_STATEMENTS)
1975 if (!optimizeOpcodes(ctx,op->parms.parms[0], 0) || OPCODE_IS_TRIVIAL(op->parms.parms[0]))
1977 // direct value, can skip ourselves
1978 memcpy(op,op->parms.parms[1],sizeof(*op));
1980 else
1982 joined_retv |= 1;
1983 lastJoinOp = op;
1984 op = op->parms.parms[1];
1987 goto start_over;
1989 #define RESTART_DIRECTVALUE(X) { op->parms.dv.directValue = (X); goto start_over_directvalue; }
1990 start_over_directvalue:
1991 op->opcodeType = OPCODETYPE_DIRECTVALUE;
1992 op->parms.dv.valuePtr=NULL;
1994 start_over: // when an opcode changed substantially in optimization, goto here to reprocess it
1996 retv = retv_parm[0]=retv_parm[1]=retv_parm[2]=0;
1998 if (!op || // should never really happen
1999 OPCODE_IS_TRIVIAL(op) || // should happen often (vars)
2000 op->opcodeType < 0 || op->opcodeType >= OPCODETYPE_INVALID // should never happen (assert would be appropriate heh)
2001 ) return joined_retv;
2003 if (!needsResult)
2005 if (op->fntype == FUNCTYPE_EELFUNC)
2007 needsResult=1; // assume eel functions are non-const for now
2009 else if (op->fntype == FUNCTYPE_FUNCTIONTYPEREC)
2011 functionType *pfn = (functionType *)op->fn;
2012 if (!pfn || !(pfn->nParams&NSEEL_NPARAMS_FLAG_CONST)) needsResult=1;
2014 else if (op->fntype >= FN_NONCONST_BEGIN && op->fntype < FUNCTYPE_SIMPLEMAX)
2016 needsResult=1;
2020 if (op->opcodeType>=OPCODETYPE_FUNC2) retv_parm[1] = optimizeOpcodes(ctx,op->parms.parms[1], needsResult);
2021 if (op->opcodeType>=OPCODETYPE_FUNC3) retv_parm[2] = optimizeOpcodes(ctx,op->parms.parms[2], needsResult);
2023 retv_parm[0] = optimizeOpcodes(ctx,op->parms.parms[0], needsResult ||
2024 (FNPTR_HAS_CONDITIONAL_EXEC(op) && (retv_parm[1] || retv_parm[2] || op->opcodeType <= OPCODETYPE_FUNC1)) );
2026 if (op->opcodeType != OPCODETYPE_MOREPARAMS)
2028 if (op->fntype >= 0 && op->fntype < FUNCTYPE_SIMPLEMAX)
2030 if (op->opcodeType == OPCODETYPE_FUNC1) // within FUNCTYPE_SIMPLE
2032 if (op->parms.parms[0]->opcodeType == OPCODETYPE_DIRECTVALUE)
2034 switch (op->fntype)
2036 case FN_NOTNOT: RESTART_DIRECTVALUE(fabs(op->parms.parms[0]->parms.dv.directValue)>=NSEEL_CLOSEFACTOR ? 1.0 : 0.0);
2037 case FN_NOT: RESTART_DIRECTVALUE(fabs(op->parms.parms[0]->parms.dv.directValue)>=NSEEL_CLOSEFACTOR ? 0.0 : 1.0);
2038 case FN_UMINUS: RESTART_DIRECTVALUE(- op->parms.parms[0]->parms.dv.directValue);
2041 else if (op->fntype == FN_NOT || op->fntype == FN_NOTNOT)
2043 if (op->parms.parms[0]->opcodeType == OPCODETYPE_FUNC1)
2045 switch (op->parms.parms[0]->fntype)
2047 case FN_UMINUS:
2048 case FN_NOTNOT: // ignore any NOTNOTs UMINUS or UPLUS, they would have no effect anyway
2049 op->parms.parms[0] = op->parms.parms[0]->parms.parms[0];
2050 goto start_over;
2052 case FN_NOT:
2053 op->fntype = op->fntype==FN_NOT ? FN_NOTNOT : FN_NOT; // switch between FN_NOT and FN_NOTNOT
2054 op->parms.parms[0] = op->parms.parms[0]->parms.parms[0];
2055 goto start_over;
2058 else if (op->parms.parms[0]->opcodeType == OPCODETYPE_FUNC2)
2060 int repl_type = -1;
2061 switch (op->parms.parms[0]->fntype)
2063 case FN_EQ: repl_type = FN_NE; break;
2064 case FN_NE: repl_type = FN_EQ; break;
2065 case FN_EQ_EXACT: repl_type = FN_NE_EXACT; break;
2066 case FN_NE_EXACT: repl_type = FN_EQ_EXACT; break;
2067 case FN_LT: repl_type = FN_GTE; break;
2068 case FN_LTE: repl_type = FN_GT; break;
2069 case FN_GT: repl_type = FN_LTE; break;
2070 case FN_GTE: repl_type = FN_LT; break;
2072 if (repl_type != -1)
2074 const int oldtype = op->fntype;
2075 memcpy(op,op->parms.parms[0],sizeof(*op));
2076 if (oldtype == FN_NOT) op->fntype = repl_type;
2077 goto start_over;
2082 else if (op->opcodeType == OPCODETYPE_FUNC2) // within FUNCTYPE_SIMPLE
2084 const int dv0 = op->parms.parms[0]->opcodeType == OPCODETYPE_DIRECTVALUE;
2085 const int dv1 = op->parms.parms[1]->opcodeType == OPCODETYPE_DIRECTVALUE;
2086 if (dv0 && dv1)
2088 int reval = -1;
2089 switch (op->fntype)
2091 case FN_MOD:
2093 EEL_F ret = 0.0;
2094 int a = (int) fabs(op->parms.parms[1]->parms.dv.directValue);
2095 if (a)
2097 #ifdef GLUE_MOD_IS_64
2098 ret = ((WDL_INT64) fabs(op->parms.parms[0]->parms.dv.directValue)) % a;
2099 #else
2100 ret = ((int) fabs(op->parms.parms[0]->parms.dv.directValue)) % a;
2101 #endif
2102 if (WDL_NOT_NORMALLY(ret<0)) ret = -ret;
2104 RESTART_DIRECTVALUE(ret);
2106 break;
2107 case FN_SHL: RESTART_DIRECTVALUE(((int)op->parms.parms[0]->parms.dv.directValue) << ((int)op->parms.parms[1]->parms.dv.directValue));
2108 case FN_SHR: RESTART_DIRECTVALUE(((int)op->parms.parms[0]->parms.dv.directValue) >> ((int)op->parms.parms[1]->parms.dv.directValue));
2109 case FN_POW: RESTART_DIRECTVALUE(pow(op->parms.parms[0]->parms.dv.directValue, op->parms.parms[1]->parms.dv.directValue));
2110 case FN_DIVIDE: RESTART_DIRECTVALUE(op->parms.parms[0]->parms.dv.directValue / op->parms.parms[1]->parms.dv.directValue);
2111 case FN_MULTIPLY: RESTART_DIRECTVALUE(op->parms.parms[0]->parms.dv.directValue * op->parms.parms[1]->parms.dv.directValue);
2113 case FN_ADD: RESTART_DIRECTVALUE(op->parms.parms[0]->parms.dv.directValue + op->parms.parms[1]->parms.dv.directValue);
2114 case FN_SUB: RESTART_DIRECTVALUE(op->parms.parms[0]->parms.dv.directValue - op->parms.parms[1]->parms.dv.directValue);
2115 case FN_AND: RESTART_DIRECTVALUE((double) (((WDL_INT64)op->parms.parms[0]->parms.dv.directValue) & ((WDL_INT64)op->parms.parms[1]->parms.dv.directValue)));
2116 case FN_OR: RESTART_DIRECTVALUE((double) (((WDL_INT64)op->parms.parms[0]->parms.dv.directValue) | ((WDL_INT64)op->parms.parms[1]->parms.dv.directValue)));
2117 case FN_XOR: RESTART_DIRECTVALUE((double) (((WDL_INT64)op->parms.parms[0]->parms.dv.directValue) ^ ((WDL_INT64)op->parms.parms[1]->parms.dv.directValue)));
2119 case FN_EQ: reval = fabs(op->parms.parms[0]->parms.dv.directValue - op->parms.parms[1]->parms.dv.directValue) < NSEEL_CLOSEFACTOR; break;
2120 case FN_NE: reval = fabs(op->parms.parms[0]->parms.dv.directValue - op->parms.parms[1]->parms.dv.directValue) >= NSEEL_CLOSEFACTOR; break;
2121 case FN_EQ_EXACT: reval = op->parms.parms[0]->parms.dv.directValue == op->parms.parms[1]->parms.dv.directValue; break;
2122 case FN_NE_EXACT: reval = op->parms.parms[0]->parms.dv.directValue != op->parms.parms[1]->parms.dv.directValue; break;
2123 case FN_LT: reval = op->parms.parms[0]->parms.dv.directValue < op->parms.parms[1]->parms.dv.directValue; break;
2124 case FN_LTE: reval = op->parms.parms[0]->parms.dv.directValue <= op->parms.parms[1]->parms.dv.directValue; break;
2125 case FN_GT: reval = op->parms.parms[0]->parms.dv.directValue > op->parms.parms[1]->parms.dv.directValue; break;
2126 case FN_GTE: reval = op->parms.parms[0]->parms.dv.directValue >= op->parms.parms[1]->parms.dv.directValue; break;
2127 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;
2128 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;
2131 if (reval >= 0) RESTART_DIRECTVALUE((EEL_F) reval);
2133 else if (dv0 || dv1)
2135 double dvalue = op->parms.parms[!dv0]->parms.dv.directValue;
2136 switch (op->fntype)
2138 case FN_OR:
2139 case FN_XOR:
2140 if (!(WDL_INT64)dvalue)
2142 // replace with or0
2143 op->opcodeType = OPCODETYPE_FUNC1;
2144 op->fntype = FUNCTYPE_FUNCTIONTYPEREC;
2145 op->fn = &fn_or0;
2146 if (dv0) op->parms.parms[0] = op->parms.parms[1];
2147 goto start_over;
2149 break;
2150 case FN_SUB:
2151 if (dv0)
2153 if (dvalue == 0.0)
2155 op->opcodeType = OPCODETYPE_FUNC1;
2156 op->fntype = FN_UMINUS;
2157 op->parms.parms[0] = op->parms.parms[1];
2158 goto start_over;
2160 break;
2162 WDL_FALLTHROUGH; // fall through, if dv1 we can remove +0.0
2164 case FN_ADD:
2165 if (dvalue == 0.0)
2167 memcpy(op,op->parms.parms[!!dv0],sizeof(*op));
2168 goto start_over;
2170 break;
2171 case FN_AND:
2172 if ((WDL_INT64)dvalue) break;
2173 dvalue = 0.0; // treat x&0 as x*0, which optimizes to 0
2175 WDL_FALLTHROUGH; // fall through
2176 case FN_MULTIPLY:
2177 if (dvalue == 0.0) // remove multiply by 0.0 (using 0.0 direct value as replacement), unless the nonzero side did something
2179 if (!retv_parm[!!dv0])
2181 memcpy(op,op->parms.parms[!dv0],sizeof(*op)); // set to 0 if other action wouldn't do anything
2182 goto start_over;
2184 else
2186 // this is 0.0 * oldexpressionthatmustbeprocessed or oldexpressionthatmustbeprocessed*0.0
2187 op->fntype = FN_JOIN_STATEMENTS;
2189 if (dv0) // 0.0*oldexpression, reverse the order so that 0 is returned
2191 // set to (oldexpression;0)
2192 opcodeRec *tmp = op->parms.parms[1];
2193 op->parms.parms[1] = op->parms.parms[0];
2194 op->parms.parms[0] = tmp;
2196 goto start_over;
2199 else if (dvalue == 1.0) // remove multiply by 1.0 (using non-1.0 value as replacement)
2201 memcpy(op,op->parms.parms[!!dv0],sizeof(*op));
2202 goto start_over;
2204 break;
2205 case FN_POW:
2206 if (dv1)
2208 // x^0 = 1
2209 if (fabs(dvalue) < 1e-30)
2211 RESTART_DIRECTVALUE(1.0);
2213 // x^1 = x
2214 if (fabs(dvalue-1.0) < 1e-30)
2216 memcpy(op,op->parms.parms[0],sizeof(*op));
2217 goto start_over;
2220 else if (dv0)
2222 // pow(constant, x) = exp((x) * ln(constant)), if constant>0
2223 // opcodeRec *parm0 = op->parms.parms[0];
2224 if (dvalue > 0.0)
2226 static functionType *expcpy;
2227 if (!expcpy)
2229 expcpy = nseel_getFunctionByName(NULL,"exp",NULL);
2230 if (WDL_NOT_NORMALLY(!expcpy)) break;
2233 // 1^x = 1
2234 if (fabs(dvalue-1.0) < 1e-30)
2236 RESTART_DIRECTVALUE(1.0);
2239 dvalue=log(dvalue);
2241 if (fabs(dvalue-1.0) < 1e-9)
2243 // caller wanted e^x
2244 op->parms.parms[0]=op->parms.parms[1];
2246 else
2248 // it would be nice to replace 10^x with exp(log(10)*x) or 2^x with exp(log(2),x), but
2249 // doing so breaks rounding. we could maybe only allow 10^x, which is used for dB conversion,
2250 // but for now we should just force the programmer do it exp(log(10)*x) themselves.
2251 break;
2254 parm0->opcodeType = OPCODETYPE_FUNC2;
2255 parm0->fntype = FN_MULTIPLY;
2256 parm0->parms.parms[0] = nseel_createCompiledValue(ctx,dvalue);
2257 parm0->parms.parms[1] = op->parms.parms[1];
2261 op->opcodeType = OPCODETYPE_FUNC1;
2262 op->fntype = FUNCTYPE_FUNCTIONTYPEREC;
2263 op->fn = expcpy;
2264 goto start_over;
2267 break;
2268 case FN_MOD:
2269 if (dv1)
2271 const int a = (int) dvalue;
2272 if (!a)
2274 RESTART_DIRECTVALUE(0.0);
2277 break;
2278 case FN_DIVIDE:
2279 if (dv1)
2281 if (dvalue == 1.0) // remove divide by 1.0 (using non-1.0 value as replacement)
2283 memcpy(op,op->parms.parms[!!dv0],sizeof(*op));
2284 goto start_over;
2286 else
2288 // change to a multiply
2289 if (dvalue == 0.0)
2291 op->fntype = FN_MULTIPLY;
2292 goto start_over;
2294 else
2296 double d = 1.0/dvalue;
2297 WDL_UINT64 w;
2298 memcpy(&w,&d,sizeof(d));
2299 // allow conversion to multiply if reciprocal is exact
2300 // we could also just look to see if the last few digits of the mantissa were 0, which would probably be good
2301 // enough, but if the user really wants it they should do * (1/x) instead to force precalculation of reciprocal.
2302 if (!(w & WDL_UINT64_CONST(0xfffffffffffff)))
2304 op->fntype = FN_MULTIPLY;
2305 op->parms.parms[1]->parms.dv.directValue = d;
2306 op->parms.parms[1]->parms.dv.valuePtr=NULL;
2307 goto start_over;
2312 else if (dvalue == 0.0)
2314 if (!retv_parm[!!dv0])
2316 // if 0/x set to always 0.
2317 // this is 0.0 / (oldexpression that can be eliminated)
2318 memcpy(op,op->parms.parms[!dv0],sizeof(*op)); // set to 0 if other action wouldn't do anything
2320 else
2322 opcodeRec *tmp;
2323 // this is 0.0 / oldexpressionthatmustbeprocessed
2324 op->fntype = FN_JOIN_STATEMENTS;
2325 tmp = op->parms.parms[1];
2326 op->parms.parms[1] = op->parms.parms[0];
2327 op->parms.parms[0] = tmp;
2328 // set to (oldexpression;0)
2330 goto start_over;
2332 break;
2333 case FN_EQ:
2334 if (dvalue == 0.0)
2336 // convert x == 0.0 to !x
2337 op->opcodeType=OPCODETYPE_FUNC1;
2338 op->fntype = FN_NOT;
2339 if (dv0) op->parms.parms[0]=op->parms.parms[1];
2340 goto start_over;
2342 break;
2343 case FN_NE:
2344 if (dvalue == 0.0)
2346 // convert x != 0.0 to !!
2347 op->opcodeType=OPCODETYPE_FUNC1;
2348 op->fntype = FN_NOTNOT;
2349 if (dv0) op->parms.parms[0]=op->parms.parms[1];
2350 goto start_over;
2352 break;
2353 case FN_LOGICAL_AND:
2354 if (dv0)
2356 // dvalue && expr
2357 if (fabs(dvalue) < NSEEL_CLOSEFACTOR)
2359 // 0 && expr, replace with 0
2360 RESTART_DIRECTVALUE(0.0);
2362 else
2364 // 1 && expr, replace with 0 != expr
2365 op->fntype = FN_NE;
2366 op->parms.parms[0]->parms.dv.valuePtr=NULL;
2367 op->parms.parms[0]->parms.dv.directValue = 0.0;
2370 else
2372 // expr && dvalue
2373 if (fabs(dvalue) < NSEEL_CLOSEFACTOR)
2375 // expr && 0
2376 if (!retv_parm[0])
2378 // expr has no consequence, drop it
2379 RESTART_DIRECTVALUE(0.0);
2381 else
2383 // replace with (expr; 0)
2384 op->fntype = FN_JOIN_STATEMENTS;
2385 op->parms.parms[1]->parms.dv.valuePtr=NULL;
2386 op->parms.parms[1]->parms.dv.directValue = 0.0;
2389 else
2391 // expr && 1, replace with expr != 0
2392 op->fntype = FN_NE;
2393 op->parms.parms[1]->parms.dv.valuePtr=NULL;
2394 op->parms.parms[1]->parms.dv.directValue = 0.0;
2397 goto start_over;
2398 case FN_LOGICAL_OR:
2399 if (dv0)
2401 // dvalue || expr
2402 if (fabs(dvalue) >= NSEEL_CLOSEFACTOR)
2404 // 1 || expr, replace with 1
2405 RESTART_DIRECTVALUE(1.0);
2407 else
2409 // 0 || expr, replace with 0 != expr
2410 op->fntype = FN_NE;
2411 op->parms.parms[0]->parms.dv.valuePtr=NULL;
2412 op->parms.parms[0]->parms.dv.directValue = 0.0;
2415 else
2417 // expr || dvalue
2418 if (fabs(dvalue) >= NSEEL_CLOSEFACTOR)
2420 // expr || 1
2421 if (!retv_parm[0])
2423 // expr has no consequence, drop it and return 1
2424 RESTART_DIRECTVALUE(1.0);
2426 else
2428 // replace with (expr; 1)
2429 op->fntype = FN_JOIN_STATEMENTS;
2430 op->parms.parms[1]->parms.dv.valuePtr=NULL;
2431 op->parms.parms[1]->parms.dv.directValue = 1.0;
2434 else
2436 // expr || 0, replace with expr != 0
2437 op->fntype = FN_NE;
2438 op->parms.parms[1]->parms.dv.valuePtr=NULL;
2439 op->parms.parms[1]->parms.dv.directValue = 0.0;
2442 goto start_over;
2444 } // dv0 || dv1
2446 // general optimization of two parameters
2447 switch (op->fntype)
2449 case FN_MULTIPLY:
2451 opcodeRec *first_parm = op->parms.parms[0],*second_parm = op->parms.parms[1];
2453 if (second_parm->opcodeType == first_parm->opcodeType)
2455 switch(first_parm->opcodeType)
2457 case OPCODETYPE_VALUE_FROM_NAMESPACENAME:
2458 if (first_parm->namespaceidx != second_parm->namespaceidx) break;
2459 WDL_FALLTHROUGH; // fall through
2460 case OPCODETYPE_VARPTR:
2461 if (first_parm->relname && second_parm->relname && !stricmp(second_parm->relname,first_parm->relname)) second_parm=NULL;
2462 break;
2463 case OPCODETYPE_VARPTRPTR:
2464 if (first_parm->parms.dv.valuePtr && first_parm->parms.dv.valuePtr==second_parm->parms.dv.valuePtr) second_parm=NULL;
2465 break;
2468 if (!second_parm) // switch from x*x to sqr(x)
2470 static functionType *sqrcpy;
2471 if (!sqrcpy) sqrcpy = nseel_getFunctionByName(NULL,"sqr",NULL);
2472 if (WDL_NORMALLY(sqrcpy))
2474 op->opcodeType = OPCODETYPE_FUNC1;
2475 op->fntype = FUNCTYPE_FUNCTIONTYPEREC;
2476 op->fn = sqrcpy;
2477 goto start_over;
2482 break;
2483 case FN_POW:
2485 opcodeRec *first_parm = op->parms.parms[0];
2486 if (first_parm->opcodeType == op->opcodeType && first_parm->fntype == FN_POW)
2488 // since first_parm is a pow too, we can multiply the exponents.
2490 // set our base to be the base of the inner pow
2491 op->parms.parms[0] = first_parm->parms.parms[0];
2493 // make the old extra pow be a multiply of the exponents
2494 first_parm->fntype = FN_MULTIPLY;
2495 first_parm->parms.parms[0] = op->parms.parms[1];
2497 // put that as the exponent
2498 op->parms.parms[1] = first_parm;
2500 goto start_over;
2503 break;
2504 case FN_LOGICAL_AND:
2505 case FN_LOGICAL_OR:
2506 if (op->parms.parms[0]->fntype == FN_NOTNOT)
2508 // remove notnot, unnecessary for input to &&/|| operators
2509 op->parms.parms[0] = op->parms.parms[0]->parms.parms[0];
2510 goto start_over;
2512 if (op->parms.parms[1]->fntype == FN_NOTNOT)
2514 // remove notnot, unnecessary for input to &&/|| operators
2515 op->parms.parms[1] = op->parms.parms[1]->parms.parms[0];
2516 goto start_over;
2518 break;
2521 else if (op->opcodeType==OPCODETYPE_FUNC3) // within FUNCTYPE_SIMPLE
2523 if (op->fntype == FN_IF_ELSE)
2525 if (op->parms.parms[0]->opcodeType == OPCODETYPE_DIRECTVALUE)
2527 int s = fabs(op->parms.parms[0]->parms.dv.directValue) >= NSEEL_CLOSEFACTOR;
2528 memcpy(op,op->parms.parms[s ? 1 : 2],sizeof(opcodeRec));
2529 goto start_over;
2531 if (op->parms.parms[0]->opcodeType == OPCODETYPE_FUNC1)
2533 if (op->parms.parms[0]->fntype == FN_NOTNOT)
2535 // remove notnot, unnecessary for input to ? operator
2536 op->parms.parms[0] = op->parms.parms[0]->parms.parms[0];
2537 goto start_over;
2542 if (op->fntype >= FN_NONCONST_BEGIN && op->fntype < FUNCTYPE_SIMPLEMAX) retv|=1;
2544 // FUNCTYPE_SIMPLE
2546 else if (op->fntype == FUNCTYPE_FUNCTIONTYPEREC && op->fn)
2550 probably worth doing reduction on:
2551 _divop (constant change to multiply)
2552 _and
2556 maybe:
2561 also, optimize should (recursively or maybe iteratively?) search transitive functions (mul/div) for more constant reduction possibilities
2567 functionType *pfn = (functionType *)op->fn;
2569 if (!(pfn->nParams&NSEEL_NPARAMS_FLAG_CONST)) retv|=1;
2571 if (op->opcodeType==OPCODETYPE_FUNC1) // within FUNCTYPE_FUNCTIONTYPEREC
2573 if (op->parms.parms[0]->opcodeType == OPCODETYPE_DIRECTVALUE)
2575 int suc=1;
2576 EEL_F v = op->parms.parms[0]->parms.dv.directValue;
2577 #define DOF(x) if (!strcmp(pfn->name,#x)) v = x(v); else
2578 #define DOF2(x,y) if (!strcmp(pfn->name,#x)) v = x(y); else
2579 DOF(sin)
2580 DOF(cos)
2581 DOF(tan)
2582 DOF(asin)
2583 DOF(acos)
2584 DOF(atan)
2585 DOF2(sqrt, fabs(v))
2586 DOF(exp)
2587 DOF(log)
2588 DOF(log10)
2589 /* else */ suc=0;
2590 #undef DOF
2591 #undef DOF2
2592 if (suc)
2594 RESTART_DIRECTVALUE(v);
2600 else if (op->opcodeType==OPCODETYPE_FUNC2) // within FUNCTYPE_FUNCTIONTYPEREC
2602 const int dv0=op->parms.parms[0]->opcodeType == OPCODETYPE_DIRECTVALUE;
2603 const int dv1=op->parms.parms[1]->opcodeType == OPCODETYPE_DIRECTVALUE;
2604 if (dv0 && dv1)
2606 if (!strcmp(pfn->name,"atan2"))
2608 RESTART_DIRECTVALUE(atan2(op->parms.parms[0]->parms.dv.directValue, op->parms.parms[1]->parms.dv.directValue));
2612 // FUNCTYPE_FUNCTIONTYPEREC
2614 else
2616 // unknown or eel func, assume non-const
2617 retv |= 1;
2621 // if we need results, or our function has effects itself, then finish
2622 if (retv || needsResult)
2624 return retv || joined_retv || retv_parm[0] || retv_parm[1] || retv_parm[2];
2627 // we don't need results here, and our function is const, which means we can remove it
2629 int cnt=0, idx1=0, idx2=0, x;
2630 for (x=0;x<3;x++) if (retv_parm[x]) { if (!cnt++) idx1=x; else idx2=x; }
2632 if (!cnt) // none of the parameters do anything, remove this opcode
2634 if (lastJoinOp)
2636 // replace previous join with its first linked opcode, removing this opcode completely
2637 memcpy(lastJoinOp,lastJoinOp->parms.parms[0],sizeof(*lastJoinOp));
2639 else if (op->opcodeType != OPCODETYPE_DIRECTVALUE)
2641 // allow caller to easily detect this as trivial and remove it
2642 op->opcodeType = OPCODETYPE_DIRECTVALUE;
2643 op->parms.dv.valuePtr=NULL;
2644 op->parms.dv.directValue=0.0;
2646 // return joined_retv below
2648 else
2650 // if parameters are non-const, and we're a conditional, preserve our function
2651 if (FNPTR_HAS_CONDITIONAL_EXEC(op)) return 1;
2653 // otherwise, condense into either the non-const statement, or a join
2654 if (cnt==1)
2656 memcpy(op,op->parms.parms[idx1],sizeof(*op));
2658 else if (cnt == 2)
2660 op->opcodeType = OPCODETYPE_FUNC2;
2661 op->fntype = FN_JOIN_STATEMENTS;
2662 op->fn = op;
2663 op->parms.parms[0] = op->parms.parms[idx1];
2664 op->parms.parms[1] = op->parms.parms[idx2];
2665 op->parms.parms[2] = NULL;
2667 else
2669 // todo need to create a new opcodeRec here, for now just leave as is
2670 // (non-conditional const 3 parameter functions are rare anyway)
2672 return 1;
2675 return joined_retv;
2679 static int generateValueToReg(compileContext *ctx, opcodeRec *op, unsigned char *bufOut, int whichReg, const namespaceInformation *functionPrefix, int allowCache)
2681 EEL_F *b=NULL;
2682 if (op->opcodeType==OPCODETYPE_VALUE_FROM_NAMESPACENAME)
2684 char nm[NSEEL_MAX_VARIABLE_NAMELEN+1];
2685 const char *p = op->relname;
2686 combineNamespaceFields(nm,functionPrefix,p+(*p == '#'),op->namespaceidx);
2687 if (!nm[0]) return -1;
2688 if (*p == '#')
2690 if (ctx->isGeneratingCommonFunction)
2691 b = newCtxDataBlock(sizeof(EEL_F),sizeof(EEL_F));
2692 else
2693 b = newDataBlock(sizeof(EEL_F),sizeof(EEL_F));
2695 if (!b) RET_MINUS1_FAIL("error creating storage for str")
2697 if (!ctx->onNamedString) return -1; // should never happen, will not generate OPCODETYPE_VALUE_FROM_NAMESPACENAME with # prefix if !onNamedString
2699 *b = ctx->onNamedString(ctx->caller_this,nm);
2701 else
2703 b = nseel_int_register_var(ctx,nm,0,NULL);
2704 if (!b) RET_MINUS1_FAIL("error registering var")
2707 else
2709 if (op->opcodeType != OPCODETYPE_DIRECTVALUE) allowCache=0;
2711 if (op->opcodeType==OPCODETYPE_DIRECTVALUE_TEMPSTRING && ctx->onNamedString)
2713 op->parms.dv.directValue = ctx->onNamedString(ctx->caller_this,"");
2714 op->parms.dv.valuePtr = NULL;
2717 b=op->parms.dv.valuePtr;
2718 if (!b && op->opcodeType == OPCODETYPE_VARPTR && op->relname && op->relname[0])
2720 op->parms.dv.valuePtr = b = nseel_int_register_var(ctx,op->relname,0,NULL);
2723 if (b && op->opcodeType == OPCODETYPE_VARPTRPTR) b = *(EEL_F **)b;
2724 if (!b && allowCache)
2726 int n=50; // only scan last X items
2727 opcodeRec *r = ctx->directValueCache;
2728 while (r && n--)
2730 if (r->parms.dv.directValue == op->parms.dv.directValue && (b=r->parms.dv.valuePtr)) break;
2731 r=(opcodeRec*)r->fn;
2734 if (!b)
2736 ctx->l_stats[3]++;
2737 if (ctx->isGeneratingCommonFunction)
2738 b = newCtxDataBlock(sizeof(EEL_F),sizeof(EEL_F));
2739 else
2740 b = newDataBlock(sizeof(EEL_F),sizeof(EEL_F));
2742 if (!b) RET_MINUS1_FAIL("error allocating data block")
2744 if (op->opcodeType != OPCODETYPE_VARPTRPTR) op->parms.dv.valuePtr = b;
2745 *b = denormal_filter_double2(op->parms.dv.directValue);
2747 if (allowCache)
2749 op->fn = ctx->directValueCache;
2750 ctx->directValueCache = op;
2755 GLUE_MOV_PX_DIRECTVALUE_GEN(bufOut,(INT_PTR)b,whichReg);
2756 return GLUE_MOV_PX_DIRECTVALUE_SIZE;
2760 unsigned char *compileCodeBlockWithRet(compileContext *ctx, opcodeRec *rec, int *computTableSize, const namespaceInformation *namespacePathToThis,
2761 int supportedReturnValues, int *rvType, int *fpStackUsage, int *canHaveDenormalOutput)
2763 unsigned char *p, *newblock2;
2764 // generate code call
2765 int funcsz=compileOpcodes(ctx,rec,NULL,1024*1024*128,NULL,namespacePathToThis,supportedReturnValues, rvType,fpStackUsage, NULL);
2766 if (funcsz<0) return NULL;
2768 p = newblock2 = newCodeBlock(funcsz+ sizeof(GLUE_RET)+GLUE_FUNC_ENTER_SIZE+GLUE_FUNC_LEAVE_SIZE,32);
2769 if (!newblock2) return NULL;
2770 #if GLUE_FUNC_ENTER_SIZE > 0
2771 memcpy(p,&GLUE_FUNC_ENTER,GLUE_FUNC_ENTER_SIZE);
2772 p += GLUE_FUNC_ENTER_SIZE;
2773 #endif
2774 *fpStackUsage=0;
2775 funcsz=compileOpcodes(ctx,rec,p, funcsz, computTableSize,namespacePathToThis,supportedReturnValues, rvType,fpStackUsage, canHaveDenormalOutput);
2776 if (funcsz<0) return NULL;
2777 p+=funcsz;
2779 #if GLUE_FUNC_LEAVE_SIZE > 0
2780 memcpy(p,&GLUE_FUNC_LEAVE,GLUE_FUNC_LEAVE_SIZE);
2781 p+=GLUE_FUNC_LEAVE_SIZE;
2782 #endif
2783 memcpy(p,&GLUE_RET,sizeof(GLUE_RET)); p+=sizeof(GLUE_RET);
2784 #if defined(__arm__) || defined(__aarch64__)
2785 __clear_cache(newblock2,p);
2786 #endif
2788 ctx->l_stats[2]+=funcsz+2;
2789 return newblock2;
2792 static int compileNativeFunctionCall(compileContext *ctx, opcodeRec *op, unsigned char *bufOut, int bufOut_len, int *computTableSize, const namespaceInformation *namespacePathToThis,
2793 int *rvMode, int *fpStackUsage, int preferredReturnValues, int *canHaveDenormalOutput)
2795 // builtin function generation
2796 int func_size=0;
2797 int cfunc_abiinfo=0;
2798 int local_fpstack_use=0; // how many items we have pushed onto the fp stack
2799 int parm_size=0;
2800 int restore_stack_amt=0;
2801 #ifdef GLUE_HAS_FUSE
2802 int fuse_flags = 0; // 1 = last parm was trivial (likely mov rax, movsd xmm0 ,[rax])
2803 #endif
2804 #if GLUE_MAX_SPILL_REGS > 0
2805 int spill_reg = -1;
2806 #endif
2808 NSEEL_PPPROC preProc=0;
2809 void **repl=NULL;
2811 int n_params= 1 + op->opcodeType - OPCODETYPE_FUNC1;
2813 const int parm0_dv = op->parms.parms[0]->opcodeType == OPCODETYPE_DIRECTVALUE;
2814 const int parm1_dv = n_params > 1 && op->parms.parms[1]->opcodeType == OPCODETYPE_DIRECTVALUE;
2816 void *func = nseel_getBuiltinFunctionAddress(ctx, op->fntype, op->fn, &preProc,&repl,
2817 &cfunc_abiinfo,preferredReturnValues,
2818 parm0_dv ? &op->parms.parms[0]->parms.dv.directValue : NULL,
2819 parm1_dv ? &op->parms.parms[1]->parms.dv.directValue : NULL
2822 *fpStackUsage=BIF_GETFPSTACKUSE(cfunc_abiinfo);
2823 *rvMode = RETURNVALUE_NORMAL;
2825 if (cfunc_abiinfo & BIF_TAKES_VARPARM)
2827 #if defined(__arm__) || (defined (_M_ARM) && _M_ARM == 7)
2828 const int max_params=16384; // arm uses up to two instructions, should be good for at leaast 64k (16384*4)
2829 #elif defined(__ppc__)
2830 const int max_params=4096; // 32kb max offset addressing for stack, so 4096*4 = 16384, should be safe
2831 #elif defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
2832 const int max_params=32768;
2833 #else
2834 const int max_params=32768; // sanity check, the stack is free to grow on x86/x86-64
2835 #endif
2836 int x;
2837 // this mode is less efficient in that it creates a list of pointers on the stack to pass to the function
2838 // but it is more flexible and works for >3 parameters.
2839 if (op->opcodeType == OPCODETYPE_FUNCX)
2841 n_params=0;
2842 for (x=0;x<3;x++)
2844 opcodeRec *prni=op->parms.parms[x];
2845 while (prni)
2847 const int isMP = prni->opcodeType == OPCODETYPE_MOREPARAMS;
2848 n_params++;
2849 if (!isMP||n_params>=max_params) break;
2850 prni = prni->parms.parms[1];
2855 restore_stack_amt = (sizeof(void *) * n_params + 15)&~15;
2857 if (restore_stack_amt)
2859 int offs = restore_stack_amt;
2860 while (offs > 0)
2862 int amt = offs;
2863 if (amt > 4096) amt=4096;
2865 if (bufOut_len < parm_size+GLUE_MOVE_STACK_SIZE) RET_MINUS1_FAIL("insufficient size for varparm")
2866 if (bufOut) GLUE_MOVE_STACK(bufOut+parm_size, - amt);
2867 parm_size += GLUE_MOVE_STACK_SIZE;
2868 offs -= amt;
2870 if (offs>0) // make sure this page is in memory
2872 if (bufOut_len < parm_size+GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(0))
2873 RET_MINUS1_FAIL("insufficient size for varparm stackchk")
2874 if (bufOut) GLUE_STORE_P1_TO_STACK_AT_OFFS(bufOut+parm_size,0);
2875 parm_size += GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(0);
2880 if (op->opcodeType == OPCODETYPE_FUNCX)
2882 n_params=0;
2883 for (x=0;x<3;x++)
2885 opcodeRec *prni=op->parms.parms[x];
2886 while (prni)
2888 const int isMP = prni->opcodeType == OPCODETYPE_MOREPARAMS;
2889 opcodeRec *r = isMP ? prni->parms.parms[0] : prni;
2890 if (r)
2892 int canHaveDenorm=0;
2893 int rvt=RETURNVALUE_NORMAL;
2894 int subfpstackuse=0, use_offs;
2896 int lsz = compileOpcodes(ctx,r,bufOut ? bufOut + parm_size : NULL,bufOut_len - parm_size, computTableSize, namespacePathToThis, rvt,&rvt, &subfpstackuse, &canHaveDenorm);
2897 if (canHaveDenorm && canHaveDenormalOutput) *canHaveDenormalOutput = 1;
2899 if (lsz<0) RET_MINUS1_FAIL("call coc for varparmX failed")
2900 if (rvt != RETURNVALUE_NORMAL) RET_MINUS1_FAIL("call coc for varparmX gave bad type back");
2902 parm_size += lsz;
2903 use_offs = n_params*(int) sizeof(void *);
2905 if (bufOut_len < parm_size+GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(use_offs))
2906 RET_MINUS1_FAIL("call coc for varparmX size");
2907 if (bufOut) GLUE_STORE_P1_TO_STACK_AT_OFFS(bufOut + parm_size, use_offs);
2908 parm_size+=GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(use_offs);
2910 if (subfpstackuse+local_fpstack_use > *fpStackUsage) *fpStackUsage = subfpstackuse+local_fpstack_use;
2912 else RET_MINUS1_FAIL("zero parameter varparmX")
2914 n_params++;
2916 if (!isMP||n_params>=max_params) break;
2917 prni = prni->parms.parms[1];
2921 else for (x=0;x<n_params;x++)
2923 opcodeRec *r = op->parms.parms[x];
2924 if (r)
2926 int canHaveDenorm=0;
2927 int subfpstackuse=0;
2928 int rvt=RETURNVALUE_NORMAL;
2929 int use_offs;
2931 int lsz = compileOpcodes(ctx,r,bufOut ? bufOut + parm_size : NULL,bufOut_len - parm_size, computTableSize, namespacePathToThis, rvt,&rvt, &subfpstackuse, &canHaveDenorm);
2932 if (canHaveDenorm && canHaveDenormalOutput) *canHaveDenormalOutput = 1;
2934 if (lsz<0) RET_MINUS1_FAIL("call coc for varparm123 failed")
2935 if (rvt != RETURNVALUE_NORMAL) RET_MINUS1_FAIL("call coc for varparm123 gave bad type back");
2937 parm_size += lsz;
2939 use_offs = x*(int)sizeof(void *);
2940 if (bufOut_len < parm_size+GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(use_offs))
2941 RET_MINUS1_FAIL("call coc for varparm123 size");
2942 if (bufOut) GLUE_STORE_P1_TO_STACK_AT_OFFS(bufOut + parm_size, use_offs);
2943 parm_size+=GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(use_offs);
2945 if (subfpstackuse+local_fpstack_use > *fpStackUsage) *fpStackUsage = subfpstackuse+local_fpstack_use;
2947 else RET_MINUS1_FAIL("zero parameter for varparm123");
2950 if (bufOut_len < parm_size+GLUE_MOV_PX_DIRECTVALUE_SIZE+GLUE_MOVE_PX_STACKPTR_SIZE) RET_MINUS1_FAIL("insufficient size for varparm p1")
2951 if (bufOut) GLUE_MOV_PX_DIRECTVALUE_GEN(bufOut+parm_size, (INT_PTR)n_params,1);
2952 parm_size+=GLUE_MOV_PX_DIRECTVALUE_SIZE;
2953 if (bufOut) GLUE_MOVE_PX_STACKPTR_GEN(bufOut+parm_size, 0);
2954 parm_size+=GLUE_MOVE_PX_STACKPTR_SIZE;
2957 else // not varparm
2959 int pn;
2960 #if GLUE_MAX_FPSTACK_SIZE > 0
2961 int need_fxch=0;
2962 #endif
2963 int last_nt_parm=-1, last_nt_parm_type=-1;
2965 if (op->opcodeType == OPCODETYPE_FUNCX)
2967 // this is not yet supported (calling conventions will need to be sorted, among other things)
2968 RET_MINUS1_FAIL("funcx for native functions requires BIF_TAKES_VARPARM or BIF_TAKES_VARPARM_EX")
2971 if (parm0_dv)
2973 if (func == nseel_asm_stack_pop)
2975 func = GLUE_realAddress(nseel_asm_stack_pop_fast,&func_size);
2976 if (!func || bufOut_len < func_size) RET_MINUS1_FAIL(func?"failed on popfast size":"failed on popfast addr")
2978 if (bufOut)
2980 memcpy(bufOut,func,func_size);
2981 NSEEL_PProc_Stack(bufOut,func_size,ctx);
2983 return func_size;
2985 else if (func == nseel_asm_stack_peek)
2987 int f = (int) op->parms.parms[0]->parms.dv.directValue;
2988 if (!f)
2990 func = GLUE_realAddress(nseel_asm_stack_peek_top,&func_size);
2991 if (!func || bufOut_len < func_size) RET_MINUS1_FAIL(func?"failed on peek size":"failed on peek addr")
2993 if (bufOut)
2995 memcpy(bufOut,func,func_size);
2996 NSEEL_PProc_Stack_PeekTop(bufOut,func_size,ctx);
2998 return func_size;
3000 else
3002 func = GLUE_realAddress(nseel_asm_stack_peek_int,&func_size);
3003 if (!func || bufOut_len < func_size) RET_MINUS1_FAIL(func?"failed on peekint size":"failed on peekint addr")
3005 if (bufOut)
3007 memcpy(bufOut,func,func_size);
3008 NSEEL_PProc_Stack_PeekInt(bufOut,func_size,ctx,f*sizeof(EEL_F));
3010 return func_size;
3014 // end of built-in function specific special casing
3017 // first pass, calculate any non-trivial parameters
3018 for (pn=0; pn < n_params; pn++)
3020 if (!OPCODE_IS_TRIVIAL(op->parms.parms[pn]))
3022 int canHaveDenorm=0;
3023 int subfpstackuse=0;
3024 int lsz=0;
3025 int rvt=RETURNVALUE_NORMAL;
3026 int may_need_fppush=-1;
3027 if (last_nt_parm>=0)
3029 if (last_nt_parm_type==RETURNVALUE_FPSTACK)
3031 may_need_fppush= parm_size;
3033 else
3035 // push last result
3036 if (bufOut_len < parm_size + (int)sizeof(GLUE_PUSH_P1)) RET_MINUS1_FAIL("failed on size, pushp1")
3037 if (bufOut) memcpy(bufOut + parm_size, &GLUE_PUSH_P1, sizeof(GLUE_PUSH_P1));
3038 parm_size += sizeof(GLUE_PUSH_P1);
3042 if (func == nseel_asm_bnot) rvt=RETURNVALUE_BOOL_REVERSED|RETURNVALUE_BOOL;
3043 else if (pn == n_params - 1)
3045 if (cfunc_abiinfo&BIF_LASTPARMONSTACK) rvt=RETURNVALUE_FPSTACK;
3046 else if (cfunc_abiinfo&BIF_LASTPARM_ASBOOL) rvt=RETURNVALUE_BOOL;
3047 else if (func == nseel_asm_assign) rvt=RETURNVALUE_FPSTACK|RETURNVALUE_NORMAL;
3049 else if (pn == n_params -2 && (cfunc_abiinfo&BIF_SECONDLASTPARMST))
3051 rvt=RETURNVALUE_FPSTACK;
3054 lsz = compileOpcodes(ctx,op->parms.parms[pn],bufOut ? bufOut + parm_size : NULL,bufOut_len - parm_size, computTableSize, namespacePathToThis, rvt,&rvt, &subfpstackuse, &canHaveDenorm);
3056 if (lsz<0) RET_MINUS1_FAIL("call coc failed")
3058 if (func == nseel_asm_bnot && rvt==RETURNVALUE_BOOL_REVERSED)
3060 // remove bnot, compileOpcodes() used fptobool_rev
3061 func = NULL;
3062 rvt = RETURNVALUE_BOOL;
3065 if (canHaveDenorm && canHaveDenormalOutput) *canHaveDenormalOutput = 1;
3067 parm_size += lsz;
3069 if (may_need_fppush>=0)
3071 #if GLUE_MAX_SPILL_REGS > 0
3072 if (local_fpstack_use+subfpstackuse < GLUE_MAX_SPILL_REGS && pn == n_params - 1 && !(ctx->optimizeDisableFlags&OPTFLAG_NO_FPSTACK))
3074 int spill_sz;
3075 spill_reg = local_fpstack_use+subfpstackuse;
3076 spill_sz = GLUE_SAVE_TO_SPILL_SIZE(spill_reg);
3077 if (bufOut_len < parm_size + spill_sz) RET_MINUS1_FAIL("failed on size, savetospilll")
3079 if (bufOut)
3081 memmove(bufOut + may_need_fppush + spill_sz, bufOut + may_need_fppush, parm_size - may_need_fppush);
3082 GLUE_SAVE_TO_SPILL(bufOut + may_need_fppush, spill_reg);
3084 parm_size += spill_sz;
3085 local_fpstack_use++;
3087 else
3088 #endif
3089 if (local_fpstack_use+subfpstackuse >= (GLUE_MAX_FPSTACK_SIZE-1) || (ctx->optimizeDisableFlags&OPTFLAG_NO_FPSTACK))
3091 if (bufOut_len < parm_size + (int)sizeof(GLUE_POP_FPSTACK_TOSTACK))
3092 RET_MINUS1_FAIL("failed on size, popfpstacktostack")
3094 if (bufOut)
3096 memmove(bufOut + may_need_fppush + sizeof(GLUE_POP_FPSTACK_TOSTACK), bufOut + may_need_fppush, parm_size - may_need_fppush);
3097 memcpy(bufOut + may_need_fppush, &GLUE_POP_FPSTACK_TOSTACK, sizeof(GLUE_POP_FPSTACK_TOSTACK));
3100 parm_size += sizeof(GLUE_POP_FPSTACK_TOSTACK);
3102 else
3104 local_fpstack_use++;
3108 if (subfpstackuse+local_fpstack_use > *fpStackUsage) *fpStackUsage = subfpstackuse+local_fpstack_use;
3110 last_nt_parm = pn;
3111 last_nt_parm_type = rvt;
3113 if (pn == n_params - 1 && func == nseel_asm_assign)
3115 if (!(ctx->optimizeDisableFlags & OPTFLAG_FULL_DENORMAL_CHECKS) &&
3116 (!canHaveDenorm || (ctx->optimizeDisableFlags & OPTFLAG_NO_DENORMAL_CHECKS)))
3118 if (rvt == RETURNVALUE_FPSTACK)
3120 cfunc_abiinfo |= BIF_LASTPARMONSTACK;
3121 func = nseel_asm_assign_fast_fromfp;
3123 else
3125 func = nseel_asm_assign_fast;
3128 else
3130 if (rvt == RETURNVALUE_FPSTACK)
3132 cfunc_abiinfo |= BIF_LASTPARMONSTACK;
3133 func = nseel_asm_assign_fromfp;
3141 pn = last_nt_parm;
3143 if (pn >= 0) // if the last thing executed doesn't go to the last parameter, move it there
3145 if ((cfunc_abiinfo&BIF_SECONDLASTPARMST) && pn == n_params-2)
3147 // do nothing, things are in the right place
3149 else if (pn != n_params-1)
3151 // generate mov p1->pX
3152 if (bufOut_len < parm_size + GLUE_SET_PX_FROM_P1_SIZE) RET_MINUS1_FAIL("size, pxfromp1")
3153 if (bufOut) GLUE_SET_PX_FROM_P1(bufOut + parm_size,n_params - 1 - pn);
3154 parm_size += GLUE_SET_PX_FROM_P1_SIZE;
3158 // pop any pushed parameters
3159 while (--pn >= 0)
3161 if (!OPCODE_IS_TRIVIAL(op->parms.parms[pn]))
3163 if ((cfunc_abiinfo&BIF_SECONDLASTPARMST) && pn == n_params-2)
3165 if (!local_fpstack_use)
3167 #if GLUE_HAS_FPREG2 > 0
3168 if (bufOut_len < parm_size + (int)sizeof(GLUE_POP_STACK_TO_FPREG2)) RET_MINUS1_FAIL("size, popstacktofpstack2 2")
3169 if (bufOut) memcpy(bufOut+parm_size,GLUE_POP_STACK_TO_FPREG2,sizeof(GLUE_POP_STACK_TO_FPREG2));
3170 parm_size += sizeof(GLUE_POP_STACK_TO_FPREG2);
3171 #else
3172 if (bufOut_len < parm_size + (int)sizeof(GLUE_POP_STACK_TO_FPSTACK)) RET_MINUS1_FAIL("size, popstacktofpstack 2")
3173 if (bufOut) memcpy(bufOut+parm_size,GLUE_POP_STACK_TO_FPSTACK,sizeof(GLUE_POP_STACK_TO_FPSTACK));
3174 parm_size += sizeof(GLUE_POP_STACK_TO_FPSTACK);
3175 #endif
3176 #if GLUE_MAX_FPSTACK_SIZE > 0
3177 need_fxch = 1;
3178 #endif
3180 else
3182 #if GLUE_MAX_SPILL_REGS > 0
3183 if (spill_reg<0) RET_MINUS1_FAIL("spill reg not allocated");
3185 if (bufOut_len < parm_size + GLUE_RESTORE_SPILL_TO_FPREG2_SIZE(spill_reg)) RET_MINUS1_FAIL("size, copy fps to fpreg2")
3186 if (bufOut) GLUE_RESTORE_SPILL_TO_FPREG2(bufOut+parm_size,spill_reg);
3187 parm_size += GLUE_RESTORE_SPILL_TO_FPREG2_SIZE(spill_reg);
3188 #endif
3189 local_fpstack_use--;
3192 else
3194 if (bufOut_len < parm_size + GLUE_POP_PX_SIZE) RET_MINUS1_FAIL("size, poppx")
3195 if (bufOut) GLUE_POP_PX(bufOut + parm_size,n_params - 1 - pn);
3196 parm_size += GLUE_POP_PX_SIZE;
3201 // finally, set trivial pointers
3202 for (pn=0; pn < n_params; pn++)
3204 if (OPCODE_IS_TRIVIAL(op->parms.parms[pn]))
3206 if (pn == n_params-2 && (cfunc_abiinfo&(BIF_SECONDLASTPARMST))) // second to last parameter
3208 #if GLUE_HAS_FPREG2 > 0
3209 const int req = RETURNVALUE_FPREG2;
3210 #else
3211 const int req = RETURNVALUE_FPSTACK;
3212 #endif
3213 int a = compileOpcodes(ctx,op->parms.parms[pn],bufOut ? bufOut+parm_size : NULL,bufOut_len - parm_size,computTableSize,namespacePathToThis,
3214 req,NULL,NULL,canHaveDenormalOutput);
3215 if (a<0) RET_MINUS1_FAIL("coc call here 2")
3216 parm_size+=a;
3218 #if GLUE_MAX_FPSTACK_SIZE > 0
3219 need_fxch = 1;
3220 #endif
3222 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)
3224 int rvt=0, a;
3225 int wantFpStack = func == nseel_asm_assign;
3226 #ifdef GLUE_PREFER_NONFP_DV_ASSIGNS // x86-64, and maybe others, prefer to avoid the fp stack for a simple copy
3227 if (wantFpStack &&
3228 (op->parms.parms[pn]->opcodeType != OPCODETYPE_DIRECTVALUE ||
3229 op->parms.parms[pn]->parms.dv.directValue != 0.0))
3231 wantFpStack=-1; // cacheable but non-FP stack
3233 #endif
3234 #if GLUE_HAS_FPREG2 > 0
3235 if (pn > 0 && (cfunc_abiinfo&(BIF_SECONDLASTPARMST) && !OPCODE_IS_TRIVIAL(op->parms.parms[pn-1])))
3237 if (bufOut_len < parm_size+(int)sizeof(GLUE_COPY_FPSTACK_TO_FPREG2)) RET_MINUS1_FAIL("fptofpstack2tfp");
3238 if (bufOut) memcpy(bufOut+parm_size,GLUE_COPY_FPSTACK_TO_FPREG2,sizeof(GLUE_COPY_FPSTACK_TO_FPREG2));
3239 parm_size += sizeof(GLUE_COPY_FPSTACK_TO_FPREG2);
3241 #endif
3243 a = compileOpcodes(ctx,op->parms.parms[pn],bufOut ? bufOut+parm_size : NULL,bufOut_len - parm_size,computTableSize,namespacePathToThis,
3244 func == nseel_asm_bnot ? (RETURNVALUE_BOOL_REVERSED|RETURNVALUE_BOOL) :
3245 (cfunc_abiinfo & BIF_LASTPARMONSTACK) ? RETURNVALUE_FPSTACK :
3246 (cfunc_abiinfo & BIF_LASTPARM_ASBOOL) ? RETURNVALUE_BOOL :
3247 wantFpStack < 0 ? (RETURNVALUE_CACHEABLE|RETURNVALUE_NORMAL) :
3248 wantFpStack ? (RETURNVALUE_FPSTACK|RETURNVALUE_NORMAL) :
3249 RETURNVALUE_NORMAL,
3250 &rvt, NULL,canHaveDenormalOutput);
3252 if (a<0) RET_MINUS1_FAIL("coc call here 3")
3253 #ifdef GLUE_HAS_FUSE
3254 if (rvt == RETURNVALUE_FPSTACK) fuse_flags |= 1;
3255 #endif
3257 if (func == nseel_asm_bnot && rvt == RETURNVALUE_BOOL_REVERSED)
3259 // remove bnot, compileOpcodes() used fptobool_rev
3260 // case: !b ? 2 : 3, for example
3261 func = NULL;
3262 rvt = RETURNVALUE_BOOL;
3265 parm_size+=a;
3266 #if GLUE_MAX_FPSTACK_SIZE > 0
3267 need_fxch = 0;
3268 #endif
3270 if (func == nseel_asm_assign)
3272 if (rvt == RETURNVALUE_FPSTACK)
3274 if (!(ctx->optimizeDisableFlags & OPTFLAG_FULL_DENORMAL_CHECKS))
3276 func = nseel_asm_assign_fast_fromfp;
3278 else
3280 func = nseel_asm_assign_fromfp;
3283 else if (!(ctx->optimizeDisableFlags & OPTFLAG_FULL_DENORMAL_CHECKS))
3285 // assigning a value (from a variable or other non-computer), can use a fast assign (no denormal/result checking)
3286 func = nseel_asm_assign_fast;
3290 else
3292 if (bufOut_len < parm_size + GLUE_MOV_PX_DIRECTVALUE_SIZE) RET_MINUS1_FAIL("size, pxdvsz")
3293 if (bufOut)
3295 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")
3297 parm_size += GLUE_MOV_PX_DIRECTVALUE_SIZE;
3302 #if GLUE_MAX_FPSTACK_SIZE > 0
3303 if ((cfunc_abiinfo&(BIF_SECONDLASTPARMST)) && !(cfunc_abiinfo&(BIF_LAZYPARMORDERING))&&
3304 ((!!need_fxch)^!!(cfunc_abiinfo&BIF_REVERSEFPORDER))
3307 // emit fxch
3308 if (bufOut_len < sizeof(GLUE_FXCH)) RET_MINUS1_FAIL("len,fxch")
3309 if (bufOut)
3311 memcpy(bufOut+parm_size,GLUE_FXCH,sizeof(GLUE_FXCH));
3313 parm_size+=sizeof(GLUE_FXCH);
3315 #endif
3317 if (!*canHaveDenormalOutput)
3319 // if add_op or sub_op, and constant non-denormal input, safe to omit denormal checks
3320 if (func == (void*)nseel_asm_add_op && parm1_dv && fabs(op->parms.parms[1]->parms.dv.directValue) >= DENORMAL_CLEARING_THRESHOLD)
3322 func = nseel_asm_add_op_fast;
3324 else if (func == (void*)nseel_asm_sub_op && parm1_dv && fabs(op->parms.parms[1]->parms.dv.directValue) >= DENORMAL_CLEARING_THRESHOLD)
3326 func = nseel_asm_sub_op_fast;
3328 // or if mul/div by a fixed value of >= or <= 1.0
3329 else if (func == (void *)nseel_asm_mul_op && parm1_dv && fabs(op->parms.parms[1]->parms.dv.directValue) >= 1.0)
3331 func = nseel_asm_mul_op_fast;
3333 else if (func == (void *)nseel_asm_div_op && parm1_dv && fabs(op->parms.parms[1]->parms.dv.directValue) <= 1.0)
3335 func = nseel_asm_div_op_fast;
3338 } // not varparm
3340 if (cfunc_abiinfo & (BIF_CLEARDENORMAL | BIF_RETURNSBOOL) ) *canHaveDenormalOutput=0;
3341 else if (!(cfunc_abiinfo & BIF_WONTMAKEDENORMAL)) *canHaveDenormalOutput=1;
3343 if (func)
3345 func = GLUE_realAddress(func,&func_size);
3346 if (!func) RET_MINUS1_FAIL("failrealladdrfunc")
3348 else
3350 func_size = 0;
3353 if (bufOut_len < parm_size + func_size) RET_MINUS1_FAIL("funcsz")
3355 if (bufOut)
3357 unsigned char *p=bufOut + parm_size;
3358 memcpy(p, func, func_size);
3359 #if GLUE_HAS_FUSE
3360 parm_size += GLUE_FUSE(ctx,p,parm_size,func_size,fuse_flags,
3361 #if GLUE_MAX_SPILL_REGS > 0
3362 spill_reg
3363 #else
3365 #endif
3367 p = bufOut + parm_size;
3368 #endif
3369 if (preProc) p=preProc(p,func_size,ctx);
3370 if (repl)
3372 if (repl[0]) p=EEL_GLUE_set_immediate(p,(INT_PTR)repl[0]);
3373 if (repl[1]) p=EEL_GLUE_set_immediate(p,(INT_PTR)repl[1]);
3374 if (repl[2]) p=EEL_GLUE_set_immediate(p,(INT_PTR)repl[2]);
3375 if (repl[3]) p=EEL_GLUE_set_immediate(p,(INT_PTR)repl[3]);
3379 if (restore_stack_amt)
3381 int rem = restore_stack_amt;
3382 while (rem > 0)
3384 int amt = rem;
3385 if (amt > 4096) amt=4096;
3386 rem -= amt;
3388 if (bufOut_len < parm_size + func_size + GLUE_MOVE_STACK_SIZE) RET_MINUS1_FAIL("insufficient size for varparm")
3389 if (bufOut) GLUE_MOVE_STACK(bufOut + parm_size + func_size, amt);
3390 parm_size += GLUE_MOVE_STACK_SIZE;
3394 if (cfunc_abiinfo&BIF_RETURNSONSTACK) *rvMode = RETURNVALUE_FPSTACK;
3395 else if (cfunc_abiinfo&BIF_RETURNSBOOL) *rvMode=RETURNVALUE_BOOL;
3397 return parm_size + func_size;
3400 static int compileEelFunctionCall(compileContext *ctx, opcodeRec *op, unsigned char *bufOut, int bufOut_len, int *computTableSize, const namespaceInformation *namespacePathToThis,
3401 int *rvMode, int *fpStackUse, int *canHaveDenormalOutput)
3403 int func_size=0, parm_size=0;
3404 int pn;
3405 int last_nt_parm=-1,last_nt_parm_mode=0;
3406 void *func_e=NULL;
3407 int n_params;
3408 opcodeRec *parmptrs[NSEEL_MAX_EELFUNC_PARAMETERS];
3409 int cfp_numparams=-1;
3410 int cfp_statesize=0;
3411 EEL_F **cfp_ptrs=NULL;
3412 int func_raw=0;
3413 int do_parms;
3414 int x;
3416 void *func;
3418 for (x=0; x < 3; x ++) parmptrs[x] = op->parms.parms[x];
3420 if (op->opcodeType == OPCODETYPE_FUNCX)
3422 n_params=0;
3423 for (x=0;x<3;x++)
3425 opcodeRec *prni=op->parms.parms[x];
3426 while (prni && n_params < NSEEL_MAX_EELFUNC_PARAMETERS)
3428 const int isMP = prni->opcodeType == OPCODETYPE_MOREPARAMS;
3429 parmptrs[n_params++] = isMP ? prni->parms.parms[0] : prni;
3430 if (!isMP) break;
3431 prni = prni->parms.parms[1];
3435 else
3437 n_params = 1 + op->opcodeType - OPCODETYPE_FUNC1;
3440 *fpStackUse = 0;
3441 func = nseel_getEELFunctionAddress(ctx, op,
3442 &cfp_numparams,&cfp_statesize,&cfp_ptrs,
3443 computTableSize,
3444 &func_e, &func_raw,
3445 !!bufOut,namespacePathToThis,rvMode,fpStackUse,canHaveDenormalOutput, parmptrs, n_params);
3447 if (func_raw) func_size = (int) ((char*)func_e - (char*)func);
3448 else if (func) func = GLUE_realAddress(func,&func_size);
3450 if (!func) RET_MINUS1_FAIL("eelfuncaddr")
3452 #if GLUE_MAX_FPSTACK_SIZE > 0
3453 *fpStackUse += 1;
3454 #endif
3456 if (cfp_numparams>0 && n_params != cfp_numparams)
3458 RET_MINUS1_FAIL("eelfuncnp")
3461 // user defined function
3462 do_parms = cfp_numparams>0 && cfp_ptrs && cfp_statesize>0;
3464 // if function local/parameter state is zero, we need to allocate storage for it
3465 if (cfp_statesize>0 && cfp_ptrs && !cfp_ptrs[0])
3467 EEL_F *pstate = newDataBlock(sizeof(EEL_F)*cfp_statesize,8);
3468 if (!pstate) RET_MINUS1_FAIL("eelfuncdb")
3470 for (pn=0;pn<cfp_statesize;pn++)
3472 pstate[pn]=0;
3473 cfp_ptrs[pn] = pstate + pn;
3478 // first process parameters that are non-trivial
3479 for (pn=0; pn < n_params; pn++)
3481 int needDenorm=0;
3482 int lsz,sUse=0;
3484 if (!parmptrs[pn] || OPCODE_IS_TRIVIAL(parmptrs[pn])) continue; // skip and process after
3486 if (last_nt_parm >= 0 && do_parms)
3488 if (last_nt_parm_mode == RETURNVALUE_FPSTACK)
3490 if (bufOut_len < parm_size + (int)sizeof(GLUE_POP_FPSTACK_TOSTACK)) RET_MINUS1_FAIL("eelfunc_size popfpstacktostack")
3491 if (bufOut) memcpy(bufOut + parm_size,GLUE_POP_FPSTACK_TOSTACK,sizeof(GLUE_POP_FPSTACK_TOSTACK));
3492 parm_size+=sizeof(GLUE_POP_FPSTACK_TOSTACK);
3494 else
3496 if (bufOut_len < parm_size + (int)sizeof(GLUE_PUSH_P1PTR_AS_VALUE)) RET_MINUS1_FAIL("eelfunc_size pushp1ptrasval")
3498 // push
3499 if (bufOut) memcpy(bufOut + parm_size,&GLUE_PUSH_P1PTR_AS_VALUE,sizeof(GLUE_PUSH_P1PTR_AS_VALUE));
3500 parm_size+=sizeof(GLUE_PUSH_P1PTR_AS_VALUE);
3504 last_nt_parm_mode=0;
3505 lsz = compileOpcodes(ctx,parmptrs[pn],bufOut ? bufOut + parm_size : NULL,bufOut_len - parm_size, computTableSize, namespacePathToThis,
3506 do_parms ? (RETURNVALUE_FPSTACK|RETURNVALUE_NORMAL) : RETURNVALUE_IGNORE,&last_nt_parm_mode,&sUse, &needDenorm);
3508 // todo: if needDenorm, denorm convert when copying parameter
3510 if (lsz<0) RET_MINUS1_FAIL("eelfunc, coc fail")
3512 if (last_nt_parm_mode == RETURNVALUE_FPSTACK) sUse++;
3513 if (sUse > *fpStackUse) *fpStackUse=sUse;
3514 parm_size += lsz;
3516 last_nt_parm = pn;
3518 // pop non-trivial results into place
3519 if (last_nt_parm >=0 && do_parms)
3521 while (--pn >= 0)
3523 if (!parmptrs[pn] || OPCODE_IS_TRIVIAL(parmptrs[pn])) continue; // skip and process after
3524 if (pn == last_nt_parm)
3526 if (last_nt_parm_mode == RETURNVALUE_FPSTACK)
3528 // pop to memory directly
3529 const int cpsize = GLUE_POP_FPSTACK_TO_PTR(NULL,NULL);
3530 if (bufOut_len < parm_size + cpsize) RET_MINUS1_FAIL("eelfunc size popfpstacktoptr")
3532 if (bufOut) GLUE_POP_FPSTACK_TO_PTR((unsigned char *)bufOut + parm_size,cfp_ptrs[pn]);
3533 parm_size += cpsize;
3535 else
3537 // copy direct p1ptr to mem
3538 const int cpsize = GLUE_COPY_VALUE_AT_P1_TO_PTR(NULL,NULL);
3539 if (bufOut_len < parm_size + cpsize) RET_MINUS1_FAIL("eelfunc size copyvalueatp1toptr")
3541 if (bufOut) GLUE_COPY_VALUE_AT_P1_TO_PTR((unsigned char *)bufOut + parm_size,cfp_ptrs[pn]);
3542 parm_size += cpsize;
3545 else
3547 const int popsize = GLUE_POP_VALUE_TO_ADDR(NULL,NULL);
3548 if (bufOut_len < parm_size + popsize) RET_MINUS1_FAIL("eelfunc size pop value to addr")
3550 if (bufOut) GLUE_POP_VALUE_TO_ADDR((unsigned char *)bufOut + parm_size,cfp_ptrs[pn]);
3551 parm_size+=popsize;
3557 // finally, set any trivial parameters
3558 if (do_parms)
3560 const int cpsize = GLUE_MOV_PX_DIRECTVALUE_SIZE + GLUE_COPY_VALUE_AT_P1_TO_PTR(NULL,NULL);
3561 for (pn=0; pn < n_params; pn++)
3563 if (!parmptrs[pn] || !OPCODE_IS_TRIVIAL(parmptrs[pn])) continue; // set trivial values, we already set nontrivials
3565 if (bufOut_len < parm_size + cpsize) RET_MINUS1_FAIL("eelfunc size trivial set")
3567 if (bufOut)
3569 if (generateValueToReg(ctx,parmptrs[pn],bufOut + parm_size,0,namespacePathToThis, 1)<0) RET_MINUS1_FAIL("eelfunc gvr fail")
3570 GLUE_COPY_VALUE_AT_P1_TO_PTR(bufOut + parm_size + GLUE_MOV_PX_DIRECTVALUE_SIZE,cfp_ptrs[pn]);
3572 parm_size += cpsize;
3577 if (bufOut_len < parm_size + func_size) RET_MINUS1_FAIL("eelfunc size combined")
3579 if (bufOut) memcpy(bufOut + parm_size, func, func_size);
3581 return parm_size + func_size;
3582 // end of EEL function generation
3585 #ifdef DUMP_OPS_DURING_COMPILE
3586 void dumpOp(compileContext *ctx, opcodeRec *op, int start);
3587 #endif
3589 #ifdef EEL_DUMP_OPS
3590 void dumpOpcodeTree(compileContext *ctx, FILE *fp, opcodeRec *op, int indent_amt)
3592 const char *fname="";
3593 fprintf(fp,"%*sOP TYPE %d", indent_amt, "",
3594 op->opcodeType==OPCODETYPE_DIRECTVALUE_TEMPSTRING ? 10000 : // remap around OPCODETYPE_DIRECTVALUE_TEMPSTRING
3595 op->opcodeType > OPCODETYPE_DIRECTVALUE_TEMPSTRING ? op->opcodeType - 1 :
3596 op->opcodeType);
3598 if ((op->opcodeType == OPCODETYPE_FUNC1 ||
3599 op->opcodeType == OPCODETYPE_FUNC2 ||
3600 op->opcodeType == OPCODETYPE_FUNC3 ||
3601 op->opcodeType == OPCODETYPE_FUNCX))
3603 if (op->fntype == FUNCTYPE_FUNCTIONTYPEREC)
3605 functionType *fn_ptr = (functionType *)op->fn;
3606 fname = fn_ptr->name;
3608 else if (op->fntype == FUNCTYPE_EELFUNC)
3610 fname = op->relname;
3612 if (!fname) fname ="";
3615 switch (op->opcodeType)
3617 case OPCODETYPE_DIRECTVALUE:
3618 fprintf(fp," DV=%f\r\n",op->parms.dv.directValue);
3619 break;
3620 case OPCODETYPE_VALUE_FROM_NAMESPACENAME: // this.* or namespace.* are encoded this way
3621 fprintf(fp," NSN=%s(%d)\r\n",op->relname?op->relname : "(null)",op->namespaceidx);
3622 break;
3623 case OPCODETYPE_VARPTR:
3625 const char *nm = op->relname;
3626 if (!nm || !*nm)
3628 int wb;
3629 for (wb = 0; wb < ctx->varTable_numBlocks; wb ++)
3631 char **plist=ctx->varTable_Names[wb];
3632 if (!plist) break;
3634 if (op->parms.dv.valuePtr >= ctx->varTable_Values[wb] && op->parms.dv.valuePtr < ctx->varTable_Values[wb] + NSEEL_VARS_PER_BLOCK)
3636 nm = plist[op->parms.dv.valuePtr - ctx->varTable_Values[wb]];
3637 break;
3641 fprintf(fp," VP=%s\r\n", nm?nm : "(null)");
3643 break;
3644 case OPCODETYPE_VARPTRPTR:
3645 fprintf(fp, " VPP?\r\n");
3646 break;
3647 case OPCODETYPE_FUNC1:
3648 if (op->fntype == FN_NOT)
3649 fprintf(fp," FUNC1 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_not");
3650 else if (op->fntype == FN_NOTNOT)
3651 fprintf(fp," FUNC1 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_notnot");
3652 else if (op->fntype == FN_MEMORY)
3653 fprintf(fp," FUNC1 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_mem");
3654 else if (op->fntype == FN_GMEMORY)
3655 fprintf(fp," FUNC1 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_gmem");
3656 else if (op->fntype == FN_WHILE)
3657 fprintf(fp," FUNC1 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "while");
3658 else
3659 fprintf(fp," FUNC1 %d %s {\r\n",op->fntype, fname);
3661 if (op->parms.parms[0])
3662 dumpOpcodeTree(ctx,fp,op->parms.parms[0],indent_amt+2);
3663 else
3664 fprintf(fp,"%*sINVALID PARM\r\n",indent_amt+2,"");
3665 fprintf(fp,"%*s}\r\n", indent_amt, "");
3666 break;
3667 case OPCODETYPE_MOREPARAMS:
3668 case OPCODETYPE_FUNC2:
3669 if (op->opcodeType == OPCODETYPE_MOREPARAMS)
3670 fprintf(fp," MOREPARAMS {\r\n");
3671 else
3673 if (op->fntype == FN_POW)
3674 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "pow");
3675 else if (op->fntype == FN_MOD)
3676 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_mod");
3677 else if (op->fntype == FN_XOR)
3678 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_xor");
3679 else if (op->fntype == FN_SHL)
3680 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_shl");
3681 else if (op->fntype == FN_SHR)
3682 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_shr");
3683 else if (op->fntype == FN_LT)
3684 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_below");
3685 else if (op->fntype == FN_GT)
3686 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_above");
3687 else if (op->fntype == FN_LTE)
3688 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_beleq");
3689 else if (op->fntype == FN_GTE)
3690 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_aboeq");
3691 else if (op->fntype == FN_EQ)
3692 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_equal");
3693 else if (op->fntype == FN_NE)
3694 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_noteq");
3695 else if (op->fntype == FN_EQ_EXACT)
3696 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_equal_exact");
3697 else if (op->fntype == FN_NE_EXACT)
3698 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_noteq_exact");
3699 else if (op->fntype == FN_LOGICAL_AND)
3700 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_and");
3701 else if (op->fntype == FN_LOGICAL_OR)
3702 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_or");
3703 else if (op->fntype == FN_ASSIGN)
3704 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_set");
3705 else if (op->fntype == FN_ADD_OP)
3706 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_addop");
3707 else if (op->fntype == FN_SUB_OP)
3708 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_subop");
3709 else if (op->fntype == FN_MUL_OP)
3710 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_mulop");
3711 else if (op->fntype == FN_DIV_OP)
3712 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_divop");
3713 else if (op->fntype == FN_OR_OP)
3714 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_orop");
3715 else if (op->fntype == FN_AND_OP)
3716 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_andop");
3717 else if (op->fntype == FN_XOR_OP)
3718 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_xorop");
3719 else if (op->fntype == FN_MOD_OP)
3720 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_modop");
3721 else if (op->fntype == FN_POW_OP)
3722 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_powop");
3723 else if (op->fntype == FN_LOOP)
3724 fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "loop");
3725 else
3726 fprintf(fp," FUNC2 %d %s {\r\n",op->fntype, fname);
3728 if (op->parms.parms[0])
3729 dumpOpcodeTree(ctx,fp,op->parms.parms[0],indent_amt+2);
3730 else
3731 fprintf(fp,"%*sINVALID PARM\r\n",indent_amt+2,"");
3733 if (op->parms.parms[1])
3734 dumpOpcodeTree(ctx,fp,op->parms.parms[1],indent_amt+2);
3735 else
3736 fprintf(fp,"%*sINVALID PARM\r\n",indent_amt+2,"");
3737 fprintf(fp,"%*s}\r\n", indent_amt, "");
3738 break;
3739 case OPCODETYPE_FUNCX:
3740 case OPCODETYPE_FUNC3:
3741 if (op->opcodeType == OPCODETYPE_FUNCX)
3742 fprintf(fp," FUNCX %d %s {\r\n",op->fntype, fname);
3743 else if (op->fntype == FN_IF_ELSE)
3744 fprintf(fp," FUNC3 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_if");
3745 else
3746 fprintf(fp," FUNC3 %d %s {\r\n",op->fntype, fname);
3747 if (op->parms.parms[0])
3748 dumpOpcodeTree(ctx,fp,op->parms.parms[0],indent_amt+2);
3749 else
3750 fprintf(fp,"%*sINVALID PARM\r\n",indent_amt+2,"");
3752 if (op->parms.parms[1])
3753 dumpOpcodeTree(ctx,fp,op->parms.parms[1],indent_amt+2);
3754 else
3755 fprintf(fp,"%*sINVALID PARM\r\n",indent_amt+2,"");
3757 if (op->parms.parms[2])
3758 dumpOpcodeTree(ctx,fp,op->parms.parms[2],indent_amt+2);
3759 else
3760 fprintf(fp,"%*sINVALID PARM\r\n",indent_amt+2,"");
3761 fprintf(fp,"%*s}\r\n", indent_amt, "");
3763 break;
3767 #endif
3769 #ifdef GLUE_MAX_JMPSIZE
3770 #define CHECK_SIZE_FORJMP(x,y) if ((x)<0 || (x)>=GLUE_MAX_JMPSIZE) goto y;
3771 #define RET_MINUS1_FAIL_FALLBACK(err,j) goto j;
3772 #else
3773 #define CHECK_SIZE_FORJMP(x,y)
3774 #define RET_MINUS1_FAIL_FALLBACK(err,j) RET_MINUS1_FAIL(err)
3775 #endif
3776 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)
3778 int rv_offset=0, denormal_force=-1;
3779 if (!op) RET_MINUS1_FAIL("coi !op")
3781 *fpStackUse=0;
3782 for (;;)
3784 // special case: statement delimiting means we can process the left side into place, and iteratively do the second parameter without recursing
3785 // also we don't need to save/restore anything to the stack (which the normal 2 parameter function processing does)
3786 if (op->opcodeType == OPCODETYPE_FUNC2 && op->fntype == FN_JOIN_STATEMENTS)
3788 int fUse1;
3789 int parm_size = compileOpcodes(ctx,op->parms.parms[0],bufOut,bufOut_len, computTableSize, namespacePathToThis, RETURNVALUE_IGNORE, NULL,&fUse1,NULL);
3790 if (parm_size < 0) RET_MINUS1_FAIL("coc join fail")
3791 op = op->parms.parms[1];
3792 if (!op) RET_MINUS1_FAIL("join got to null")
3794 if (fUse1>*fpStackUse) *fpStackUse=fUse1;
3795 if (bufOut) bufOut += parm_size;
3796 bufOut_len -= parm_size;
3797 rv_offset += parm_size;
3798 #ifdef DUMP_OPS_DURING_COMPILE
3799 if (op->opcodeType != OPCODETYPE_FUNC2 || op->fntype != FN_JOIN_STATEMENTS) dumpOp(ctx,op,0);
3800 #endif
3801 denormal_force=-1;
3803 // special case: __denormal_likely(), __denormal_unlikely()
3804 else if (op->opcodeType == OPCODETYPE_FUNC1 && (op->fntype == FN_DENORMAL_LIKELY || op->fntype == FN_DENORMAL_UNLIKELY))
3806 denormal_force = op->fntype == FN_DENORMAL_LIKELY;
3807 op = op->parms.parms[0];
3809 else
3811 break;
3814 if (denormal_force >= 0 && canHaveDenormalOutput)
3816 *canHaveDenormalOutput = denormal_force;
3817 canHaveDenormalOutput = &denormal_force; // prevent it from being changed by functions below
3820 // special case: BAND/BOR
3821 if (op->opcodeType == OPCODETYPE_FUNC2 && (op->fntype == FN_LOGICAL_AND || op->fntype == FN_LOGICAL_OR))
3823 int fUse1=0;
3824 int parm_size;
3825 #ifdef GLUE_MAX_JMPSIZE
3826 int parm_size_pre;
3827 #endif
3828 int retType=RETURNVALUE_IGNORE;
3829 if (preferredReturnValues != RETURNVALUE_IGNORE) retType = RETURNVALUE_BOOL;
3831 *calledRvType = retType;
3833 parm_size = compileOpcodes(ctx,op->parms.parms[0],bufOut,bufOut_len, computTableSize, namespacePathToThis, RETURNVALUE_BOOL, NULL, &fUse1, NULL);
3834 if (parm_size < 0) RET_MINUS1_FAIL("loop band/bor coc fail")
3836 if (fUse1 > *fpStackUse) *fpStackUse=fUse1;
3839 #ifdef GLUE_MAX_JMPSIZE
3840 parm_size_pre=parm_size;
3841 #endif
3844 int sz2, fUse2=0;
3845 const int testsz=op->fntype == FN_LOGICAL_OR ? sizeof(GLUE_JMP_IF_P1_NZ) : sizeof(GLUE_JMP_IF_P1_Z);
3846 if (bufOut_len < parm_size+testsz) RET_MINUS1_FAIL_FALLBACK("band/bor size fail",doNonInlinedAndOr_)
3848 if (bufOut) memcpy(bufOut+parm_size,op->fntype == FN_LOGICAL_OR ? GLUE_JMP_IF_P1_NZ : GLUE_JMP_IF_P1_Z,testsz);
3849 parm_size += testsz;
3851 sz2 = compileOpcodes(ctx,op->parms.parms[1],bufOut?bufOut+parm_size:NULL,bufOut_len-parm_size, computTableSize, namespacePathToThis, retType, NULL,&fUse2, NULL);
3853 CHECK_SIZE_FORJMP(sz2,doNonInlinedAndOr_)
3854 if (sz2<0) RET_MINUS1_FAIL("band/bor coc fail")
3856 if (bufOut) GLUE_JMP_SET_OFFSET(bufOut + parm_size, sz2);
3857 parm_size+=sz2;
3859 if (fUse2 > *fpStackUse) *fpStackUse=fUse2;
3860 return rv_offset + parm_size;
3862 #ifdef GLUE_MAX_JMPSIZE
3863 if (0)
3865 void *stub;
3866 int stubsize;
3867 unsigned char *newblock2, *p;
3869 // encode as function call
3870 doNonInlinedAndOr_:
3871 parm_size = parm_size_pre;
3873 if (op->fntype == FN_LOGICAL_AND)
3875 stub = GLUE_realAddress(nseel_asm_band,&stubsize);
3877 else
3879 stub = GLUE_realAddress(nseel_asm_bor,&stubsize);
3882 if (bufOut_len < parm_size + stubsize) RET_MINUS1_FAIL("band/bor len fail")
3884 if (bufOut)
3886 int fUse2=0;
3887 newblock2 = compileCodeBlockWithRet(ctx,op->parms.parms[1],computTableSize,namespacePathToThis, retType, NULL, &fUse2, NULL);
3888 if (!newblock2) RET_MINUS1_FAIL("band/bor ccbwr fail")
3890 if (fUse2 > *fpStackUse) *fpStackUse=fUse2;
3892 p = bufOut + parm_size;
3893 memcpy(p, stub, stubsize);
3895 p=EEL_GLUE_set_immediate(p,(INT_PTR)newblock2);
3897 return rv_offset + parm_size + stubsize;
3899 #endif
3902 if (op->opcodeType == OPCODETYPE_FUNC3 && op->fntype == FN_IF_ELSE) // special case: IF
3904 int fUse1=0;
3905 #ifdef GLUE_MAX_JMPSIZE
3906 int parm_size_pre;
3907 #endif
3908 int use_rv = RETURNVALUE_IGNORE;
3909 int rvMode=0;
3910 int parm_size = compileOpcodes(ctx,op->parms.parms[0],bufOut,bufOut_len, computTableSize, namespacePathToThis, RETURNVALUE_BOOL|RETURNVALUE_BOOL_REVERSED, &rvMode,&fUse1, NULL);
3911 if (parm_size < 0) RET_MINUS1_FAIL("if coc fail")
3912 if (fUse1 > *fpStackUse) *fpStackUse=fUse1;
3914 if (preferredReturnValues & RETURNVALUE_NORMAL) use_rv=RETURNVALUE_NORMAL;
3915 else if (preferredReturnValues & RETURNVALUE_FPSTACK) use_rv=RETURNVALUE_FPSTACK;
3916 else if (preferredReturnValues & RETURNVALUE_BOOL) use_rv=RETURNVALUE_BOOL;
3918 *calledRvType = use_rv;
3919 #ifdef GLUE_MAX_JMPSIZE
3920 parm_size_pre = parm_size;
3921 #endif
3924 int csz,hasSecondHalf;
3925 if (rvMode & RETURNVALUE_BOOL_REVERSED)
3927 if (bufOut_len < parm_size + (int)sizeof(GLUE_JMP_IF_P1_NZ)) RET_MINUS1_FAIL_FALLBACK("if size fail",doNonInlineIf_)
3928 if (bufOut) memcpy(bufOut+parm_size,GLUE_JMP_IF_P1_NZ,sizeof(GLUE_JMP_IF_P1_NZ));
3929 parm_size += sizeof(GLUE_JMP_IF_P1_NZ);
3931 else
3933 if (bufOut_len < parm_size + (int)sizeof(GLUE_JMP_IF_P1_Z)) RET_MINUS1_FAIL_FALLBACK("if size fail",doNonInlineIf_)
3934 if (bufOut) memcpy(bufOut+parm_size,GLUE_JMP_IF_P1_Z,sizeof(GLUE_JMP_IF_P1_Z));
3935 parm_size += sizeof(GLUE_JMP_IF_P1_Z);
3937 csz=compileOpcodes(ctx,op->parms.parms[1],bufOut ? bufOut+parm_size : NULL,bufOut_len - parm_size, computTableSize, namespacePathToThis, use_rv, NULL,&fUse1, canHaveDenormalOutput);
3938 if (fUse1 > *fpStackUse) *fpStackUse=fUse1;
3939 hasSecondHalf = preferredReturnValues || !OPCODE_IS_TRIVIAL(op->parms.parms[2]);
3941 CHECK_SIZE_FORJMP(csz,doNonInlineIf_)
3942 if (csz<0) RET_MINUS1_FAIL("if coc fial")
3944 if (bufOut) GLUE_JMP_SET_OFFSET(bufOut + parm_size, csz + (hasSecondHalf?sizeof(GLUE_JMP_NC):0));
3945 parm_size+=csz;
3947 if (hasSecondHalf)
3949 if (bufOut_len < parm_size + (int)sizeof(GLUE_JMP_NC)) RET_MINUS1_FAIL_FALLBACK("if len fail",doNonInlineIf_)
3950 if (bufOut) memcpy(bufOut+parm_size,GLUE_JMP_NC,sizeof(GLUE_JMP_NC));
3951 parm_size+=sizeof(GLUE_JMP_NC);
3953 csz=compileOpcodes(ctx,op->parms.parms[2],bufOut ? bufOut+parm_size : NULL,bufOut_len - parm_size, computTableSize, namespacePathToThis, use_rv, NULL, &fUse1, canHaveDenormalOutput);
3955 CHECK_SIZE_FORJMP(csz,doNonInlineIf_)
3956 if (csz<0) RET_MINUS1_FAIL("if coc 2 fail")
3958 // update jump address
3959 if (bufOut) GLUE_JMP_SET_OFFSET(bufOut + parm_size,csz);
3960 parm_size+=csz;
3961 if (fUse1 > *fpStackUse) *fpStackUse=fUse1;
3963 return rv_offset + parm_size;
3965 #ifdef GLUE_MAX_JMPSIZE
3966 if (0)
3968 unsigned char *newblock2,*newblock3,*ptr;
3969 void *stub;
3970 int stubsize;
3971 doNonInlineIf_:
3972 parm_size = parm_size_pre;
3973 stub = GLUE_realAddress(nseel_asm_if,&stubsize);
3975 if (!stub || bufOut_len < parm_size + stubsize) RET_MINUS1_FAIL(stub ? "if sz fail" : "if addr fail")
3977 if (bufOut)
3979 int fUse2=0;
3980 newblock2 = compileCodeBlockWithRet(ctx,op->parms.parms[1],computTableSize,namespacePathToThis, use_rv, NULL,&fUse2, canHaveDenormalOutput);
3981 if (fUse2 > *fpStackUse) *fpStackUse=fUse2;
3982 newblock3 = compileCodeBlockWithRet(ctx,op->parms.parms[2],computTableSize,namespacePathToThis, use_rv, NULL,&fUse2, canHaveDenormalOutput);
3983 if (fUse2 > *fpStackUse) *fpStackUse=fUse2;
3984 if (!newblock2 || !newblock3) RET_MINUS1_FAIL("if subblock gen fail")
3986 ptr = bufOut + parm_size;
3987 memcpy(ptr, stub, stubsize);
3989 ptr=EEL_GLUE_set_immediate(ptr,(INT_PTR)newblock2);
3990 EEL_GLUE_set_immediate(ptr,(INT_PTR)newblock3);
3992 return rv_offset + parm_size + stubsize;
3994 #endif
3998 // special case: while
3999 if (op->opcodeType == OPCODETYPE_FUNC1 && op->fntype == FN_WHILE)
4001 *calledRvType = RETURNVALUE_BOOL;
4003 #ifndef GLUE_INLINE_LOOPS
4004 // todo: PPC looping support when loop length is small enough
4006 unsigned char *pwr=bufOut;
4007 unsigned char *newblock2;
4008 int stubsz;
4009 void *stubfunc = GLUE_realAddress(nseel_asm_repeatwhile,&stubsz);
4010 if (!stubfunc || bufOut_len < stubsz) RET_MINUS1_FAIL(stubfunc ? "repeatwhile size fail" :"repeatwhile addr fail")
4012 if (bufOut)
4014 int fUse1 = 0;
4015 newblock2=compileCodeBlockWithRet(ctx,op->parms.parms[0],computTableSize,namespacePathToThis, RETURNVALUE_BOOL, NULL, &fUse1, NULL);
4016 if (!newblock2) RET_MINUS1_FAIL("repeatwhile ccbwr fail")
4018 if (fUse1 > *fpStackUse) *fpStackUse = fUse1;
4019 memcpy(pwr,stubfunc,stubsz);
4020 pwr=EEL_GLUE_set_immediate(pwr,(INT_PTR)newblock2);
4023 return rv_offset+stubsz;
4025 #else
4027 #ifndef GLUE_WHILE_END_NOJUMP
4028 unsigned char *jzoutpt;
4029 #endif
4030 unsigned char *looppt;
4031 int parm_size=0,subsz, fUse1=0;
4032 if (bufOut_len < parm_size + (int)(GLUE_WHILE_SETUP_SIZE + sizeof(GLUE_WHILE_BEGIN))) RET_MINUS1_FAIL("while size fail 1")
4034 if (bufOut) memcpy(bufOut + parm_size,GLUE_WHILE_SETUP,GLUE_WHILE_SETUP_SIZE);
4035 parm_size+=GLUE_WHILE_SETUP_SIZE;
4037 looppt = bufOut + parm_size;
4038 if (bufOut) memcpy(bufOut + parm_size,GLUE_WHILE_BEGIN,sizeof(GLUE_WHILE_BEGIN));
4039 parm_size+=sizeof(GLUE_WHILE_BEGIN);
4041 subsz = compileOpcodes(ctx,op->parms.parms[0],bufOut ? (bufOut + parm_size) : NULL,bufOut_len - parm_size, computTableSize, namespacePathToThis, RETURNVALUE_BOOL, NULL,&fUse1, NULL);
4042 if (fUse1 > *fpStackUse) *fpStackUse = fUse1;
4043 if (subsz<0) RET_MINUS1_FAIL("while coc fail")
4045 if (bufOut_len < parm_size + (int)(sizeof(GLUE_WHILE_END) + sizeof(GLUE_WHILE_CHECK_RV))) RET_MINUS1_FAIL("which size fial 2")
4047 parm_size+=subsz;
4048 if (bufOut) memcpy(bufOut + parm_size, GLUE_WHILE_END, sizeof(GLUE_WHILE_END));
4049 parm_size+=sizeof(GLUE_WHILE_END);
4050 #ifndef GLUE_WHILE_END_NOJUMP
4051 jzoutpt = bufOut + parm_size;
4052 #endif
4054 if (bufOut) memcpy(bufOut + parm_size, GLUE_WHILE_CHECK_RV, sizeof(GLUE_WHILE_CHECK_RV));
4055 parm_size+=sizeof(GLUE_WHILE_CHECK_RV);
4056 if (bufOut)
4058 GLUE_JMP_SET_OFFSET(bufOut + parm_size,(looppt - (bufOut+parm_size)) );
4059 #ifndef GLUE_WHILE_END_NOJUMP
4060 GLUE_JMP_SET_OFFSET(jzoutpt, (bufOut + parm_size) - jzoutpt);
4061 #endif
4063 return rv_offset+parm_size;
4066 #endif
4069 // special case: loop
4070 if (op->opcodeType == OPCODETYPE_FUNC2 && op->fntype == FN_LOOP)
4072 int fUse1;
4073 int parm_size = compileOpcodes(ctx,op->parms.parms[0],bufOut,bufOut_len, computTableSize, namespacePathToThis, RETURNVALUE_FPSTACK, NULL,&fUse1, NULL);
4074 if (parm_size < 0) RET_MINUS1_FAIL("loop coc fail")
4076 *calledRvType = RETURNVALUE_BOOL;
4077 if (fUse1 > *fpStackUse) *fpStackUse=fUse1;
4079 #ifndef GLUE_INLINE_LOOPS
4080 // todo: PPC looping support when loop length is small enough
4082 void *stub;
4083 int stubsize;
4084 unsigned char *newblock2, *p;
4085 stub = GLUE_realAddress(nseel_asm_repeat,&stubsize);
4086 if (bufOut_len < parm_size + stubsize) RET_MINUS1_FAIL("loop size fail")
4087 if (bufOut)
4089 newblock2 = compileCodeBlockWithRet(ctx,op->parms.parms[1],computTableSize,namespacePathToThis, RETURNVALUE_IGNORE, NULL,fpStackUse, NULL);
4091 p = bufOut + parm_size;
4092 memcpy(p, stub, stubsize);
4094 p=EEL_GLUE_set_immediate(p,(INT_PTR)newblock2);
4096 return rv_offset + parm_size + stubsize;
4098 #else
4100 int subsz;
4101 int fUse2=0;
4102 unsigned char *skipptr1,*loopdest;
4104 #ifndef GLUE_LOOP_LOADCNT_SIZE
4105 #define GLUE_LOOP_LOADCNT_SIZE sizeof(GLUE_LOOP_LOADCNT)
4106 #endif
4107 if (bufOut_len < parm_size + (int)(GLUE_LOOP_LOADCNT_SIZE + GLUE_LOOP_CLAMPCNT_SIZE + GLUE_LOOP_BEGIN_SIZE)) RET_MINUS1_FAIL("loop size fail")
4109 // store, convert to int, compare against 1, if less than, skip to end
4110 if (bufOut) memcpy(bufOut+parm_size,GLUE_LOOP_LOADCNT,GLUE_LOOP_LOADCNT_SIZE);
4111 parm_size += GLUE_LOOP_LOADCNT_SIZE;
4112 skipptr1 = bufOut+parm_size;
4114 // compare aginst max loop length, jump to loop start if not above it
4115 if (bufOut) memcpy(bufOut+parm_size,GLUE_LOOP_CLAMPCNT,GLUE_LOOP_CLAMPCNT_SIZE);
4116 parm_size += GLUE_LOOP_CLAMPCNT_SIZE;
4118 // loop code:
4119 loopdest = bufOut + parm_size;
4121 if (bufOut) memcpy(bufOut+parm_size,GLUE_LOOP_BEGIN,GLUE_LOOP_BEGIN_SIZE);
4122 parm_size += GLUE_LOOP_BEGIN_SIZE;
4124 subsz = compileOpcodes(ctx,op->parms.parms[1],bufOut ? (bufOut + parm_size) : NULL,bufOut_len - parm_size, computTableSize, namespacePathToThis, RETURNVALUE_IGNORE, NULL, &fUse2, NULL);
4125 if (subsz<0) RET_MINUS1_FAIL("loop coc fail")
4126 if (fUse2 > *fpStackUse) *fpStackUse=fUse2;
4128 parm_size += subsz;
4130 if (bufOut_len < parm_size + (int)sizeof(GLUE_LOOP_END)) RET_MINUS1_FAIL("loop size fail 2")
4132 if (bufOut) memcpy(bufOut+parm_size,GLUE_LOOP_END,sizeof(GLUE_LOOP_END));
4133 parm_size += sizeof(GLUE_LOOP_END);
4135 if (bufOut)
4137 GLUE_JMP_SET_OFFSET(bufOut + parm_size,loopdest - (bufOut+parm_size));
4138 GLUE_JMP_SET_OFFSET(skipptr1, (bufOut+parm_size) - skipptr1);
4141 return rv_offset + parm_size;
4144 #endif
4148 switch (op->opcodeType)
4150 case OPCODETYPE_DIRECTVALUE:
4151 if (preferredReturnValues == RETURNVALUE_BOOL)
4153 int w = fabs(op->parms.dv.directValue) >= NSEEL_CLOSEFACTOR;
4154 int wsz=(w?sizeof(GLUE_SET_P1_NZ):sizeof(GLUE_SET_P1_Z));
4156 *calledRvType = RETURNVALUE_BOOL;
4157 if (bufOut_len < wsz) RET_MINUS1_FAIL("direct bool size fail3")
4158 if (bufOut) memcpy(bufOut,w?GLUE_SET_P1_NZ:GLUE_SET_P1_Z,wsz);
4159 return rv_offset+wsz;
4161 else if (preferredReturnValues & RETURNVALUE_FPSTACK)
4163 #ifdef GLUE_HAS_FLDZ
4164 if (op->parms.dv.directValue == 0.0)
4166 #if GLUE_MAX_FPSTACK_SIZE > 0
4167 if (*fpStackUse < 1) *fpStackUse = 1;
4168 #endif
4169 *calledRvType = RETURNVALUE_FPSTACK;
4170 if (bufOut_len < sizeof(GLUE_FLDZ)) RET_MINUS1_FAIL("direct fp fail 1")
4171 if (bufOut) memcpy(bufOut,GLUE_FLDZ,sizeof(GLUE_FLDZ));
4172 return rv_offset+sizeof(GLUE_FLDZ);
4174 #endif
4175 #ifdef GLUE_HAS_FLD1
4176 if (op->parms.dv.directValue == 1.0)
4178 #if GLUE_MAX_FPSTACK_SIZE > 0
4179 if (*fpStackUse < 1) *fpStackUse = 1;
4180 #endif
4181 *calledRvType = RETURNVALUE_FPSTACK;
4182 if (bufOut_len < sizeof(GLUE_FLD1)) RET_MINUS1_FAIL("direct fp fail 1")
4183 if (bufOut) memcpy(bufOut,GLUE_FLD1,sizeof(GLUE_FLD1));
4184 return rv_offset+sizeof(GLUE_FLD1);
4186 #endif
4188 WDL_FALLTHROUGH; // fall through
4189 case OPCODETYPE_DIRECTVALUE_TEMPSTRING:
4190 case OPCODETYPE_VALUE_FROM_NAMESPACENAME:
4191 case OPCODETYPE_VARPTR:
4192 case OPCODETYPE_VARPTRPTR:
4194 #if GLUE_HAS_FPREG2 > 0
4195 if (preferredReturnValues & RETURNVALUE_FPREG2)
4197 if (preferredReturnValues != RETURNVALUE_FPREG2) RET_MINUS1_FAIL("RETURNVALUE_FPREG2 but not exact hm")
4198 if (bufOut_len < GLUE_MOV_PX_DIRECTVALUE_TOFPREG2_SIZE) RET_MINUS1_FAIL("direct fp2 fail 2")
4199 if (bufOut)
4201 if (generateValueToReg(ctx,op,bufOut,-2,namespacePathToThis, 1 /*allow caching*/)<0) RET_MINUS1_FAIL("direct fp2 fail gvr")
4203 *calledRvType = RETURNVALUE_FPREG2;
4204 return rv_offset+GLUE_MOV_PX_DIRECTVALUE_TOFPREG2_SIZE;
4206 #endif
4208 #ifdef GLUE_MOV_PX_DIRECTVALUE_TOSTACK_SIZE
4209 if (OPCODE_IS_TRIVIAL(op))
4211 if (preferredReturnValues & RETURNVALUE_FPSTACK)
4213 #if GLUE_MAX_FPSTACK_SIZE > 0
4214 if (*fpStackUse < 1) *fpStackUse = 1;
4215 #endif
4216 if (bufOut_len < GLUE_MOV_PX_DIRECTVALUE_TOSTACK_SIZE) RET_MINUS1_FAIL("direct fp fail 2")
4217 if (bufOut)
4219 if (generateValueToReg(ctx,op,bufOut,-1,namespacePathToThis, 1 /*allow caching*/)<0) RET_MINUS1_FAIL("direct fp fail gvr")
4221 *calledRvType = RETURNVALUE_FPSTACK;
4222 return rv_offset+GLUE_MOV_PX_DIRECTVALUE_TOSTACK_SIZE;
4225 #endif
4227 if (bufOut_len < GLUE_MOV_PX_DIRECTVALUE_SIZE)
4229 RET_MINUS1_FAIL("direct value fail 1")
4231 if (bufOut)
4233 if (generateValueToReg(ctx,op,bufOut,0,namespacePathToThis,
4234 (preferredReturnValues&(RETURNVALUE_FPSTACK|RETURNVALUE_CACHEABLE))!=0)<0)
4236 RET_MINUS1_FAIL("direct value gvr fail3")
4239 return rv_offset + GLUE_MOV_PX_DIRECTVALUE_SIZE;
4241 case OPCODETYPE_FUNCX:
4242 case OPCODETYPE_FUNC1:
4243 case OPCODETYPE_FUNC2:
4244 case OPCODETYPE_FUNC3:
4246 if (op->fntype == FUNCTYPE_EELFUNC)
4248 int a, fpUse1=0;
4250 a = compileEelFunctionCall(ctx,op,bufOut,bufOut_len,computTableSize,namespacePathToThis, calledRvType,&fpUse1,canHaveDenormalOutput);
4251 if (a<0) return a;
4252 if (fpUse1 > *fpStackUse) *fpStackUse = fpUse1;
4253 rv_offset += a;
4255 else
4257 int a, fpUse1=0;
4258 a = compileNativeFunctionCall(ctx,op,bufOut,bufOut_len,computTableSize,namespacePathToThis, calledRvType,&fpUse1,preferredReturnValues,canHaveDenormalOutput);
4259 if (a<0)return a;
4260 if (fpUse1 > *fpStackUse) *fpStackUse = fpUse1;
4261 rv_offset += a;
4263 return rv_offset;
4266 RET_MINUS1_FAIL("default opcode fail")
4269 #ifdef DUMP_OPS_DURING_COMPILE
4270 FILE *g_debugfp;
4271 int g_debugfp_indent;
4272 int g_debugfp_histsz=0;
4274 void dumpOp(compileContext *ctx, opcodeRec *op, int start)
4276 if (start>=0)
4278 if (g_debugfp)
4280 static opcodeRec **hist;
4282 int x;
4283 int hit=0;
4284 if (!hist) hist = (opcodeRec**) calloc(1024,1024*sizeof(opcodeRec*));
4285 for(x=0;x<g_debugfp_histsz;x++)
4287 if (hist[x] == op) { hit=1; break; }
4289 if (x ==g_debugfp_histsz && g_debugfp_histsz<1024*1024) hist[g_debugfp_histsz++] = op;
4291 if (!start)
4293 g_debugfp_indent-=2;
4294 fprintf(g_debugfp,"%*s}(join)\n",g_debugfp_indent," ");
4296 if (g_debugfp_indent>=100) *(char *)1=0;
4297 fprintf(g_debugfp,"%*s{ %p : %d%s: ",g_debugfp_indent," ",op,op->opcodeType, hit ? " -- DUPLICATE" : "");
4298 switch (op->opcodeType)
4300 case OPCODETYPE_DIRECTVALUE:
4301 fprintf(g_debugfp,"dv %f",op->parms.dv.directValue);
4302 break;
4303 case OPCODETYPE_VARPTR:
4304 if (op->relname && op->relname[0])
4306 fprintf(g_debugfp,"var %s",op->relname);
4308 else
4310 int wb;
4311 for (wb = 0; wb < ctx->varTable_numBlocks; wb ++)
4313 char **plist=ctx->varTable_Names[wb];
4314 if (!plist) break;
4316 if (op->parms.dv.valuePtr >= ctx->varTable_Values[wb] && op->parms.dv.valuePtr < ctx->varTable_Values[wb] + NSEEL_VARS_PER_BLOCK)
4318 fprintf(g_debugfp,"var %s",plist[op->parms.dv.valuePtr - ctx->varTable_Values[wb]]);
4319 break;
4323 break;
4324 case OPCODETYPE_FUNC1:
4325 case OPCODETYPE_FUNC2:
4326 case OPCODETYPE_FUNC3:
4327 case OPCODETYPE_FUNCX:
4328 if (op->fntype == FUNCTYPE_FUNCTIONTYPEREC)
4330 functionType *p=(functionType*)op->fn;
4331 fprintf(g_debugfp,"func %d: %s",p->nParams&0xff,p->name);
4333 else
4334 fprintf(g_debugfp,"sf %d",op->fntype);
4335 break;
4338 fprintf(g_debugfp,"\n");
4339 g_debugfp_indent+=2;
4342 else
4344 if (g_debugfp)
4346 g_debugfp_indent-=2;
4347 fprintf(g_debugfp,"%*s}%p\n",g_debugfp_indent," ",op);
4351 #endif
4353 int compileOpcodes(compileContext *ctx, opcodeRec *op, unsigned char *bufOut, int bufOut_len, int *computTableSize, const namespaceInformation *namespacePathToThis,
4354 int supportedReturnValues, int *rvType, int *fpStackUse, int *canHaveDenormalOutput)
4356 int code_returns=RETURNVALUE_NORMAL;
4357 int fpsu=0;
4358 int codesz;
4359 int denorm=0;
4361 #ifdef DUMP_OPS_DURING_COMPILE
4362 dumpOp(ctx,op,1);
4363 #endif
4365 codesz = compileOpcodesInternal(ctx,op,bufOut,bufOut_len,computTableSize,namespacePathToThis,&code_returns, supportedReturnValues,&fpsu,&denorm);
4366 if (denorm && canHaveDenormalOutput) *canHaveDenormalOutput=1;
4368 #ifdef DUMP_OPS_DURING_COMPILE
4369 dumpOp(ctx,op,-1);
4370 #endif
4371 #ifdef EEL_DUMP_OPS
4372 // dump opcode trees for verification, after optimizing
4373 if (g_eel_dump_fp2)
4375 fprintf(g_eel_dump_fp2,"-- compileOpcodes generated %d bytes of code!\r\n",codesz);
4377 #endif
4378 if (codesz < 0) return codesz;
4380 #if GLUE_HAS_FPREG2 > 0
4381 if (supportedReturnValues & RETURNVALUE_FPREG2)
4383 if (code_returns != RETURNVALUE_FPREG2)
4384 RET_MINUS1_FAIL("fpstack2 not return correct fail");
4386 #endif
4390 wdl_log("opcode %d %d (%s): fpu use: %d\n",op->opcodeType,op->fntype,
4391 op->opcodeType >= OPCODETYPE_FUNC1 && op->fntype == FUNCTYPE_FUNCTIONTYPEREC ? (
4392 ((functionType *)op->fn)->name
4393 ) : "",
4394 fpsu);
4398 if (fpStackUse) *fpStackUse=fpsu;
4400 if (bufOut) bufOut += codesz;
4401 bufOut_len -= codesz;
4404 if (code_returns == RETURNVALUE_BOOL && !(supportedReturnValues & RETURNVALUE_BOOL) && supportedReturnValues)
4406 int stubsize;
4407 void *stub = GLUE_realAddress(nseel_asm_booltofp,&stubsize);
4408 if (!stub || bufOut_len < stubsize) RET_MINUS1_FAIL(stub?"booltofp size":"booltfp addr")
4409 if (bufOut)
4411 memcpy(bufOut,stub,stubsize);
4412 bufOut += stubsize;
4414 codesz+=stubsize;
4415 bufOut_len -= stubsize;
4417 code_returns = RETURNVALUE_FPSTACK;
4421 // default processing of code_returns to meet return value requirements
4422 if (supportedReturnValues & code_returns)
4424 if (rvType) *rvType = code_returns;
4425 return codesz;
4429 if (rvType) *rvType = RETURNVALUE_IGNORE;
4432 if (code_returns == RETURNVALUE_NORMAL)
4434 if (supportedReturnValues & (RETURNVALUE_FPSTACK|RETURNVALUE_BOOL))
4436 if (bufOut_len < GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE) RET_MINUS1_FAIL("pushvalatpxtofpstack,size")
4437 if (bufOut)
4439 GLUE_PUSH_VAL_AT_PX_TO_FPSTACK(bufOut,0); // always fld qword [eax] but we might change that later
4440 bufOut += GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE;
4442 codesz += GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE;
4443 bufOut_len -= GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE;
4445 if (supportedReturnValues & RETURNVALUE_BOOL)
4447 code_returns = RETURNVALUE_FPSTACK;
4449 else
4451 if (rvType) *rvType = RETURNVALUE_FPSTACK;
4456 if (code_returns == RETURNVALUE_FPSTACK)
4458 if (supportedReturnValues & (RETURNVALUE_BOOL|RETURNVALUE_BOOL_REVERSED))
4460 int stubsize;
4461 void *stub;
4463 if (supportedReturnValues & RETURNVALUE_BOOL_REVERSED)
4465 if (rvType) *rvType = RETURNVALUE_BOOL_REVERSED;
4466 stub = GLUE_realAddress(nseel_asm_fptobool_rev,&stubsize);
4468 else
4470 if (rvType) *rvType = RETURNVALUE_BOOL;
4471 stub = GLUE_realAddress(nseel_asm_fptobool,&stubsize);
4475 if (!stub || bufOut_len < stubsize) RET_MINUS1_FAIL(stub?"fptobool size":"fptobool addr")
4476 if (bufOut)
4478 memcpy(bufOut,stub,stubsize);
4479 bufOut += stubsize;
4481 codesz+=stubsize;
4482 bufOut_len -= stubsize;
4484 else if (supportedReturnValues & RETURNVALUE_NORMAL)
4486 if (computTableSize) (*computTableSize) ++;
4488 if (bufOut_len < GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE) RET_MINUS1_FAIL("popfpstacktowtptopxsize")
4490 // generate fp-pop to temp space
4491 if (bufOut) GLUE_POP_FPSTACK_TO_WTP_TO_PX(bufOut,0);
4492 codesz+=GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE;
4493 if (rvType) *rvType = RETURNVALUE_NORMAL;
4495 else
4497 // toss return value that will be ignored
4498 if (bufOut_len < GLUE_POP_FPSTACK_SIZE) RET_MINUS1_FAIL("popfpstack size")
4499 if (bufOut) memcpy(bufOut,GLUE_POP_FPSTACK,GLUE_POP_FPSTACK_SIZE);
4500 codesz+=GLUE_POP_FPSTACK_SIZE;
4504 return codesz;
4508 #if 0
4509 static void movestringover(char *str, int amount)
4511 char tmp[1024+8];
4513 int l=(int)strlen(str);
4514 l=wdl_min(1024-amount-1,l);
4516 memcpy(tmp,str,l+1);
4518 while (l >= 0 && tmp[l]!='\n') l--;
4519 l++;
4521 tmp[l]=0;//ensure we null terminate
4523 memcpy(str+amount,tmp,l+1);
4525 #endif
4527 //------------------------------------------------------------------------------
4528 NSEEL_CODEHANDLE NSEEL_code_compile(NSEEL_VMCTX _ctx, const char *_expression, int lineoffs)
4530 return NSEEL_code_compile_ex(_ctx,_expression,lineoffs,0);
4533 typedef struct topLevelCodeSegmentRec {
4534 struct topLevelCodeSegmentRec *_next;
4535 void *code;
4536 int codesz;
4537 int tmptable_use;
4538 } topLevelCodeSegmentRec;
4541 NSEEL_CODEHANDLE NSEEL_code_compile_ex(NSEEL_VMCTX _ctx, const char *_expression, int lineoffs, int compile_flags)
4543 compileContext *ctx = (compileContext *)_ctx;
4544 const char *endptr;
4545 const char *_expression_end;
4546 codeHandleType *handle;
4547 topLevelCodeSegmentRec *startpts_tail=NULL;
4548 topLevelCodeSegmentRec *startpts=NULL;
4549 _codeHandleFunctionRec *oldCommonFunctionList;
4550 int curtabptr_sz=0;
4551 void *curtabptr=NULL;
4552 int had_err=0;
4554 if (!ctx) return 0;
4556 ctx->directValueCache=0;
4557 ctx->optimizeDisableFlags=0;
4558 ctx->gotEndOfInput=0;
4559 ctx->current_compile_flags = compile_flags;
4561 if (compile_flags & NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS_RESET)
4563 ctx->functions_common=NULL; // reset common function list
4565 else
4567 // reset common compiled function code, forcing a recompile if shared
4568 _codeHandleFunctionRec *a = ctx->functions_common;
4569 while (a)
4571 _codeHandleFunctionRec *b = a->derivedCopies;
4573 if (a->localstorage)
4575 // force local storage actual values to be reallocated if used again
4576 memset(a->localstorage,0,sizeof(EEL_F *) * a->localstorage_size);
4579 a->startptr = NULL; // force this copy to be recompiled
4580 a->startptr_size = -1;
4582 while (b)
4584 b->startptr = NULL; // force derived copies to get recompiled
4585 b->startptr_size = -1;
4586 // no need to reset b->localstorage, since it points to a->localstorage
4587 b=b->derivedCopies;
4590 a=a->next;
4594 ctx->last_error_string[0]=0;
4596 if (!_expression || !*_expression) return 0;
4598 _expression_end = _expression + strlen(_expression);
4600 oldCommonFunctionList = ctx->functions_common;
4602 ctx->isGeneratingCommonFunction=0;
4603 ctx->isSharedFunctions = !!(compile_flags & NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS);
4604 ctx->functions_local = NULL;
4606 freeBlocks(&ctx->tmpblocks,0);
4607 freeBlocks(&ctx->blocks_head_code,1);
4608 freeBlocks(&ctx->blocks_head_data,0);
4609 memset(ctx->l_stats,0,sizeof(ctx->l_stats));
4611 handle = (codeHandleType*)newDataBlock(sizeof(codeHandleType),8);
4613 if (!handle)
4615 return 0;
4619 memset(handle,0,sizeof(codeHandleType));
4621 ctx->l_stats[0] += (int)(_expression_end - _expression);
4622 ctx->tmpCodeHandle = handle;
4623 endptr=_expression;
4625 while (*endptr)
4627 int computTableTop = 0;
4628 int startptr_size=0;
4629 void *startptr=NULL;
4630 opcodeRec *start_opcode=NULL;
4631 const char *expr=endptr;
4633 int function_numparms=0;
4634 char is_fname[NSEEL_MAX_VARIABLE_NAMELEN+1];
4635 is_fname[0]=0;
4637 memset(ctx->function_localTable_Size,0,sizeof(ctx->function_localTable_Size));
4638 memset(ctx->function_localTable_Names,0,sizeof(ctx->function_localTable_Names));
4639 ctx->function_localTable_ValuePtrs=0;
4640 ctx->function_usesNamespaces=0;
4641 ctx->function_curName=NULL;
4642 ctx->function_globalFlag=0;
4644 ctx->errVar=0;
4646 // single out top level segment
4648 int had_something = 0, pcnt=0, pcnt2=0;
4649 int state=0;
4650 for (;;)
4652 int l;
4653 const char *p=nseel_simple_tokenizer(&endptr,_expression_end,&l,&state);
4654 if (!p)
4656 if (pcnt || pcnt2) ctx->gotEndOfInput|=4;
4657 break;
4660 if (*p == ';')
4662 if (had_something && !pcnt && !pcnt2) break;
4664 else if (*p == '/' && l > 1 && (p[1] == '/' || p[1] == '*'))
4666 if (l > 19 && !strnicmp(p,"//#eel-no-optimize:",19))
4667 ctx->optimizeDisableFlags = atoi(p+19);
4669 else
4671 if (!had_something)
4673 expr = p;
4674 had_something = 1;
4677 if (*p == '(') pcnt++;
4678 else if (*p == ')') { if (--pcnt<0) pcnt=0; }
4679 else if (*p == '[') pcnt2++;
4680 else if (*p == ']') { if (--pcnt2<0) pcnt2=0; }
4683 if (!*expr || !had_something) break;
4686 // parse
4689 int tmplen,funcname_len;
4690 const char *p = expr;
4691 const char *tok1 = nseel_simple_tokenizer(&p,endptr,&tmplen,NULL);
4692 const char *funcname = nseel_simple_tokenizer(&p,endptr,&funcname_len,NULL);
4693 if (tok1 && funcname && tmplen == 8 && !strnicmp(tok1,"function",8) && (isalpha_safe(funcname[0]) || funcname[0] == '_'))
4695 int had_parms_locals=0;
4696 if (funcname_len > sizeof(is_fname)-1) funcname_len=sizeof(is_fname)-1;
4697 memcpy(is_fname, funcname, funcname_len);
4698 is_fname[funcname_len]=0;
4699 ctx->function_curName = is_fname; // only assigned for the duration of the loop, cleared later //-V507
4701 while (NULL != (tok1 = nseel_simple_tokenizer(&p,endptr,&tmplen,NULL)))
4703 int is_parms = 0, localTableContext = 0;
4704 int maxcnt=0;
4705 const char *sp_save;
4707 if (tok1[0] == '(')
4709 if (had_parms_locals)
4711 expr = p-1; // begin compilation at this code!
4712 break;
4714 is_parms = 1;
4716 else
4718 if (tmplen == 5 && !strnicmp(tok1,"local",tmplen)) localTableContext=0;
4719 else if (tmplen == 6 && !strnicmp(tok1,"static",tmplen)) localTableContext=0;
4720 else if (tmplen == 8 && !strnicmp(tok1,"instance",tmplen)) localTableContext=1;
4721 else if ((tmplen == 7 && !strnicmp(tok1,"globals",tmplen)) ||
4722 (tmplen == 6 && !strnicmp(tok1,"global",tmplen)))
4724 ctx->function_globalFlag = 1;
4725 localTableContext=2;
4727 else break; // unknown token!
4729 tok1 = nseel_simple_tokenizer(&p,endptr,&tmplen,NULL);
4730 if (!tok1 || tok1[0] != '(') break;
4732 had_parms_locals = 1;
4735 sp_save=p;
4737 while (NULL != (tok1 = nseel_simple_tokenizer(&p,endptr,&tmplen,NULL)))
4739 if (tok1[0] == ')') break;
4740 if (*tok1 == '#' && localTableContext!=1 && localTableContext!=2)
4742 ctx->errVar = (int) (tok1 - _expression);
4743 lstrcpyn_safe(ctx->last_error_string,"#string can only be in instance() or globals()",sizeof(ctx->last_error_string));
4744 goto had_error;
4747 if (isalpha_safe(*tok1) || *tok1 == '_' || *tok1 == '#')
4749 maxcnt++;
4750 if (p < endptr && *p == '*')
4752 if (!is_parms && localTableContext!=2)
4754 ctx->errVar = (int) (p - _expression);
4755 lstrcpyn_safe(ctx->last_error_string,"namespace* can only be used in parameters or globals()",sizeof(ctx->last_error_string));
4756 goto had_error;
4758 p++;
4761 else if (*tok1 != ',')
4763 ctx->errVar = (int)(tok1 - _expression);
4764 lstrcpyn_safe(ctx->last_error_string,"unknown character in function parameters",sizeof(ctx->last_error_string));
4765 goto had_error;
4769 if (tok1 && maxcnt > 0)
4771 char **ot = ctx->function_localTable_Names[localTableContext];
4772 const int osz = ctx->function_localTable_Size[localTableContext];
4774 maxcnt += osz;
4776 ctx->function_localTable_Names[localTableContext] = (char **)newTmpBlock(ctx,sizeof(char *) * maxcnt);
4778 if (ctx->function_localTable_Names[localTableContext])
4780 int i=osz;
4781 if (osz && ot) memcpy(ctx->function_localTable_Names[localTableContext],ot,sizeof(char *) * osz);
4782 p=sp_save;
4784 while (NULL != (tok1 = nseel_simple_tokenizer(&p,endptr,&tmplen,NULL)))
4786 if (tok1[0] == ')') break;
4787 if (isalpha_safe(*tok1) || *tok1 == '_' || *tok1 == '#')
4789 char *newstr;
4790 int l = tmplen;
4791 if (*p == '*') // xyz* for namespace
4793 p++;
4794 l++;
4796 if (l > NSEEL_MAX_VARIABLE_NAMELEN) l = NSEEL_MAX_VARIABLE_NAMELEN;
4797 newstr = newTmpBlock(ctx,l+1);
4798 if (newstr)
4800 memcpy(newstr,tok1,l);
4801 newstr[l]=0;
4802 ctx->function_localTable_Names[localTableContext][i++] = newstr;
4806 ctx->function_localTable_Size[localTableContext]=i;
4807 if (is_parms) function_numparms = i;
4813 if (ctx->function_localTable_Size[0]>0)
4815 ctx->function_localTable_ValuePtrs =
4816 ctx->isSharedFunctions ? newDataBlock(ctx->function_localTable_Size[0] * sizeof(EEL_F *),8) :
4817 newTmpBlock(ctx,ctx->function_localTable_Size[0] * sizeof(EEL_F *));
4818 if (!ctx->function_localTable_ValuePtrs)
4820 ctx->function_localTable_Size[0]=0;
4821 function_numparms=0;
4823 else
4825 memset(ctx->function_localTable_ValuePtrs,0,sizeof(EEL_F *) * ctx->function_localTable_Size[0]); // force values to be allocated
4830 int nseelparse(compileContext* context);
4831 void nseelrestart (void *input_file ,void *yyscanner );
4833 ctx->rdbuf_start = _expression;
4835 #ifdef NSEEL_SUPER_MINIMAL_LEXER
4837 ctx->rdbuf = expr;
4838 ctx->rdbuf_end = endptr;
4839 if (!nseelparse(ctx) && !ctx->errVar)
4841 start_opcode = ctx->result;
4843 #else
4845 nseelrestart(NULL,ctx->scanner);
4847 ctx->rdbuf = expr;
4848 ctx->rdbuf_end = endptr;
4850 if (!nseelparse(ctx) && !ctx->errVar)
4852 start_opcode = ctx->result;
4854 if (ctx->errVar)
4856 const char *p=expr;
4857 ctx->errVar += expr-_expression;
4859 #endif
4860 ctx->rdbuf = NULL;
4863 if (start_opcode)
4865 int rvMode=0, fUse=0;
4867 #ifdef LOG_OPT
4868 int sd=0;
4869 wdl_log("pre opt sz=%d (tsackDepth=%d)\n",compileOpcodes(ctx,start_opcode,NULL,1024*1024*256,NULL, NULL,RETURNVALUE_IGNORE,NULL,&sd,NULL),sd);
4870 #endif
4872 #ifdef EEL_DUMP_OPS
4873 // dump opcode trees for verification, before optimizing
4874 if (g_eel_dump_fp)
4876 fprintf(g_eel_dump_fp,"-- opcode chunk --\r\n");
4877 dumpOpcodeTree(ctx,g_eel_dump_fp,start_opcode,2);
4879 #endif
4881 if (!(ctx->optimizeDisableFlags&OPTFLAG_NO_OPTIMIZE)) optimizeOpcodes(ctx,start_opcode,is_fname[0] ? 1 : 0);
4882 #ifdef LOG_OPT
4883 wdl_log("post opt sz=%d, stack depth=%d\n",compileOpcodes(ctx,start_opcode,NULL,1024*1024*256,NULL,NULL, RETURNVALUE_IGNORE,NULL,&sd,NULL),sd);
4884 #endif
4886 #ifdef EEL_DUMP_OPS
4887 // dump opcode trees for verification, after optimizing
4888 if (g_eel_dump_fp2)
4890 fprintf(g_eel_dump_fp2,"-- POST-OPTIMIZED opcode chunk --\r\n");
4891 dumpOpcodeTree(ctx,g_eel_dump_fp2,start_opcode,2);
4893 #endif
4895 if (is_fname[0])
4897 _codeHandleFunctionRec *fr = ctx->isSharedFunctions ? newDataBlock(sizeof(_codeHandleFunctionRec),8) :
4898 newTmpBlock(ctx,sizeof(_codeHandleFunctionRec));
4899 if (fr)
4901 memset(fr,0,sizeof(_codeHandleFunctionRec));
4902 fr->startptr_size = -1;
4903 fr->opcodes = start_opcode;
4905 if (ctx->function_localTable_Size[0] > 0 && ctx->function_localTable_ValuePtrs)
4907 if (ctx->function_localTable_Names[0])
4909 int i;
4910 for(i=0;i<function_numparms;i++)
4912 const char *nptr = ctx->function_localTable_Names[0][i];
4913 if (nptr && *nptr && nptr[strlen(nptr)-1] == '*')
4915 fr->parameterAsNamespaceMask |= ((unsigned int)1)<<i;
4919 fr->num_params=function_numparms;
4920 fr->localstorage = ctx->function_localTable_ValuePtrs;
4921 fr->localstorage_size = ctx->function_localTable_Size[0];
4924 fr->usesNamespaces = ctx->function_usesNamespaces;
4925 fr->isCommonFunction = ctx->isSharedFunctions;
4927 lstrcpyn_safe(fr->fname,is_fname,sizeof(fr->fname));
4929 if (ctx->isSharedFunctions)
4931 fr->next = ctx->functions_common;
4932 ctx->functions_common = fr;
4934 else
4936 fr->next = ctx->functions_local;
4937 ctx->functions_local = fr;
4940 continue;
4943 #ifdef DUMP_OPS_DURING_COMPILE
4944 g_debugfp_indent=0;
4945 g_debugfp_histsz=0;
4946 g_debugfp = fopen("C:/temp/foo.txt","w");
4947 #endif
4948 startptr_size = compileOpcodes(ctx,start_opcode,NULL,1024*1024*256,NULL, NULL,
4949 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
4950 // if a function, allow the code to decide how return values are generated
4952 #ifdef DUMP_OPS_DURING_COMPILE
4953 if (g_debugfp) fclose(g_debugfp);
4954 g_debugfp=0;
4955 #endif
4958 if (!startptr_size) continue; // optimized away
4959 if (startptr_size>0)
4961 startptr = newTmpBlock(ctx,startptr_size);
4962 if (startptr)
4964 startptr_size=compileOpcodes(ctx,start_opcode,(unsigned char*)startptr,startptr_size,&computTableTop, NULL, RETURNVALUE_IGNORE, NULL,NULL, NULL);
4965 if (startptr_size<=0) startptr = NULL;
4971 if (!startptr)
4973 had_error:
4974 #ifdef NSEEL_EEL1_COMPAT_MODE
4975 continue;
4977 #else
4978 //if (!ctx->last_error_string[0])
4980 int byteoffs = ctx->errVar;
4981 int linenumber;
4982 char cur_err[sizeof(ctx->last_error_string)];
4983 lstrcpyn_safe(cur_err,ctx->last_error_string,sizeof(cur_err));
4984 if (cur_err[0]) lstrcatn(cur_err,": ",sizeof(cur_err));
4985 else lstrcpyn_safe(cur_err,"syntax error: ",sizeof(cur_err));
4987 if (_expression + byteoffs >= _expression_end)
4989 if (ctx->gotEndOfInput&4) byteoffs = (int)(expr-_expression);
4990 else byteoffs=(int)(_expression_end-_expression);
4993 if (byteoffs < 0) byteoffs=0;
4995 linenumber=findLineNumber(_expression,byteoffs)+1;
4997 if (ctx->gotEndOfInput&4)
4999 snprintf(ctx->last_error_string,sizeof(ctx->last_error_string),"%d: %.200smissing ) or ]",linenumber+lineoffs,cur_err);
5001 else
5003 const char *p = _expression + byteoffs;
5004 int x=1, right_amt_nospace=0, left_amt_nospace=0;
5005 while (x < 32 && p-x >= _expression && p[-x] != '\r' && p[-x] != '\n')
5007 if (!isspace_safe(p[-x])) left_amt_nospace=x;
5008 x++;
5010 x=0;
5011 while (x < 60 && p[x] && p[x] != '\r' && p[x] != '\n')
5013 if (!isspace_safe(p[x])) right_amt_nospace=x+1;
5014 x++;
5017 // display left_amt >>>> right_amt_nospace
5018 snprintf(ctx->last_error_string,sizeof(ctx->last_error_string),"%d: %.200s'%.*s <!> %.*s'",linenumber+lineoffs,cur_err,
5019 left_amt_nospace, p-left_amt_nospace,
5020 right_amt_nospace ? right_amt_nospace : 5,right_amt_nospace ? p : "<eol>");
5024 startpts=NULL;
5025 startpts_tail=NULL;
5026 had_err=1;
5027 break;
5028 #endif
5031 if (!is_fname[0]) // redundant check (if is_fname[0] is set and we succeeded, it should continue)
5032 // but we'll be on the safe side
5034 topLevelCodeSegmentRec *p = newTmpBlock(ctx,sizeof(topLevelCodeSegmentRec));
5035 p->_next=0;
5036 p->code = startptr;
5037 p->codesz = startptr_size;
5038 p->tmptable_use = computTableTop;
5040 if (!startpts_tail) startpts_tail=startpts=p;
5041 else
5043 startpts_tail->_next=p;
5044 startpts_tail=p;
5047 if (curtabptr_sz < computTableTop)
5049 curtabptr_sz=computTableTop;
5054 memset(ctx->function_localTable_Size,0,sizeof(ctx->function_localTable_Size));
5055 memset(ctx->function_localTable_Names,0,sizeof(ctx->function_localTable_Names));
5056 ctx->function_localTable_ValuePtrs=0;
5057 ctx->function_usesNamespaces=0;
5058 ctx->function_curName=NULL;
5059 ctx->function_globalFlag=0;
5061 ctx->tmpCodeHandle = NULL;
5063 if (handle->want_stack)
5065 if (!handle->stack) startpts=NULL;
5068 if (startpts)
5070 curtabptr_sz += 2; // many functions use the worktable for temporary storage of up to 2 EEL_F's
5072 handle->workTable_size = curtabptr_sz;
5073 handle->workTable = curtabptr = newDataBlock((curtabptr_sz+MIN_COMPUTABLE_SIZE + COMPUTABLE_EXTRA_SPACE) * sizeof(EEL_F),32);
5075 #ifdef EEL_VALIDATE_WORKTABLE_USE
5076 if (curtabptr) memset(curtabptr,0x3a,(curtabptr_sz+MIN_COMPUTABLE_SIZE + COMPUTABLE_EXTRA_SPACE) * sizeof(EEL_F));
5077 #endif
5078 if (!curtabptr) startpts=NULL;
5082 if (startpts || (!had_err && (compile_flags & NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS)))
5084 unsigned char *writeptr;
5085 topLevelCodeSegmentRec *p=startpts;
5086 int size=sizeof(GLUE_RET)+GLUE_FUNC_ENTER_SIZE+GLUE_FUNC_LEAVE_SIZE; // for ret at end :)
5087 int wtpos=0;
5089 // now we build one big code segment out of our list of them, inserting a mov esi, computable before each item as necessary
5090 while (p)
5092 if (wtpos <= 0)
5094 wtpos=MIN_COMPUTABLE_SIZE;
5095 size += GLUE_RESET_WTP(NULL,0);
5097 size+=p->codesz;
5098 wtpos -= p->tmptable_use;
5099 p=p->_next;
5101 handle->code = newCodeBlock(size,32);
5102 if (handle->code)
5104 writeptr=(unsigned char *)handle->code;
5105 #if GLUE_FUNC_ENTER_SIZE > 0
5106 memcpy(writeptr,&GLUE_FUNC_ENTER,GLUE_FUNC_ENTER_SIZE);
5107 writeptr += GLUE_FUNC_ENTER_SIZE;
5108 #endif
5109 p=startpts;
5110 wtpos=0;
5111 while (p)
5113 if (wtpos <= 0)
5115 wtpos=MIN_COMPUTABLE_SIZE;
5116 writeptr+=GLUE_RESET_WTP(writeptr,curtabptr);
5118 memcpy(writeptr,(char*)p->code,p->codesz);
5119 writeptr += p->codesz;
5120 wtpos -= p->tmptable_use;
5122 p=p->_next;
5124 #if GLUE_FUNC_LEAVE_SIZE > 0
5125 memcpy(writeptr,&GLUE_FUNC_LEAVE,GLUE_FUNC_LEAVE_SIZE);
5126 writeptr += GLUE_FUNC_LEAVE_SIZE;
5127 #endif
5128 memcpy(writeptr,&GLUE_RET,sizeof(GLUE_RET)); writeptr += sizeof(GLUE_RET);
5129 ctx->l_stats[1]=size;
5130 handle->code_size = (int) (writeptr - (unsigned char *)handle->code);
5131 #if defined(__arm__) || defined(__aarch64__)
5132 __clear_cache(handle->code,writeptr);
5133 #endif
5136 handle->blocks_code = ctx->blocks_head_code;
5137 #ifndef EEL_DOESNT_NEED_EXEC_PERMS
5138 eel_set_blocks_allow_execute(handle->blocks_code,1);
5139 #endif
5140 handle->blocks_data = ctx->blocks_head_data;
5141 ctx->blocks_head_code=0;
5142 ctx->blocks_head_data=0;
5144 else
5146 // failed compiling, or failed calloc()
5147 handle=NULL;
5151 ctx->directValueCache=0;
5152 ctx->functions_local = NULL;
5154 ctx->isGeneratingCommonFunction=0;
5155 ctx->isSharedFunctions=0;
5157 freeBlocks(&ctx->tmpblocks,0);
5158 freeBlocks(&ctx->blocks_head_code,1);
5159 freeBlocks(&ctx->blocks_head_data,0);
5161 if (handle)
5163 handle->compile_flags = compile_flags;
5164 handle->ramPtr = ctx->ram_state->blocks;
5165 memcpy(handle->code_stats,ctx->l_stats,sizeof(ctx->l_stats));
5166 nseel_evallib_stats[0]+=ctx->l_stats[0];
5167 nseel_evallib_stats[1]+=ctx->l_stats[1];
5168 nseel_evallib_stats[2]+=ctx->l_stats[2];
5169 nseel_evallib_stats[3]+=ctx->l_stats[3];
5170 nseel_evallib_stats[4]++;
5172 else
5174 ctx->functions_common = oldCommonFunctionList; // failed compiling, remove any added common functions from the list
5176 // remove any derived copies of functions due to error, since we may have added some that have been freed
5177 while (oldCommonFunctionList)
5179 oldCommonFunctionList->derivedCopies=NULL;
5180 oldCommonFunctionList=oldCommonFunctionList->next;
5183 memset(ctx->l_stats,0,sizeof(ctx->l_stats));
5185 return (NSEEL_CODEHANDLE)handle;
5188 //------------------------------------------------------------------------------
5189 void NSEEL_code_execute(NSEEL_CODEHANDLE code)
5191 #ifndef GLUE_TABPTR_IGNORED
5192 INT_PTR tabptr;
5193 #endif
5194 INT_PTR codeptr;
5195 codeHandleType *h = (codeHandleType *)code;
5196 if (!h || !h->code) return;
5198 codeptr = (INT_PTR) h->code;
5199 #if 0
5201 unsigned int *p=(unsigned int *)codeptr;
5202 while (*p != GLUE_RET[0])
5204 printf("instr:%04X:%04X\n",*p>>16,*p&0xffff);
5205 p++;
5208 #endif
5210 #ifndef GLUE_TABPTR_IGNORED
5211 tabptr=(INT_PTR)h->workTable;
5212 #endif
5213 //printf("calling code!\n");
5214 GLUE_CALL_CODE(tabptr,codeptr,(INT_PTR)h->ramPtr);
5218 int NSEEL_code_geterror_flag(NSEEL_VMCTX ctx)
5220 compileContext *c=(compileContext *)ctx;
5221 if (c) return (c->gotEndOfInput ? 1 : 0);
5222 return 0;
5225 char *NSEEL_code_getcodeerror(NSEEL_VMCTX ctx)
5227 compileContext *c=(compileContext *)ctx;
5228 if (ctx && c->last_error_string[0]) return c->last_error_string;
5229 return 0;
5232 //------------------------------------------------------------------------------
5233 void NSEEL_code_free(NSEEL_CODEHANDLE code)
5235 codeHandleType *h = (codeHandleType *)code;
5236 if (h != NULL)
5238 #ifdef EEL_VALIDATE_WORKTABLE_USE
5239 if (h->workTable)
5241 char *p = ((char*)h->workTable) + h->workTable_size*sizeof(EEL_F);
5242 int x;
5243 for(x=COMPUTABLE_EXTRA_SPACE*sizeof(EEL_F) - 1;x >= 0; x --)
5244 if (p[x] != 0x3a)
5246 wdl_log("worktable overrun at byte %d (wts=%d), value = %f\n",x,h->workTable_size, *(EEL_F*)(p+(x&~(sizeof(EEL_F)-1))));
5247 break;
5250 #endif
5252 nseel_evallib_stats[0]-=h->code_stats[0];
5253 nseel_evallib_stats[1]-=h->code_stats[1];
5254 nseel_evallib_stats[2]-=h->code_stats[2];
5255 nseel_evallib_stats[3]-=h->code_stats[3];
5256 nseel_evallib_stats[4]--;
5258 freeBlocks(&h->blocks_code,1);
5259 freeBlocks(&h->blocks_data,0);
5264 //------------------------------------------------------------------------------
5266 NSEEL_VMCTX NSEEL_VM_alloc() // return a handle
5268 compileContext *ctx=calloc(1,sizeof(compileContext));
5270 #ifdef NSEEL_SUPER_MINIMAL_LEXER
5271 if (ctx) ctx->scanner = ctx;
5272 #else
5273 if (ctx)
5275 int nseellex_init(void ** ptr_yy_globals);
5276 void nseelset_extra(void *user_defined , void *yyscanner);
5277 if (nseellex_init(&ctx->scanner))
5279 free(ctx);
5280 return NULL;
5282 nseelset_extra(ctx,ctx->scanner);
5284 #endif
5286 if (ctx)
5288 ctx->ram_state = __newBlock_align(&ctx->ctx_pblocks,sizeof(*ctx->ram_state),16,0);
5289 memset(ctx->ram_state,0,sizeof(*ctx->ram_state));
5290 ctx->ram_state->sign_mask[0] = ctx->ram_state->sign_mask[1] = WDL_UINT64_CONST(0x8000000000000000);
5291 ctx->ram_state->abs_mask[0] = ctx->ram_state->abs_mask[1] = WDL_UINT64_CONST(0x7FFFFFFFFFFFFFFF);
5292 ctx->ram_state->maxblocks = NSEEL_RAM_BLOCKS_DEFAULTMAX;
5293 ctx->ram_state->closefact = NSEEL_CLOSEFACTOR;
5295 return ctx;
5298 int NSEEL_VM_setramsize(NSEEL_VMCTX _ctx, int maxent)
5300 compileContext *ctx = (compileContext *)_ctx;
5301 if (!ctx) return 0;
5302 if (maxent > 0)
5304 maxent = (maxent + NSEEL_RAM_ITEMSPERBLOCK - 1)/NSEEL_RAM_ITEMSPERBLOCK;
5305 if (maxent > NSEEL_RAM_BLOCKS) maxent = NSEEL_RAM_BLOCKS;
5306 ctx->ram_state->maxblocks = maxent;
5309 return ctx->ram_state->maxblocks * NSEEL_RAM_ITEMSPERBLOCK;
5312 void NSEEL_VM_preallocram(NSEEL_VMCTX _ctx, int maxent)
5314 compileContext *ctx = (compileContext *)_ctx;
5315 int x;
5316 if (!ctx || !maxent) return;
5318 if (maxent < 0)
5320 maxent = ctx->ram_state->maxblocks;
5322 else
5324 maxent = (maxent + NSEEL_RAM_ITEMSPERBLOCK - 1)/NSEEL_RAM_ITEMSPERBLOCK;
5325 if (maxent > ctx->ram_state->maxblocks) maxent = ctx->ram_state->maxblocks;
5327 for (x = 0; x < maxent; x ++)
5328 __NSEEL_RAMAlloc(ctx->ram_state->blocks,x * NSEEL_RAM_ITEMSPERBLOCK);
5331 void NSEEL_VM_SetFunctionValidator(NSEEL_VMCTX _ctx, const char * (*validateFunc)(const char *fn_name, void *user), void *user)
5333 if (_ctx)
5335 compileContext *ctx = (compileContext *)_ctx;
5336 ctx->func_check = validateFunc;
5337 ctx->func_check_user = user;
5341 void NSEEL_VM_SetFunctionTable(NSEEL_VMCTX _ctx, eel_function_table *tab)
5343 if (_ctx)
5345 compileContext *ctx = (compileContext *)_ctx;
5346 ctx->registered_func_tab = tab;
5349 void NSEEL_VM_free(NSEEL_VMCTX _ctx) // free when done with a VM and ALL of its code have been freed, as well
5352 if (_ctx)
5354 compileContext *ctx=(compileContext *)_ctx;
5355 EEL_GROWBUF_RESIZE(&ctx->varNameList,-1);
5356 NSEEL_VM_freeRAM(_ctx);
5358 freeBlocks(&ctx->ctx_pblocks,0);
5360 // these should be 0 normally but just in case
5361 freeBlocks(&ctx->tmpblocks,0);
5362 freeBlocks(&ctx->blocks_head_code,1);
5363 freeBlocks(&ctx->blocks_head_data,0);
5366 #ifndef NSEEL_SUPER_MINIMAL_LEXER
5367 if (ctx->scanner)
5369 int nseellex_destroy(void *yyscanner);
5370 nseellex_destroy(ctx->scanner);
5372 #endif
5373 ctx->scanner=0;
5374 if (ctx->has_used_global_vars)
5376 nseel_globalVarItem *p = NULL;
5377 NSEEL_HOSTSTUB_EnterMutex();
5378 if (--nseel_vms_referencing_globallist_cnt == 0)
5380 // clear and free globals
5381 p = nseel_globalreg_list;
5382 nseel_globalreg_list=0;
5384 NSEEL_HOSTSTUB_LeaveMutex();
5386 while (p)
5388 nseel_globalVarItem *op = p;
5389 p=p->_next;
5390 free(op);
5393 free(ctx);
5398 int *NSEEL_code_getstats(NSEEL_CODEHANDLE code)
5400 codeHandleType *h = (codeHandleType *)code;
5401 if (h)
5403 return h->code_stats;
5405 return 0;
5408 void NSEEL_VM_SetStringFunc(NSEEL_VMCTX ctx,
5409 EEL_F (*onString)(void *caller_this, struct eelStringSegmentRec *list),
5410 EEL_F (*onNamedString)(void *caller_this, const char *name))
5412 if (ctx)
5414 compileContext *c=(compileContext*)ctx;
5415 c->onString = onString;
5416 c->onNamedString = onNamedString;
5420 void NSEEL_VM_SetCustomFuncThis(NSEEL_VMCTX ctx, void *thisptr)
5422 if (ctx)
5424 compileContext *c=(compileContext*)ctx;
5425 c->caller_this=thisptr;
5433 void *NSEEL_PProc_RAM(void *data, int data_size, compileContext *ctx)
5435 if (data_size>0) data=EEL_GLUE_set_immediate(data, (INT_PTR)ctx->ram_state->blocks);
5436 return data;
5439 void *NSEEL_PProc_THIS(void *data, int data_size, compileContext *ctx)
5441 if (data_size>0) data=EEL_GLUE_set_immediate(data, (INT_PTR)ctx->caller_this);
5442 return data;
5445 static int vartable_lowerbound(compileContext *ctx, const char *name, int *ismatch)
5447 int a = 0, c = EEL_GROWBUF_GET_SIZE(&ctx->varNameList);
5448 varNameRec **list = EEL_GROWBUF_GET(&ctx->varNameList);
5449 while (a != c)
5451 const int b = (a+c)/2;
5452 const int cmp = strnicmp(name,list[b]->str,NSEEL_MAX_VARIABLE_NAMELEN);
5453 if (cmp > 0) a = b+1;
5454 else if (cmp < 0) c = b;
5455 else
5457 *ismatch = 1;
5458 return b;
5461 *ismatch = 0;
5462 return a;
5465 static void vartable_cull_list(compileContext *ctx, int refcnt_chk)
5467 const int ni = EEL_GROWBUF_GET_SIZE(&ctx->varNameList);
5468 int i = ni, ndel = 0;
5469 varNameRec **rd = EEL_GROWBUF_GET(&ctx->varNameList), **wr=rd;
5470 while (i--)
5472 varNameRec *v = rd[0];
5473 if ((!refcnt_chk || !v->refcnt) && !v->isreg)
5475 ndel++;
5477 else
5479 if (wr != rd) *wr = *rd;
5480 wr++;
5482 rd++;
5484 if (ndel) EEL_GROWBUF_RESIZE(&ctx->varNameList,ni - ndel);
5487 void NSEEL_VM_remove_unused_vars(NSEEL_VMCTX _ctx)
5489 compileContext *ctx = (compileContext *)_ctx;
5490 if (ctx) vartable_cull_list(ctx,1);
5493 void NSEEL_VM_remove_all_nonreg_vars(NSEEL_VMCTX _ctx)
5495 compileContext *ctx = (compileContext *)_ctx;
5496 if (ctx) vartable_cull_list(ctx,0);
5499 void NSEEL_VM_clear_var_refcnts(NSEEL_VMCTX _ctx)
5501 compileContext *ctx = (compileContext *)_ctx;
5502 if (ctx)
5504 int i = EEL_GROWBUF_GET_SIZE(&ctx->varNameList);
5505 varNameRec **rd = EEL_GROWBUF_GET(&ctx->varNameList);
5506 while (i--)
5508 rd[0]->refcnt=0;
5509 rd++;
5515 #ifdef NSEEL_EEL1_COMPAT_MODE
5516 static EEL_F __nseel_global_regs[100];
5517 double *NSEEL_getglobalregs() { return __nseel_global_regs; }
5518 #endif
5520 EEL_F *get_global_var(compileContext *ctx, const char *gv, int addIfNotPresent)
5522 nseel_globalVarItem *p;
5523 #ifdef NSEEL_EEL1_COMPAT_MODE
5524 if (!strnicmp(gv,"reg",3) && gv[3]>='0' && gv[3] <= '9' && gv[4] >= '0' && gv[4] <= '9' && !gv[5])
5526 return __nseel_global_regs + atoi(gv+3);
5528 #endif
5530 NSEEL_HOSTSTUB_EnterMutex();
5531 if (!ctx->has_used_global_vars)
5533 ctx->has_used_global_vars++;
5534 nseel_vms_referencing_globallist_cnt++;
5537 p = nseel_globalreg_list;
5538 while (p)
5540 if (!stricmp(p->name,gv)) break;
5541 p=p->_next;
5544 if (!p && addIfNotPresent)
5546 size_t gvl = strlen(gv);
5547 p = (nseel_globalVarItem*)malloc(sizeof(nseel_globalVarItem) + gvl);
5548 if (p)
5550 p->data=0.0;
5551 strcpy(p->name,gv);
5552 p->_next = nseel_globalreg_list;
5553 nseel_globalreg_list=p;
5556 NSEEL_HOSTSTUB_LeaveMutex();
5557 return p ? &p->data : NULL;
5562 EEL_F *nseel_int_register_var(compileContext *ctx, const char *name, int isReg, const char **namePtrOut)
5564 int slot, match;
5566 if (isReg == 0 && ctx->getVariable)
5568 EEL_F *ret = ctx->getVariable(ctx->getVariable_userctx, name);
5569 if (ret) return ret;
5572 if (!strnicmp(name,"_global.",8) && name[8])
5574 EEL_F *a=get_global_var(ctx,name+8,isReg >= 0);
5575 if (a) return a;
5578 slot = vartable_lowerbound(ctx,name, &match);
5579 if (match)
5581 varNameRec *v = EEL_GROWBUF_GET(&ctx->varNameList)[slot];
5582 if (isReg >= 0)
5584 v->refcnt++;
5585 if (isReg) v->isreg=isReg;
5586 if (namePtrOut) *namePtrOut = v->str;
5588 return v->value;
5590 if (isReg < 0) return NULL;
5592 if (ctx->varValueStore_left<1)
5594 const int sz=500;
5595 ctx->varValueStore_left = sz;
5596 ctx->varValueStore = (EEL_F *)newCtxDataBlock((int)sizeof(EEL_F)*sz,8);
5598 if (ctx->varValueStore)
5600 int listsz = EEL_GROWBUF_GET_SIZE(&ctx->varNameList);
5601 size_t l = strlen(name);
5602 varNameRec *vh;
5603 if (l > NSEEL_MAX_VARIABLE_NAMELEN) l = NSEEL_MAX_VARIABLE_NAMELEN;
5604 vh = (varNameRec*) newCtxDataBlock( (int) (sizeof(varNameRec) + l),8);
5605 if (!vh || EEL_GROWBUF_RESIZE(&ctx->varNameList, (listsz+1))) return NULL; // alloc fail
5607 (vh->value = ctx->varValueStore++)[0]=0.0;
5608 ctx->varValueStore_left--;
5610 vh->refcnt=1;
5611 vh->isreg=isReg;
5612 memcpy(vh->str,name,l);
5613 vh->str[l] = 0;
5614 if (namePtrOut) *namePtrOut = vh->str;
5616 if (slot < listsz)
5618 memmove(EEL_GROWBUF_GET(&ctx->varNameList) + slot+1,
5619 EEL_GROWBUF_GET(&ctx->varNameList) + slot, (listsz - slot) * sizeof(EEL_GROWBUF_GET(&ctx->varNameList)[0]));
5621 EEL_GROWBUF_GET(&ctx->varNameList)[slot] = vh;
5623 return vh->value;
5625 return NULL;
5629 //------------------------------------------------------------------------------
5631 void NSEEL_VM_enumallvars(NSEEL_VMCTX ctx, int (*func)(const char *name, EEL_F *val, void *ctx), void *userctx)
5633 compileContext *tctx = (compileContext *) ctx;
5634 int ni;
5635 varNameRec **rd;
5636 if (!tctx) return;
5638 ni = EEL_GROWBUF_GET_SIZE(&tctx->varNameList);
5639 rd = EEL_GROWBUF_GET(&tctx->varNameList);
5640 while (ni--)
5642 if (!func(rd[0]->str,rd[0]->value,userctx)) break;
5643 rd++;
5648 //------------------------------------------------------------------------------
5649 EEL_F *NSEEL_VM_regvar(NSEEL_VMCTX _ctx, const char *var)
5651 compileContext *ctx = (compileContext *)_ctx;
5652 if (!ctx) return 0;
5654 if (!strnicmp(var,"reg",3) && strlen(var) == 5 && isdigit_safe(var[3]) && isdigit_safe(var[4]))
5656 EEL_F *a=get_global_var(ctx,var,1);
5657 if (a) return a;
5660 return nseel_int_register_var(ctx,var,1,NULL);
5663 EEL_F *NSEEL_VM_getvar(NSEEL_VMCTX _ctx, const char *var)
5665 compileContext *ctx = (compileContext *)_ctx;
5666 if (!ctx) return 0;
5668 if (!strnicmp(var,"reg",3) && strlen(var) == 5 && isdigit_safe(var[3]) && isdigit_safe(var[4]))
5670 EEL_F *a=get_global_var(ctx,var,0);
5671 if (a) return a;
5674 return nseel_int_register_var(ctx,var,-1,NULL);
5677 int NSEEL_VM_get_var_refcnt(NSEEL_VMCTX _ctx, const char *name)
5679 compileContext *ctx = (compileContext *)_ctx;
5680 int slot,match;
5681 if (!ctx) return -1;
5682 slot = vartable_lowerbound(ctx,name, &match);
5683 return match ? EEL_GROWBUF_GET(&ctx->varNameList)[slot]->refcnt : -1;
5689 opcodeRec *nseel_createFunctionByName(compileContext *ctx, const char *name, int np, opcodeRec *code1, opcodeRec *code2, opcodeRec *code3)
5691 int chkamt=0;
5692 functionType *f=nseel_getFunctionByName(ctx,name,&chkamt);
5693 if (f) while (chkamt-->=0)
5695 if ((f->nParams&FUNCTIONTYPE_PARAMETERCOUNTMASK) == np)
5697 opcodeRec *o=newOpCode(ctx,NULL, np==3?OPCODETYPE_FUNC3:np==2?OPCODETYPE_FUNC2:OPCODETYPE_FUNC1);
5698 if (o)
5700 o->fntype = FUNCTYPE_FUNCTIONTYPEREC;
5701 o->fn = f;
5702 o->parms.parms[0]=code1;
5703 o->parms.parms[1]=code2;
5704 o->parms.parms[2]=code3;
5706 return o;
5708 f++;
5709 if (chkamt < 0 || stricmp(f->name,name)) break;
5711 return NULL;
5717 //------------------------------------------------------------------------------
5718 opcodeRec *nseel_translate(compileContext *ctx, const char *tmp, size_t tmplen) // tmplen 0 = null term
5720 // this depends on the string being nul terminated eventually, tmplen is used more as a hint than anything else
5721 if ((tmp[0] == '0' || tmp[0] == '$') && toupper_safe(tmp[1])=='X')
5723 char *p;
5724 return nseel_createCompiledValue(ctx,(EEL_F)strtoul(tmp+2,&p,16));
5726 else if (tmp[0] == '$')
5728 if (tmp[1] == '~')
5730 char *p=(char*)tmp+2;
5731 unsigned int v=(unsigned int) strtoul(tmp+2,&p,10);
5732 if (v>53) v=53;
5733 return nseel_createCompiledValue(ctx,(EEL_F)((((WDL_INT64)1) << v) - 1));
5735 else if (!tmplen ? !stricmp(tmp,"$E") : (tmplen == 2 && !strnicmp(tmp,"$E",2)))
5736 return nseel_createCompiledValue(ctx,(EEL_F)2.718281828459045);
5737 else if (!tmplen ? !stricmp(tmp, "$PI") : (tmplen == 3 && !strnicmp(tmp, "$PI", 3)))
5738 return nseel_createCompiledValue(ctx,(EEL_F)3.141592653589793);
5739 else if (!tmplen ? !stricmp(tmp, "$PHI") : (tmplen == 4 && !strnicmp(tmp, "$PHI", 4)))
5740 return nseel_createCompiledValue(ctx,(EEL_F)1.6180339887498948);
5741 else if ((!tmplen || tmplen == 4) && tmp[1] == '\'' && tmp[2] && tmp[3] == '\'')
5742 return nseel_createCompiledValue(ctx,(EEL_F)tmp[2]);
5743 else return NULL;
5745 else if (tmp[0] == '\'')
5747 char b[64];
5748 int x,sz;
5749 unsigned int rv=0;
5751 if (!tmplen) // nul terminated tmplen, calculate a workable length
5753 // faster than strlen(tmp) if tmp is large, we'll never need more than ~18 chars anyway
5754 while (tmplen < 32 && tmp[tmplen]) tmplen++;
5757 sz = tmplen > 0 ? nseel_filter_escaped_string(b,sizeof(b),tmp+1, tmplen - 1, '\'') : 0;
5759 if (sz > 4)
5761 if (ctx->last_error_string[0]) lstrcatn(ctx->last_error_string, ", ", sizeof(ctx->last_error_string));
5762 snprintf_append(ctx->last_error_string,sizeof(ctx->last_error_string),"multi-byte character '%.5s...' too long",b);
5763 return NULL; // do not allow 'xyzxy', limit to 4 bytes
5766 for (x=0;x<sz;x++) rv = (rv<<8) + ((unsigned char*)b)[x];
5767 return nseel_createCompiledValue(ctx,(EEL_F)rv);
5769 else if (tmp[0] == '#')
5771 char buf[2048];
5772 if (!tmplen) while (tmplen < sizeof(buf)-1 && tmp[tmplen]) tmplen++;
5773 else if (tmplen > sizeof(buf)-1) tmplen = sizeof(buf)-1;
5774 memcpy(buf,tmp,tmplen);
5775 buf[tmplen]=0;
5776 if (ctx->onNamedString)
5778 if (tmplen>0 && buf[1]&&ctx->function_curName)
5780 int err=0;
5781 opcodeRec *r = nseel_resolve_named_symbol(ctx,nseel_createCompiledValuePtr(ctx,NULL,buf),-1, &err);
5782 if (r)
5784 if (r->opcodeType!=OPCODETYPE_VALUE_FROM_NAMESPACENAME)
5786 r->opcodeType = OPCODETYPE_DIRECTVALUE;
5787 r->parms.dv.directValue = ctx->onNamedString(ctx->caller_this,buf+1);
5788 r->parms.dv.valuePtr=NULL;
5790 return r;
5792 if (err) return NULL;
5795 // if not namespaced symbol, return directly
5796 if (!buf[1])
5798 opcodeRec *r=newOpCode(ctx,NULL,OPCODETYPE_DIRECTVALUE_TEMPSTRING);
5799 if (r) r->parms.dv.directValue = -10000.0;
5800 return r;
5802 return nseel_createCompiledValue(ctx,ctx->onNamedString(ctx->caller_this,buf+1));
5805 return nseel_createCompiledValue(ctx,(EEL_F)atof(tmp));
5808 void NSEEL_VM_set_var_resolver(NSEEL_VMCTX _ctx, EEL_F *(*res)(void *userctx, const char *name), void *userctx)
5810 compileContext *ctx = (compileContext *)_ctx;
5811 if (ctx)
5813 ctx->getVariable = res;
5814 ctx->getVariable_userctx = userctx;
5819 #if defined(__ppc__) || defined(EEL_TARGET_PORTABLE)
5820 // blank stubs
5821 void eel_enterfp(int s[2]) {}
5822 void eel_leavefp(int s[2]) {}
5823 #endif