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