2 Expression Evaluator Library (NS-EEL) v2
3 Copyright (C) 2004-2013 Cockos Incorporated
4 Copyright (C) 1999-2003 Nullsoft, Inc.
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"
34 #include "../wdlcstring.h"
36 #if !defined(EEL_TARGET_PORTABLE) && !defined(_WIN32)
43 #include <libkern/OSCacheControl.h>
46 #define NSEEL_VARS_MALLOC_CHUNKSIZE 8
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; }
56 #define RET_MINUS1_FAIL(x) return -1;
60 FILE *g_eel_dump_fp
, *g_eel_dump_fp2
;
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
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
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)
81 x86_64: r12 is a pointer to ram_state->blocks
82 x86_64: r13 is a pointer to closenessfactor
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"
103 #include "glue_port.h"
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"
124 #include "glue_x86.h"
128 #ifndef GLUE_INVSQRT_NEEDREPL
129 #define GLUE_INVSQRT_NEEDREPL 0
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
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
)
163 while (byteoffs
-->0 && *exp
) if (*exp
++ =='\n') 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)
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
180 OPCODETYPE_VARPTRPTR
,
186 OPCODETYPE_MOREPARAMS
,
198 struct opcodeRec
*parms
[3];
201 EEL_F
*valuePtr
; // if direct value, valuePtr can be cached
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
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)),
227 memset(rec
,0,sizeof(*rec
));
228 rec
->opcodeType
= opType
;
232 char *p
= (char *)(rec
+1);
233 memcpy(p
,str
,str_sz
);
247 #ifndef EEL_DOESNT_NEED_EXEC_PERMS
248 static int eel_get_page_size(void)
254 const int ps
= (int)sysconf(_SC_PAGESIZE
);
255 pagesize
= wdl_max(ps
, 4096);
257 SYSTEM_INFO inf
= { 0 };
259 pagesize
= wdl_max(inf
.dwPageSize
, 4096);
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
)
278 const size_t sz
= sizeof(*llb
) + llb
->sizealloc
;
281 VirtualProtect(llb
,sz
,exec
? (PAGE_EXECUTE_READ
) : (PAGE_READWRITE
),&ov
);
282 FlushInstructionCache(GetCurrentProcess(),llb
,sz
);
284 mprotect(llb
,sz
,exec
? (PROT_READ
|PROT_EXEC
) : (PROT_READ
|PROT_WRITE
));
286 if (exec
) sys_icache_invalidate(llb
,sz
);
289 WDL_ASSERT((((INT_PTR
)llb
) & (eel_get_page_size()-1)) == 0);
290 WDL_ASSERT((sz
& (eel_get_page_size()-1)) == 0);
296 static void freeBlocks(llBlock
**start
, int is_code
);
298 static int __growbuf_resize(eel_growbuf
*buf
, int newsize
)
304 buf
->alloc
=buf
->size
=0;
308 if (newsize
> buf
->alloc
)
310 const int newalloc
= newsize
+ 4096 + newsize
/2;
311 void *newptr
= realloc(buf
->ptr
,newalloc
);
314 newptr
= malloc(newalloc
);
315 if (!newptr
) return 1;
316 if (buf
->ptr
&& buf
->size
) memcpy(newptr
,buf
->ptr
,buf
->size
);
331 #define DECL_ASMFUNC(x) void nseel_asm_##x(void);
334 void _asm_megabuf(void);
335 void _asm_gmegabuf(void);
340 DECL_ASMFUNC(booltofp
)
341 DECL_ASMFUNC(fptobool
)
342 DECL_ASMFUNC(fptobool_rev
)
368 DECL_ASMFUNC(repeatwhile
)
370 DECL_ASMFUNC(equal_exact
)
371 DECL_ASMFUNC(notequal_exact
)
372 DECL_ASMFUNC(notequal
)
375 DECL_ASMFUNC(beloweq
)
376 DECL_ASMFUNC(aboveeq
)
378 DECL_ASMFUNC(assign_fromfp
)
379 DECL_ASMFUNC(assign_fast
)
380 DECL_ASMFUNC(assign_fast_fromfp
)
385 DECL_ASMFUNC(add_op_fast
)
386 DECL_ASMFUNC(sub_op_fast
)
391 DECL_ASMFUNC(mul_op_fast
)
392 DECL_ASMFUNC(div_op_fast
)
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
);
422 static void *NSEEL_PProc_Stack(void *data
, int data_size
, compileContext
*ctx
)
424 codeHandleType
*ch
=ctx
->tmpCodeHandle
;
428 UINT_PTR m1
=(UINT_PTR
)(NSEEL_STACK_SIZE
* sizeof(EEL_F
) - 1);
429 UINT_PTR stackptr
= ((UINT_PTR
) (&ch
->stack
));
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
441 static void *NSEEL_PProc_Stack_PeekInt(void *data
, int data_size
, compileContext
*ctx
, INT_PTR offs
)
443 codeHandleType
*ch
=ctx
->tmpCodeHandle
;
447 UINT_PTR m1
=(UINT_PTR
)(NSEEL_STACK_SIZE
* sizeof(EEL_F
) - 1);
448 UINT_PTR stackptr
= ((UINT_PTR
) (&ch
->stack
));
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
460 static void *NSEEL_PProc_Stack_PeekTop(void *data
, int data_size
, compileContext
*ctx
)
462 codeHandleType
*ch
=ctx
->tmpCodeHandle
;
466 UINT_PTR stackptr
= ((UINT_PTR
) (&ch
->stack
));
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
);
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
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;
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
521 #if GLUE_MAX_SPILL_REGS > 0 && GLUE_HAS_FPREG2 <= 0
522 #error GLUE_MAX_SPILL_REGS requires GLUE_HAS_FPREG2
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
529 #define BIF_SECONDLASTPARMST 0
530 #define BIF_LAZYPARMORDERING 0
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)
538 #ifndef BIF_GETFPSTACKUSE
539 #define BIF_GETFPSTACKUSE(x) (7 - (((x)>>16)&7))
542 #define BIF_REVERSEFPORDER 0
543 #define BIF_FPSTACKUSE(x) 0
544 #define BIF_GETFPSTACKUSE(x) 0
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
)); }
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
} },
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), },
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
;
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
;
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
;
659 const int fn1size
= (int) (sizeof(fnTable1
)/sizeof(fnTable1
[0]));
663 NSEEL_HOSTSTUB_EnterMutex();
664 if (!sorted
) qsort(fnTable1
,fn1size
,sizeof(fnTable1
[0]),funcTypeCmp
);
666 NSEEL_HOSTSTUB_LeaveMutex();
668 idx
=functable_lowerbound(fnTable1
,fn1size
,name
,&match
);
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
);
682 while (idx
>0 && !stricmp(tab
->list
[idx
-1].name
,name
)) idx
--;
683 *mchk
= tab
->list_size
- 1 - idx
;
685 return tab
->list
+ idx
;
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
)
700 if (idx
>=0 && idx
< tab
->list_size
)
701 return tab
->list
+ idx
;
707 int NSEEL_init() // returns 0 on success
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
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)
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;
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
));
764 destination
->list
= (functionType
*)nv
;
766 if (destination
->list
)
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
;
791 r
->afunc
= code_startaddr
;
793 r
->replptrs
[0] = fptr
;
794 r
->replptrs
[1] = fptr2
;
799 //---------------------------------------------------------------------------------------------------------------
800 static void freeBlocks(llBlock
**start
, int is_code
)
806 llBlock
*llB
= s
->next
;
807 #ifndef EEL_DOESNT_NEED_EXEC_PERMS
811 VirtualFree(s
, 0, MEM_RELEASE
);
813 munmap(s
,sizeof(*s
) + s
->sizealloc
);
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
;
850 #ifndef EEL_DOESNT_NEED_EXEC_PERMS
853 const int code_page_size
= eel_get_page_size();
854 alloc_amt
= (sizeof(*llb
) + size
+ code_page_size
- 1) & ~(code_page_size
-1);
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
))
866 VirtualProtect(llb
, alloc_amt
, PAGE_READWRITE
, &ov
);
870 llb
= (llBlock
*)VirtualAlloc(NULL
,alloc_amt
,MEM_COMMIT
,PAGE_READWRITE
);
872 if (llb
== NULL
) return NULL
;
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);
877 llb
= (llBlock
*)mmap(NULL
,alloc_amt
, PROT_READ
|PROT_WRITE
,MAP_ANONYMOUS
|MAP_PRIVATE
,-1,0);
879 if (llb
== MAP_FAILED
) return NULL
;
881 alloc_amt
-= sizeof(*llb
);
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);
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
;
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
);
915 r
->parms
.dv
.directValue
= value
;
920 opcodeRec
*nseel_createCompiledValuePtr(compileContext
*ctx
, EEL_F
*addrValue
, const char *namestr
)
922 opcodeRec
*r
=newOpCode(ctx
,namestr
,OPCODETYPE_VARPTR
);
925 r
->parms
.dv
.valuePtr
=addrValue
;
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];
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;
951 if (name_len
== l
&& !stricmp(nmchk
,name
)) return 1;
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;
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);
982 rec
->parms
.dv
.valuePtr
= a
;
983 sname
[0]=0; // for dump_ops compat really, but this shouldn't be needed anyway
988 if (ctx
->function_curName
)
990 if (!strnicmp(sname
,"this.",5))
995 else if (!stricmp(sname
,"this"))
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
];
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;
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);
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
];
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
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);
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
);
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
;
1103 if (parmcnt
== 2 && !stricmp("loop",sname
) && !prevent_function_calls
)
1105 rec
->opcodeType
= OPCODETYPE_FUNC2
;
1106 rec
->fntype
= FN_LOOP
;
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
;
1116 const char * const ourcall
= sname
+rel_prefix_len
;
1117 const size_t ourcall_len
= strlen(ourcall
);
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
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
;
1135 break; // found exact match, finished
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
;
1151 if (match_parmcnt
[3]<0) match_parmcnt
[3]=fr
->num_params
;
1155 if (fr
) break; // found exact match, finished
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
;
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
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);
1190 ctx
->function_usesNamespaces
=1;
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
);
1207 rec
->namespaceidx
= rel_prefix_idx
;
1208 rec
->fntype
= FUNCTYPE_EELFUNC
;
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;
1222 #ifdef NSEEL_EEL1_COMPAT_MODE
1223 if (!stricmp(sname
,"assign"))
1227 rec
->opcodeType
= OPCODETYPE_FUNC2
;
1228 rec
->fntype
= FN_ASSIGN
;
1231 if (match_parmcnt_pos
< 3) match_parmcnt
[match_parmcnt_pos
++] = 2;
1233 else if (!stricmp(sname
,"if"))
1237 rec
->opcodeType
= OPCODETYPE_FUNC3
;
1238 rec
->fntype
= FN_IF_ELSE
;
1241 if (match_parmcnt_pos
< 3) match_parmcnt
[match_parmcnt_pos
++] = 3;
1243 else if (!stricmp(sname
,"equal"))
1247 rec
->opcodeType
= OPCODETYPE_FUNC2
;
1248 rec
->fntype
= FN_EQ
;
1251 if (match_parmcnt_pos
< 3) match_parmcnt
[match_parmcnt_pos
++] = 2;
1253 else if (!stricmp(sname
,"below"))
1257 rec
->opcodeType
= OPCODETYPE_FUNC2
;
1258 rec
->fntype
= FN_LT
;
1261 if (match_parmcnt_pos
< 3) match_parmcnt
[match_parmcnt_pos
++] = 2;
1263 else if (!stricmp(sname
,"above"))
1267 rec
->opcodeType
= OPCODETYPE_FUNC2
;
1268 rec
->fntype
= FN_GT
;
1271 if (match_parmcnt_pos
< 3) match_parmcnt
[match_parmcnt_pos
++] = 2;
1273 else if (!stricmp(sname
,"bnot"))
1277 rec
->opcodeType
= OPCODETYPE_FUNC1
;
1278 rec
->fntype
= FN_NOT
;
1281 if (match_parmcnt_pos
< 3) match_parmcnt
[match_parmcnt_pos
++] = 1;
1283 else if (!stricmp(sname
,"megabuf"))
1287 rec
->opcodeType
= OPCODETYPE_FUNC1
;
1288 rec
->fntype
= FN_MEMORY
;
1291 if (match_parmcnt_pos
< 3) match_parmcnt
[match_parmcnt_pos
++] = 1;
1293 else if (!stricmp(sname
,"gmegabuf"))
1297 rec
->opcodeType
= OPCODETYPE_FUNC1
;
1298 rec
->fntype
= FN_GMEMORY
;
1301 if (match_parmcnt_pos
< 3) match_parmcnt
[match_parmcnt_pos
++] = 1;
1305 // convert legacy pow() to FN_POW
1306 if (!stricmp("pow",sname
))
1310 rec
->opcodeType
= OPCODETYPE_FUNC2
;
1311 rec
->fntype
= FN_POW
;
1314 if (match_parmcnt_pos
< 3) match_parmcnt
[match_parmcnt_pos
++] = 2;
1316 else if (!stricmp("__denormal_likely",sname
) || !stricmp("__denormal_unlikely",sname
))
1320 rec
->opcodeType
= OPCODETYPE_FUNC1
;
1321 rec
->fntype
= !stricmp("__denormal_likely",sname
) ? FN_DENORMAL_LIKELY
: FN_DENORMAL_UNLIKELY
;
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
;
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;
1346 if (match_parmcnt_pos
< 3) match_parmcnt
[match_parmcnt_pos
++] = (f
->nParams
&FUNCTIONTYPE_PARAMETERCOUNTMASK
);
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
);
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;
1372 opcodeRec
*nseel_setCompiledFunctionCallParameters(compileContext
*ctx
, opcodeRec
*fn
, opcodeRec
*code1
, opcodeRec
*code2
, opcodeRec
*code3
, opcodeRec
*postCode
, int *errOut
)
1376 if (!fn
|| fn
->opcodeType
!= OPCODETYPE_VARPTR
|| !fn
->relname
|| !fn
->relname
[0])
1380 fn
->parms
.parms
[0] = code1
;
1381 fn
->parms
.parms
[1] = code2
;
1382 fn
->parms
.parms
[2] = code3
;
1386 opcodeRec
*prni
=fn
->parms
.parms
[x
];
1387 while (prni
&& np
< NSEEL_MAX_EELFUNC_PARAMETERS
)
1389 const int isMP
= prni
->opcodeType
== OPCODETYPE_MOREPARAMS
;
1392 prni
= prni
->parms
.parms
[1];
1395 r
= nseel_resolve_named_symbol(ctx
, fn
, np
<1 ? 1 : np
,errOut
);
1398 if (code1
&& r
->opcodeType
== OPCODETYPE_FUNC1
&& r
->fntype
== FN_WHILE
)
1400 // change while(x) (postcode) to be
1401 // while ((x) ? (postcode;1) : 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
1411 snprintf_append(ctx
->last_error_string
,sizeof(ctx
->last_error_string
),"syntax error following function");
1420 struct eelStringSegmentRec
*nseel_createStringSegmentRec(compileContext
*ctx
, const char *str
, int len
)
1422 struct eelStringSegmentRec
*r
= newTmpBlock(ctx
,sizeof(struct eelStringSegmentRec
));
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
));
1442 opcodeRec
*nseel_createMoreParametersOpcode(compileContext
*ctx
, opcodeRec
*code1
, opcodeRec
*code2
)
1444 opcodeRec
*r
=code1
&& code2
? newOpCode(ctx
,NULL
,OPCODETYPE_MOREPARAMS
) : NULL
;
1447 r
->parms
.parms
[0] = code1
;
1448 r
->parms
.parms
[1] = code2
;
1454 opcodeRec
*nseel_createIfElse(compileContext
*ctx
, opcodeRec
*code1
, opcodeRec
*code2
, opcodeRec
*code3
)
1456 opcodeRec
*r
=code1
? newOpCode(ctx
,NULL
,OPCODETYPE_FUNC3
) : NULL
;
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
;
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
;
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
);
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
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
)
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()
1545 n
= strlen(nameptr
);
1546 if (n
> sizeof(subfr
->fname
)-1) n
=sizeof(subfr
->fname
)-1;
1547 memcpy(subfr
->fname
,nameptr
,n
);
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
;
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
)
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
1572 while (lfp
>0 && prefix
[lfp
-1] != '.') 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);
1582 if (lfp
>0) nm
[lfp
++] = '.';
1583 memcpy(nm
+lfp
,relname
,lrn
);
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
,};
1601 #define RF(x) return (void*)nseel_asm_##x
1604 *abiInfo
=BIF_LASTPARMONSTACK
|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL
;
1607 *abiInfo
=BIF_LASTPARMONSTACK
|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL
;
1610 *abiInfo
=BIF_LASTPARMONSTACK
|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL
;
1613 *abiInfo
=BIF_LASTPARMONSTACK
|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL
;
1616 *abiInfo
=BIF_LASTPARMONSTACK
|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL
;
1619 *abiInfo
=BIF_LASTPARMONSTACK
|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL
;
1622 *abiInfo
=BIF_LASTPARMONSTACK
|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL
;
1625 *abiInfo
=BIF_LASTPARMONSTACK
|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL
;
1628 *abiInfo
=BIF_LASTPARMONSTACK
|BIF_CLEARDENORMAL
;
1629 *replList
= pow_replptrs
;
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
;
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
;
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
;
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
;
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
;
1656 *abiInfo
= BIF_RETURNSONSTACK
|BIF_TWOPARMSONFPSTACK
|BIF_FPSTACKUSE(1)|BIF_CLEARDENORMAL
;
1659 *abiInfo
= BIF_FPSTACKUSE(1)|BIF_CLEARDENORMAL
;
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);
1664 *abiInfo
= BIF_RETURNSONSTACK
|BIF_TWOPARMSONFPSTACK_LAZY
|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL
;
1667 *abiInfo
= BIF_RETURNSONSTACK
|BIF_TWOPARMSONFPSTACK
|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL
;
1670 *abiInfo
= BIF_RETURNSONSTACK
|BIF_TWOPARMSONFPSTACK
|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL
;
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
);
1677 *abiInfo
= BIF_TWOPARMSONFPSTACK_LAZY
|BIF_RETURNSBOOL
|BIF_FPSTACKUSE(2);
1680 *abiInfo
=BIF_TWOPARMSONFPSTACK_LAZY
|BIF_RETURNSBOOL
|BIF_FPSTACKUSE(2);
1683 *abiInfo
=BIF_TWOPARMSONFPSTACK_LAZY
|BIF_RETURNSBOOL
|BIF_FPSTACKUSE(2);
1686 *abiInfo
=BIF_TWOPARMSONFPSTACK_LAZY
|BIF_RETURNSBOOL
|BIF_FPSTACKUSE(2);
1688 case FN_LOGICAL_AND
:
1689 *abiInfo
= BIF_RETURNSBOOL
;
1692 *abiInfo
= BIF_RETURNSBOOL
;
1696 *abiInfo
= BIF_TWOPARMSONFPSTACK
|BIF_RETURNSBOOL
|BIF_FPSTACKUSE(2);
1699 *abiInfo
= BIF_TWOPARMSONFPSTACK
|BIF_RETURNSBOOL
|BIF_REVERSEFPORDER
|BIF_FPSTACKUSE(2);
1700 #if BIF_REVERSEFPORDER > 0
1706 *abiInfo
= BIF_TWOPARMSONFPSTACK
|BIF_RETURNSBOOL
|BIF_REVERSEFPORDER
|BIF_FPSTACKUSE(2);
1707 #if BIF_REVERSEFPORDER > 0
1713 *abiInfo
= BIF_TWOPARMSONFPSTACK
|BIF_RETURNSBOOL
|BIF_FPSTACKUSE(2);
1717 #define RF(x) return (void*)_asm_##x
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
;
1732 static void *replptrs
[4]={&__NSEEL_RAMAllocGMEM
,};
1733 *replList
= replptrs
;
1734 *abiInfo
=BIF_LASTPARMONSTACK
|BIF_FPSTACKUSE(1)|BIF_CLEARDENORMAL
;
1735 *pProc
=NSEEL_PProc_GRAM
;
1741 case FUNCTYPE_FUNCTIONTYPEREC
:
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
;
1755 *abiInfo
= p
->nParams
& BIF_NPARAMS_MASK
;
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
;
1772 static void *nseel_getEELFunctionAddress(compileContext
*ctx
,
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)
1798 while (p
>= prefix_buf
&& *p
!= '.') p
--;
1799 if (p
> prefix_buf
) *p
=0;
1801 if (fn
->parameterAsNamespaceMask
)
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
;
1822 combineNamespaceFields(tmp
,namespacePathToThis
,p
,ordered_parmptrs
[x
]->namespaceidx
);
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
);
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
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
;
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
))
1894 *endP
= ((char *)1) + fn
->startptr_base_size
;
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
);
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
1914 fn
->startptr_size
=sz
;
1921 unsigned char *codeCall
;
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
--;
1930 void *f
=GLUE_realAddress(nseel_asm_fcall
,&sz
);
1931 fn
->startptr
= newTmpBlock(ctx
,sz
);
1934 memcpy(fn
->startptr
,f
,sz
);
1935 EEL_GLUE_set_immediate(fn
->startptr
,(INT_PTR
)codeCall
);
1936 fn
->startptr_size
= sz
;
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
;
1960 return fn
->startptr
;
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
));
1984 op
= op
->parms
.parms
[1];
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
;
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
)
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
)
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
)
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];
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];
2058 else if (op
->parms
.parms
[0]->opcodeType
== OPCODETYPE_FUNC2
)
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
;
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
;
2094 int a
= (int) fabs(op
->parms
.parms
[1]->parms
.dv
.directValue
);
2097 #ifdef GLUE_MOD_IS_64
2098 ret
= ((WDL_INT64
) fabs(op
->parms
.parms
[0]->parms
.dv
.directValue
)) % a
;
2100 ret
= ((int) fabs(op
->parms
.parms
[0]->parms
.dv
.directValue
)) % a
;
2102 if (WDL_NOT_NORMALLY(ret
<0)) ret
= -ret
;
2104 RESTART_DIRECTVALUE(ret
);
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
;
2140 if (!(WDL_INT64
)dvalue
)
2143 op
->opcodeType
= OPCODETYPE_FUNC1
;
2144 op
->fntype
= FUNCTYPE_FUNCTIONTYPEREC
;
2146 if (dv0
) op
->parms
.parms
[0] = op
->parms
.parms
[1];
2155 op
->opcodeType
= OPCODETYPE_FUNC1
;
2156 op
->fntype
= FN_UMINUS
;
2157 op
->parms
.parms
[0] = op
->parms
.parms
[1];
2162 WDL_FALLTHROUGH
; // fall through, if dv1 we can remove +0.0
2167 memcpy(op
,op
->parms
.parms
[!!dv0
],sizeof(*op
));
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
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
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
;
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
));
2209 if (fabs(dvalue
) < 1e-30)
2211 RESTART_DIRECTVALUE(1.0);
2214 if (fabs(dvalue
-1.0) < 1e-30)
2216 memcpy(op
,op
->parms
.parms
[0],sizeof(*op
));
2222 // pow(constant, x) = exp((x) * ln(constant)), if constant>0
2223 // opcodeRec *parm0 = op->parms.parms[0];
2226 static functionType
*expcpy
;
2229 expcpy
= nseel_getFunctionByName(NULL
,"exp",NULL
);
2230 if (WDL_NOT_NORMALLY(!expcpy
)) break;
2234 if (fabs(dvalue
-1.0) < 1e-30)
2236 RESTART_DIRECTVALUE(1.0);
2241 if (fabs(dvalue
-1.0) < 1e-9)
2243 // caller wanted e^x
2244 op
->parms
.parms
[0]=op
->parms
.parms
[1];
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.
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
;
2271 const int a
= (int) dvalue
;
2274 RESTART_DIRECTVALUE(0.0);
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
));
2288 // change to a multiply
2291 op
->fntype
= FN_MULTIPLY
;
2296 double d
= 1.0/dvalue
;
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
;
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
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)
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];
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];
2353 case FN_LOGICAL_AND
:
2357 if (fabs(dvalue
) < NSEEL_CLOSEFACTOR
)
2359 // 0 && expr, replace with 0
2360 RESTART_DIRECTVALUE(0.0);
2364 // 1 && expr, replace with 0 != expr
2366 op
->parms
.parms
[0]->parms
.dv
.valuePtr
=NULL
;
2367 op
->parms
.parms
[0]->parms
.dv
.directValue
= 0.0;
2373 if (fabs(dvalue
) < NSEEL_CLOSEFACTOR
)
2378 // expr has no consequence, drop it
2379 RESTART_DIRECTVALUE(0.0);
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;
2391 // expr && 1, replace with expr != 0
2393 op
->parms
.parms
[1]->parms
.dv
.valuePtr
=NULL
;
2394 op
->parms
.parms
[1]->parms
.dv
.directValue
= 0.0;
2402 if (fabs(dvalue
) >= NSEEL_CLOSEFACTOR
)
2404 // 1 || expr, replace with 1
2405 RESTART_DIRECTVALUE(1.0);
2409 // 0 || expr, replace with 0 != expr
2411 op
->parms
.parms
[0]->parms
.dv
.valuePtr
=NULL
;
2412 op
->parms
.parms
[0]->parms
.dv
.directValue
= 0.0;
2418 if (fabs(dvalue
) >= NSEEL_CLOSEFACTOR
)
2423 // expr has no consequence, drop it and return 1
2424 RESTART_DIRECTVALUE(1.0);
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;
2436 // expr || 0, replace with expr != 0
2438 op
->parms
.parms
[1]->parms
.dv
.valuePtr
=NULL
;
2439 op
->parms
.parms
[1]->parms
.dv
.directValue
= 0.0;
2446 // general optimization of two parameters
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
;
2463 case OPCODETYPE_VARPTRPTR
:
2464 if (first_parm
->parms
.dv
.valuePtr
&& first_parm
->parms
.dv
.valuePtr
==second_parm
->parms
.dv
.valuePtr
) second_parm
=NULL
;
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
;
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
;
2504 case FN_LOGICAL_AND
:
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];
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];
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
));
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];
2542 if (op
->fntype
>= FN_NONCONST_BEGIN
&& op
->fntype
< FUNCTYPE_SIMPLEMAX
) retv
|=1;
2546 else if (op
->fntype
== FUNCTYPE_FUNCTIONTYPEREC
&& op
->fn
)
2550 probably worth doing reduction on:
2551 _divop (constant change to multiply)
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
)
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
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
;
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
2616 // unknown or eel func, assume non-const
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
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
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
2656 memcpy(op
,op
->parms
.parms
[idx1
],sizeof(*op
));
2660 op
->opcodeType
= OPCODETYPE_FUNC2
;
2661 op
->fntype
= FN_JOIN_STATEMENTS
;
2663 op
->parms
.parms
[0] = op
->parms
.parms
[idx1
];
2664 op
->parms
.parms
[1] = op
->parms
.parms
[idx2
];
2665 op
->parms
.parms
[2] = NULL
;
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)
2679 static int generateValueToReg(compileContext
*ctx
, opcodeRec
*op
, unsigned char *bufOut
, int whichReg
, const namespaceInformation
*functionPrefix
, int allowCache
)
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;
2690 if (ctx
->isGeneratingCommonFunction
)
2691 b
= newCtxDataBlock(sizeof(EEL_F
),sizeof(EEL_F
));
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
);
2703 b
= nseel_int_register_var(ctx
,nm
,0,NULL
);
2704 if (!b
) RET_MINUS1_FAIL("error registering var")
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
;
2730 if (r
->parms
.dv
.directValue
== op
->parms
.dv
.directValue
&& (b
=r
->parms
.dv
.valuePtr
)) break;
2731 r
=(opcodeRec
*)r
->fn
;
2737 if (ctx
->isGeneratingCommonFunction
)
2738 b
= newCtxDataBlock(sizeof(EEL_F
),sizeof(EEL_F
));
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
);
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
;
2775 funcsz
=compileOpcodes(ctx
,rec
,p
, funcsz
, computTableSize
,namespacePathToThis
,supportedReturnValues
, rvType
,fpStackUsage
, canHaveDenormalOutput
);
2776 if (funcsz
<0) return NULL
;
2779 #if GLUE_FUNC_LEAVE_SIZE > 0
2780 memcpy(p
,&GLUE_FUNC_LEAVE
,GLUE_FUNC_LEAVE_SIZE
);
2781 p
+=GLUE_FUNC_LEAVE_SIZE
;
2783 memcpy(p
,&GLUE_RET
,sizeof(GLUE_RET
)); p
+=sizeof(GLUE_RET
);
2784 #if defined(__arm__) || defined(__aarch64__)
2785 __clear_cache(newblock2
,p
);
2788 ctx
->l_stats
[2]+=funcsz
+2;
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
2797 int cfunc_abiinfo
=0;
2798 int local_fpstack_use
=0; // how many items we have pushed onto the fp stack
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])
2804 #if GLUE_MAX_SPILL_REGS > 0
2808 NSEEL_PPPROC preProc
=0;
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;
2834 const int max_params
=32768; // sanity check, the stack is free to grow on x86/x86-64
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
)
2844 opcodeRec
*prni
=op
->parms
.parms
[x
];
2847 const int isMP
= prni
->opcodeType
== OPCODETYPE_MOREPARAMS
;
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
;
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
;
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
)
2885 opcodeRec
*prni
=op
->parms
.parms
[x
];
2888 const int isMP
= prni
->opcodeType
== OPCODETYPE_MOREPARAMS
;
2889 opcodeRec
*r
= isMP
? prni
->parms
.parms
[0] : prni
;
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");
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")
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
];
2926 int canHaveDenorm
=0;
2927 int subfpstackuse
=0;
2928 int rvt
=RETURNVALUE_NORMAL
;
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");
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
;
2960 #if GLUE_MAX_FPSTACK_SIZE > 0
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")
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")
2980 memcpy(bufOut
,func
,func_size
);
2981 NSEEL_PProc_Stack(bufOut
,func_size
,ctx
);
2985 else if (func
== nseel_asm_stack_peek
)
2987 int f
= (int) op
->parms
.parms
[0]->parms
.dv
.directValue
;
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")
2995 memcpy(bufOut
,func
,func_size
);
2996 NSEEL_PProc_Stack_PeekTop(bufOut
,func_size
,ctx
);
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")
3007 memcpy(bufOut
,func
,func_size
);
3008 NSEEL_PProc_Stack_PeekInt(bufOut
,func_size
,ctx
,f
*sizeof(EEL_F
));
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;
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
;
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
3062 rvt
= RETURNVALUE_BOOL
;
3065 if (canHaveDenorm
&& canHaveDenormalOutput
) *canHaveDenormalOutput
= 1;
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
))
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")
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
++;
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")
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
);
3104 local_fpstack_use
++;
3108 if (subfpstackuse
+local_fpstack_use
> *fpStackUsage
) *fpStackUsage
= subfpstackuse
+local_fpstack_use
;
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
;
3125 func
= nseel_asm_assign_fast
;
3130 if (rvt
== RETURNVALUE_FPSTACK
)
3132 cfunc_abiinfo
|= BIF_LASTPARMONSTACK
;
3133 func
= nseel_asm_assign_fromfp
;
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
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
);
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
);
3176 #if GLUE_MAX_FPSTACK_SIZE > 0
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
);
3189 local_fpstack_use
--;
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
;
3211 const int req
= RETURNVALUE_FPSTACK
;
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")
3218 #if GLUE_MAX_FPSTACK_SIZE > 0
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)
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
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
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
);
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
) :
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;
3257 if (func
== nseel_asm_bnot
&& rvt
== RETURNVALUE_BOOL_REVERSED
)
3259 // remove bnot, compileOpcodes() used fptobool_rev
3260 // case: !b ? 2 : 3, for example
3262 rvt
= RETURNVALUE_BOOL
;
3266 #if GLUE_MAX_FPSTACK_SIZE > 0
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
;
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
;
3292 if (bufOut_len
< parm_size
+ GLUE_MOV_PX_DIRECTVALUE_SIZE
) RET_MINUS1_FAIL("size, pxdvsz")
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
))
3308 if (bufOut_len
< sizeof(GLUE_FXCH
)) RET_MINUS1_FAIL("len,fxch")
3311 memcpy(bufOut
+parm_size
,GLUE_FXCH
,sizeof(GLUE_FXCH
));
3313 parm_size
+=sizeof(GLUE_FXCH
);
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
;
3340 if (cfunc_abiinfo
& (BIF_CLEARDENORMAL
| BIF_RETURNSBOOL
) ) *canHaveDenormalOutput
=0;
3341 else if (!(cfunc_abiinfo
& BIF_WONTMAKEDENORMAL
)) *canHaveDenormalOutput
=1;
3345 func
= GLUE_realAddress(func
,&func_size
);
3346 if (!func
) RET_MINUS1_FAIL("failrealladdrfunc")
3353 if (bufOut_len
< parm_size
+ func_size
) RET_MINUS1_FAIL("funcsz")
3357 unsigned char *p
=bufOut
+ parm_size
;
3358 memcpy(p
, func
, func_size
);
3360 parm_size
+= GLUE_FUSE(ctx
,p
,parm_size
,func_size
,fuse_flags
,
3361 #if GLUE_MAX_SPILL_REGS > 0
3367 p
= bufOut
+ parm_size
;
3369 if (preProc
) p
=preProc(p
,func_size
,ctx
);
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
;
3385 if (amt
> 4096) amt
=4096;
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;
3405 int last_nt_parm
=-1,last_nt_parm_mode
=0;
3408 opcodeRec
*parmptrs
[NSEEL_MAX_EELFUNC_PARAMETERS
];
3409 int cfp_numparams
=-1;
3410 int cfp_statesize
=0;
3411 EEL_F
**cfp_ptrs
=NULL
;
3418 for (x
=0; x
< 3; x
++) parmptrs
[x
] = op
->parms
.parms
[x
];
3420 if (op
->opcodeType
== OPCODETYPE_FUNCX
)
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
;
3431 prni
= prni
->parms
.parms
[1];
3437 n_params
= 1 + op
->opcodeType
- OPCODETYPE_FUNC1
;
3441 func
= nseel_getEELFunctionAddress(ctx
, op
,
3442 &cfp_numparams
,&cfp_statesize
,&cfp_ptrs
,
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
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
++)
3473 cfp_ptrs
[pn
] = pstate
+ pn
;
3478 // first process parameters that are non-trivial
3479 for (pn
=0; pn
< n_params
; pn
++)
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
);
3496 if (bufOut_len
< parm_size
+ (int)sizeof(GLUE_PUSH_P1PTR_AS_VALUE
)) RET_MINUS1_FAIL("eelfunc_size pushp1ptrasval")
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
;
3518 // pop non-trivial results into place
3519 if (last_nt_parm
>=0 && do_parms
)
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
;
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
;
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
]);
3557 // finally, set any trivial parameters
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")
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
);
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 :
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
);
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
);
3623 case OPCODETYPE_VARPTR
:
3625 const char *nm
= op
->relname
;
3629 for (wb
= 0; wb
< ctx
->varTable_numBlocks
; wb
++)
3631 char **plist
=ctx
->varTable_Names
[wb
];
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
]];
3641 fprintf(fp
," VP=%s\r\n", nm
?nm
: "(null)");
3644 case OPCODETYPE_VARPTRPTR
:
3645 fprintf(fp
, " VPP?\r\n");
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");
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);
3664 fprintf(fp
,"%*sINVALID PARM\r\n",indent_amt
+2,"");
3665 fprintf(fp
,"%*s}\r\n", indent_amt
, "");
3667 case OPCODETYPE_MOREPARAMS
:
3668 case OPCODETYPE_FUNC2
:
3669 if (op
->opcodeType
== OPCODETYPE_MOREPARAMS
)
3670 fprintf(fp
," MOREPARAMS {\r\n");
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");
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);
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);
3736 fprintf(fp
,"%*sINVALID PARM\r\n",indent_amt
+2,"");
3737 fprintf(fp
,"%*s}\r\n", indent_amt
, "");
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");
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);
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);
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);
3760 fprintf(fp
,"%*sINVALID PARM\r\n",indent_amt
+2,"");
3761 fprintf(fp
,"%*s}\r\n", indent_amt
, "");
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;
3773 #define CHECK_SIZE_FORJMP(x,y)
3774 #define RET_MINUS1_FAIL_FALLBACK(err,j) RET_MINUS1_FAIL(err)
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")
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
)
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);
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];
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
))
3825 #ifdef GLUE_MAX_JMPSIZE
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
;
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
);
3859 if (fUse2
> *fpStackUse
) *fpStackUse
=fUse2
;
3860 return rv_offset
+ parm_size
;
3862 #ifdef GLUE_MAX_JMPSIZE
3867 unsigned char *newblock2
, *p
;
3869 // encode as function call
3871 parm_size
= parm_size_pre
;
3873 if (op
->fntype
== FN_LOGICAL_AND
)
3875 stub
= GLUE_realAddress(nseel_asm_band
,&stubsize
);
3879 stub
= GLUE_realAddress(nseel_asm_bor
,&stubsize
);
3882 if (bufOut_len
< parm_size
+ stubsize
) RET_MINUS1_FAIL("band/bor len fail")
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
;
3902 if (op
->opcodeType
== OPCODETYPE_FUNC3
&& op
->fntype
== FN_IF_ELSE
) // special case: IF
3905 #ifdef GLUE_MAX_JMPSIZE
3908 int use_rv
= RETURNVALUE_IGNORE
;
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
;
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
);
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));
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
);
3961 if (fUse1
> *fpStackUse
) *fpStackUse
=fUse1
;
3963 return rv_offset
+ parm_size
;
3965 #ifdef GLUE_MAX_JMPSIZE
3968 unsigned char *newblock2
,*newblock3
,*ptr
;
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")
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
;
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
;
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")
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
;
4027 #ifndef GLUE_WHILE_END_NOJUMP
4028 unsigned char *jzoutpt
;
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")
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
;
4054 if (bufOut
) memcpy(bufOut
+ parm_size
, GLUE_WHILE_CHECK_RV
, sizeof(GLUE_WHILE_CHECK_RV
));
4055 parm_size
+=sizeof(GLUE_WHILE_CHECK_RV
);
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
);
4063 return rv_offset
+parm_size
;
4069 // special case: loop
4070 if (op
->opcodeType
== OPCODETYPE_FUNC2
&& op
->fntype
== FN_LOOP
)
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
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")
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
;
4102 unsigned char *skipptr1
,*loopdest
;
4104 #ifndef GLUE_LOOP_LOADCNT_SIZE
4105 #define GLUE_LOOP_LOADCNT_SIZE sizeof(GLUE_LOOP_LOADCNT)
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
;
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
;
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
);
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
;
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;
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
);
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;
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
);
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")
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
;
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;
4216 if (bufOut_len
< GLUE_MOV_PX_DIRECTVALUE_TOSTACK_SIZE
) RET_MINUS1_FAIL("direct fp fail 2")
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
;
4227 if (bufOut_len
< GLUE_MOV_PX_DIRECTVALUE_SIZE
)
4229 RET_MINUS1_FAIL("direct value fail 1")
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
)
4250 a
= compileEelFunctionCall(ctx
,op
,bufOut
,bufOut_len
,computTableSize
,namespacePathToThis
, calledRvType
,&fpUse1
,canHaveDenormalOutput
);
4252 if (fpUse1
> *fpStackUse
) *fpStackUse
= fpUse1
;
4258 a
= compileNativeFunctionCall(ctx
,op
,bufOut
,bufOut_len
,computTableSize
,namespacePathToThis
, calledRvType
,&fpUse1
,preferredReturnValues
,canHaveDenormalOutput
);
4260 if (fpUse1
> *fpStackUse
) *fpStackUse
= fpUse1
;
4266 RET_MINUS1_FAIL("default opcode fail")
4269 #ifdef DUMP_OPS_DURING_COMPILE
4271 int g_debugfp_indent
;
4272 int g_debugfp_histsz
=0;
4274 void dumpOp(compileContext
*ctx
, opcodeRec
*op
, int start
)
4280 static opcodeRec
**hist
;
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
;
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
);
4303 case OPCODETYPE_VARPTR
:
4304 if (op
->relname
&& op
->relname
[0])
4306 fprintf(g_debugfp
,"var %s",op
->relname
);
4311 for (wb
= 0; wb
< ctx
->varTable_numBlocks
; wb
++)
4313 char **plist
=ctx
->varTable_Names
[wb
];
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
]]);
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
);
4334 fprintf(g_debugfp
,"sf %d",op
->fntype
);
4338 fprintf(g_debugfp
,"\n");
4339 g_debugfp_indent
+=2;
4346 g_debugfp_indent
-=2;
4347 fprintf(g_debugfp
,"%*s}%p\n",g_debugfp_indent
," ",op
);
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
;
4361 #ifdef DUMP_OPS_DURING_COMPILE
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
4372 // dump opcode trees for verification, after optimizing
4375 fprintf(g_eel_dump_fp2
,"-- compileOpcodes generated %d bytes of code!\r\n",codesz
);
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");
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
4398 if (fpStackUse
) *fpStackUse
=fpsu
;
4400 if (bufOut
) bufOut
+= codesz
;
4401 bufOut_len
-= codesz
;
4404 if (code_returns
== RETURNVALUE_BOOL
&& !(supportedReturnValues
& RETURNVALUE_BOOL
) && supportedReturnValues
)
4407 void *stub
= GLUE_realAddress(nseel_asm_booltofp
,&stubsize
);
4408 if (!stub
|| bufOut_len
< stubsize
) RET_MINUS1_FAIL(stub
?"booltofp size":"booltfp addr")
4411 memcpy(bufOut
,stub
,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
;
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")
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
;
4451 if (rvType
) *rvType
= RETURNVALUE_FPSTACK
;
4456 if (code_returns
== RETURNVALUE_FPSTACK
)
4458 if (supportedReturnValues
& (RETURNVALUE_BOOL
|RETURNVALUE_BOOL_REVERSED
))
4463 if (supportedReturnValues
& RETURNVALUE_BOOL_REVERSED
)
4465 if (rvType
) *rvType
= RETURNVALUE_BOOL_REVERSED
;
4466 stub
= GLUE_realAddress(nseel_asm_fptobool_rev
,&stubsize
);
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")
4478 memcpy(bufOut
,stub
,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
;
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
;
4509 static void movestringover(char *str
, int amount
)
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
--;
4521 tmp
[l
]=0;//ensure we null terminate
4523 memcpy(str
+amount
,tmp
,l
+1);
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
;
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
;
4545 const char *_expression_end
;
4546 codeHandleType
*handle
;
4547 topLevelCodeSegmentRec
*startpts_tail
=NULL
;
4548 topLevelCodeSegmentRec
*startpts
=NULL
;
4549 _codeHandleFunctionRec
*oldCommonFunctionList
;
4551 void *curtabptr
=NULL
;
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
4567 // reset common compiled function code, forcing a recompile if shared
4568 _codeHandleFunctionRec
*a
= ctx
->functions_common
;
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;
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
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);
4619 memset(handle
,0,sizeof(codeHandleType
));
4621 ctx
->l_stats
[0] += (int)(_expression_end
- _expression
);
4622 ctx
->tmpCodeHandle
= handle
;
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];
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;
4646 // single out top level segment
4648 int had_something
= 0, pcnt
=0, pcnt2
=0;
4653 const char *p
=nseel_simple_tokenizer(&endptr
,_expression_end
,&l
,&state
);
4656 if (pcnt
|| pcnt2
) ctx
->gotEndOfInput
|=4;
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);
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;
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;
4705 const char *sp_save
;
4709 if (had_parms_locals
)
4711 expr
= p
-1; // begin compilation at this code!
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;
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
));
4747 if (isalpha_safe(*tok1
) || *tok1
== '_' || *tok1
== '#')
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
));
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
));
4769 if (tok1
&& maxcnt
> 0)
4771 char **ot
= ctx
->function_localTable_Names
[localTableContext
];
4772 const int osz
= ctx
->function_localTable_Size
[localTableContext
];
4776 ctx
->function_localTable_Names
[localTableContext
] = (char **)newTmpBlock(ctx
,sizeof(char *) * maxcnt
);
4778 if (ctx
->function_localTable_Names
[localTableContext
])
4781 if (osz
&& ot
) memcpy(ctx
->function_localTable_Names
[localTableContext
],ot
,sizeof(char *) * osz
);
4784 while (NULL
!= (tok1
= nseel_simple_tokenizer(&p
,endptr
,&tmplen
,NULL
)))
4786 if (tok1
[0] == ')') break;
4787 if (isalpha_safe(*tok1
) || *tok1
== '_' || *tok1
== '#')
4791 if (*p
== '*') // xyz* for namespace
4796 if (l
> NSEEL_MAX_VARIABLE_NAMELEN
) l
= NSEEL_MAX_VARIABLE_NAMELEN
;
4797 newstr
= newTmpBlock(ctx
,l
+1);
4800 memcpy(newstr
,tok1
,l
);
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;
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
4838 ctx
->rdbuf_end
= endptr
;
4839 if (!nseelparse(ctx
) && !ctx
->errVar
)
4841 start_opcode
= ctx
->result
;
4845 nseelrestart(NULL
,ctx
->scanner
);
4848 ctx
->rdbuf_end
= endptr
;
4850 if (!nseelparse(ctx
) && !ctx
->errVar
)
4852 start_opcode
= ctx
->result
;
4857 ctx
->errVar
+= expr
-_expression
;
4865 int rvMode
=0, fUse
=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
);
4873 // dump opcode trees for verification, before optimizing
4876 fprintf(g_eel_dump_fp
,"-- opcode chunk --\r\n");
4877 dumpOpcodeTree(ctx
,g_eel_dump_fp
,start_opcode
,2);
4881 if (!(ctx
->optimizeDisableFlags
&OPTFLAG_NO_OPTIMIZE
)) optimizeOpcodes(ctx
,start_opcode
,is_fname
[0] ? 1 : 0);
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
);
4887 // dump opcode trees for verification, after optimizing
4890 fprintf(g_eel_dump_fp2
,"-- POST-OPTIMIZED opcode chunk --\r\n");
4891 dumpOpcodeTree(ctx
,g_eel_dump_fp2
,start_opcode
,2);
4897 _codeHandleFunctionRec
*fr
= ctx
->isSharedFunctions
? newDataBlock(sizeof(_codeHandleFunctionRec
),8) :
4898 newTmpBlock(ctx
,sizeof(_codeHandleFunctionRec
));
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])
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
;
4936 fr
->next
= ctx
->functions_local
;
4937 ctx
->functions_local
= fr
;
4943 #ifdef DUMP_OPS_DURING_COMPILE
4946 g_debugfp
= fopen("C:/temp/foo.txt","w");
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
);
4958 if (!startptr_size
) continue; // optimized away
4959 if (startptr_size
>0)
4961 startptr
= newTmpBlock(ctx
,startptr_size
);
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
;
4974 #ifdef NSEEL_EEL1_COMPAT_MODE
4978 //if (!ctx->last_error_string[0])
4980 int byteoffs
= ctx
->errVar
;
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
);
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
;
5011 while (x
< 60 && p
[x
] && p
[x
] != '\r' && p
[x
] != '\n')
5013 if (!isspace_safe(p
[x
])) right_amt_nospace
=x
+1;
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>");
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
));
5037 p
->codesz
= startptr_size
;
5038 p
->tmptable_use
= computTableTop
;
5040 if (!startpts_tail
) startpts_tail
=startpts
=p
;
5043 startpts_tail
->_next
=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
;
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
));
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 :)
5089 // now we build one big code segment out of our list of them, inserting a mov esi, computable before each item as necessary
5094 wtpos
=MIN_COMPUTABLE_SIZE
;
5095 size
+= GLUE_RESET_WTP(NULL
,0);
5098 wtpos
-= p
->tmptable_use
;
5101 handle
->code
= newCodeBlock(size
,32);
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
;
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
;
5124 #if GLUE_FUNC_LEAVE_SIZE > 0
5125 memcpy(writeptr
,&GLUE_FUNC_LEAVE
,GLUE_FUNC_LEAVE_SIZE
);
5126 writeptr
+= GLUE_FUNC_LEAVE_SIZE
;
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
);
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);
5140 handle
->blocks_data
= ctx
->blocks_head_data
;
5141 ctx
->blocks_head_code
=0;
5142 ctx
->blocks_head_data
=0;
5146 // failed compiling, or failed calloc()
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);
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]++;
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
5195 codeHandleType
*h
= (codeHandleType
*)code
;
5196 if (!h
|| !h
->code
) return;
5198 codeptr
= (INT_PTR
) h
->code
;
5201 unsigned int *p
=(unsigned int *)codeptr
;
5202 while (*p
!= GLUE_RET
[0])
5204 printf("instr:%04X:%04X\n",*p
>>16,*p
&0xffff);
5210 #ifndef GLUE_TABPTR_IGNORED
5211 tabptr
=(INT_PTR
)h
->workTable
;
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);
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
;
5232 //------------------------------------------------------------------------------
5233 void NSEEL_code_free(NSEEL_CODEHANDLE code
)
5235 codeHandleType
*h
= (codeHandleType
*)code
;
5238 #ifdef EEL_VALIDATE_WORKTABLE_USE
5241 char *p
= ((char*)h
->workTable
) + h
->workTable_size
*sizeof(EEL_F
);
5243 for(x
=COMPUTABLE_EXTRA_SPACE
*sizeof(EEL_F
) - 1;x
>= 0; x
--)
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))));
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
;
5275 int nseellex_init(void ** ptr_yy_globals
);
5276 void nseelset_extra(void *user_defined
, void *yyscanner
);
5277 if (nseellex_init(&ctx
->scanner
))
5282 nseelset_extra(ctx
,ctx
->scanner
);
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
;
5298 int NSEEL_VM_setramsize(NSEEL_VMCTX _ctx
, int maxent
)
5300 compileContext
*ctx
= (compileContext
*)_ctx
;
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
;
5316 if (!ctx
|| !maxent
) return;
5320 maxent
= ctx
->ram_state
->maxblocks
;
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
)
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
)
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
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
5369 int nseellex_destroy(void *yyscanner
);
5370 nseellex_destroy(ctx
->scanner
);
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();
5388 nseel_globalVarItem
*op
= p
;
5398 int *NSEEL_code_getstats(NSEEL_CODEHANDLE code
)
5400 codeHandleType
*h
= (codeHandleType
*)code
;
5403 return h
->code_stats
;
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
))
5414 compileContext
*c
=(compileContext
*)ctx
;
5415 c
->onString
= onString
;
5416 c
->onNamedString
= onNamedString
;
5420 void NSEEL_VM_SetCustomFuncThis(NSEEL_VMCTX ctx
, void *thisptr
)
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
);
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
);
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
);
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
;
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
;
5472 varNameRec
*v
= rd
[0];
5473 if ((!refcnt_chk
|| !v
->refcnt
) && !v
->isreg
)
5479 if (wr
!= rd
) *wr
= *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
;
5504 int i
= EEL_GROWBUF_GET_SIZE(&ctx
->varNameList
);
5505 varNameRec
**rd
= EEL_GROWBUF_GET(&ctx
->varNameList
);
5515 #ifdef NSEEL_EEL1_COMPAT_MODE
5516 static EEL_F __nseel_global_regs
[100];
5517 double *NSEEL_getglobalregs() { return __nseel_global_regs
; }
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);
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
;
5540 if (!stricmp(p
->name
,gv
)) break;
5544 if (!p
&& addIfNotPresent
)
5546 size_t gvl
= strlen(gv
);
5547 p
= (nseel_globalVarItem
*)malloc(sizeof(nseel_globalVarItem
) + gvl
);
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
)
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);
5578 slot
= vartable_lowerbound(ctx
,name
, &match
);
5581 varNameRec
*v
= EEL_GROWBUF_GET(&ctx
->varNameList
)[slot
];
5585 if (isReg
) v
->isreg
=isReg
;
5586 if (namePtrOut
) *namePtrOut
= v
->str
;
5590 if (isReg
< 0) return NULL
;
5592 if (ctx
->varValueStore_left
<1)
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
);
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
--;
5612 memcpy(vh
->str
,name
,l
);
5614 if (namePtrOut
) *namePtrOut
= vh
->str
;
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
;
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
;
5638 ni
= EEL_GROWBUF_GET_SIZE(&tctx
->varNameList
);
5639 rd
= EEL_GROWBUF_GET(&tctx
->varNameList
);
5642 if (!func(rd
[0]->str
,rd
[0]->value
,userctx
)) break;
5648 //------------------------------------------------------------------------------
5649 EEL_F
*NSEEL_VM_regvar(NSEEL_VMCTX _ctx
, const char *var
)
5651 compileContext
*ctx
= (compileContext
*)_ctx
;
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);
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
;
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);
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
;
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
)
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
);
5700 o
->fntype
= FUNCTYPE_FUNCTIONTYPEREC
;
5702 o
->parms
.parms
[0]=code1
;
5703 o
->parms
.parms
[1]=code2
;
5704 o
->parms
.parms
[2]=code3
;
5709 if (chkamt
< 0 || stricmp(f
->name
,name
)) break;
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')
5724 return nseel_createCompiledValue(ctx
,(EEL_F
)strtoul(tmp
+2,&p
,16));
5726 else if (tmp
[0] == '$')
5730 char *p
=(char*)tmp
+2;
5731 unsigned int v
=(unsigned int) strtoul(tmp
+2,&p
,10);
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]);
5745 else if (tmp
[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;
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] == '#')
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
);
5776 if (ctx
->onNamedString
)
5778 if (tmplen
>0 && buf
[1]&&ctx
->function_curName
)
5781 opcodeRec
*r
= nseel_resolve_named_symbol(ctx
,nseel_createCompiledValuePtr(ctx
,NULL
,buf
),-1, &err
);
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
;
5792 if (err
) return NULL
;
5795 // if not namespaced symbol, return directly
5798 opcodeRec
*r
=newOpCode(ctx
,NULL
,OPCODETYPE_DIRECTVALUE_TEMPSTRING
);
5799 if (r
) r
->parms
.dv
.directValue
= -10000.0;
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
;
5813 ctx
->getVariable
= res
;
5814 ctx
->getVariable_userctx
= userctx
;
5819 #if defined(__ppc__) || defined(EEL_TARGET_PORTABLE)
5821 void eel_enterfp(int s
[2]) {}
5822 void eel_leavefp(int s
[2]) {}