diff --git a/sourcepawn/compiler/amx.h b/sourcepawn/compiler/amx.h index 3c473a77..7c6b3c7e 100644 --- a/sourcepawn/compiler/amx.h +++ b/sourcepawn/compiler/amx.h @@ -619,6 +619,7 @@ typedef enum { OP_SYSREQ_D, OP_SYSREQ_ND, /* ----- */ + OP_HEAP_I, OP_NUM_OPCODES } OPCODE; diff --git a/sourcepawn/compiler/sc.h b/sourcepawn/compiler/sc.h index 5c5e004e..05676716 100644 --- a/sourcepawn/compiler/sc.h +++ b/sourcepawn/compiler/sc.h @@ -602,6 +602,7 @@ SC_FUNC void defstorage(void); SC_FUNC void modstk(int delta); SC_FUNC void setstk(cell value); SC_FUNC void modheap(int delta); +SC_FUNC void modheap_i(); SC_FUNC void setheap_pri(void); SC_FUNC void setheap(cell value); SC_FUNC void cell2addr(void); @@ -610,6 +611,7 @@ SC_FUNC void addr2cell(void); SC_FUNC void char2addr(void); SC_FUNC void charalign(void); SC_FUNC void addconst(cell value); +SC_FUNC void setheap_save(cell value); /* Code generation functions for arithmetic operators. * diff --git a/sourcepawn/compiler/sc3.c b/sourcepawn/compiler/sc3.c index dc6785df..c09b0fa1 100644 --- a/sourcepawn/compiler/sc3.c +++ b/sourcepawn/compiler/sc3.c @@ -60,9 +60,104 @@ static int dbltest(void (*oper)(),value *lval1,value *lval2); static int commutative(void (*oper)()); static int constant(value *lval); +#define HEAPUSE_STATIC 0 +#define HEAPUSE_DYNAMIC 1 + +typedef struct heapuse_s { + int type; /* HEAPUSE_STATIC or HEAPUSE_DYNAMIC */ + int size; /* size of array for static (0 for dynamic) */ + struct heapuse_s *prev; /* previous array on the list */ +} heapuse_t; + +typedef struct heapuse_list_s { + struct heapuse_list_s *prev; /* last used list */ + heapuse_t *head; /* head of the current list */ +} heapuse_list_t; + static char lastsymbol[sNAMEMAX+1]; /* name of last function/variable */ static int bitwise_opercount; /* count of bitwise operators in an expression */ -static int decl_heap=0; +//static int decl_heap=0; +static heapuse_list_t *heapusage = NULL; + +/** + * Creates a new heap allocation tracker entry + */ +void pushheaplist() +{ + heapuse_list_t *newlist=(heapuse_list_t *)malloc(sizeof(heapuse_list_t)); + newlist->prev=heapusage; + newlist->head=NULL; + heapusage=newlist; +} + +/** + * Generates code to free all heap allocations on a tracker + */ +void freeheapusage(heapuse_list_t *heap) +{ + heapuse_t *cur=heap->head; + heapuse_t *tmp; + while (cur) { + if (cur->type == HEAPUSE_STATIC) { + modheap((-1)*cur->size*sizeof(cell)); + } else { + modheap_i(); + } + tmp=cur->prev; + free(cur); + cur=tmp; + } + heap->head=NULL; +} + +/** + * Pops a heap list but does not free it. + */ +heapuse_list_t *popsaveheaplist() +{ + heapuse_list_t *oldlist=heapusage; + heapusage=heapusage->prev; + return oldlist; +} + +/** + * Pops a heap list and frees it. + */ +void popheaplist() +{ + heapuse_list_t *oldlist; + assert(heapusage!=NULL); + + freeheapusage(heapusage); + assert(heapusage->head==NULL); + + oldlist=heapusage->prev; + free(heapusage); + heapusage=oldlist; +} + +/* + * Returns the size passed in + */ +int markheap(int type, int size) +{ + heapuse_t *use; + if (type==HEAPUSE_STATIC && size==0) + return 0; + use=heapusage->head; + if (use && (type==HEAPUSE_STATIC) + && (use->type == type)) + { + use->size += size; + } else { + use=(heapuse_t *)malloc(sizeof(heapuse_t)); + use->type=type; + use->size=size; + use->prev=heapusage->head; + heapusage->head=use; + } + return size; +} /* Function addresses of binary operators for signed operations */ static void (*op1[17])(void) = { @@ -684,15 +779,13 @@ static cell calc(cell left,void (*oper)(),cell right,char *boolresult) SC_FUNC int expression(cell *val,int *tag,symbol **symptr,int chkfuncresult) { - int locheap=decl_heap; value lval={0}; + pushheaplist(); if (hier14(&lval)) rvalue(&lval); /* scrap any arrays left on the heap */ - assert(decl_heap>=locheap); - modheap((locheap-decl_heap)*sizeof(cell)); /* remove heap space, so negative delta */ - decl_heap=locheap; + popheaplist(); if (lval.ident==iCONSTEXPR && val!=NULL) /* constant expression */ *val=lval.constval; @@ -1047,6 +1140,34 @@ static int hier14(value *lval1) return FALSE; /* expression result is never an lvalue */ } +/** + * Sums up array usage in the current heap tracer and convert it into a dynamic array. + * This is used for the ternary operator, which needs to convert its array usage into + * something dynamically managed. + * !Note: + * This might break if expressions can ever return dynamic arrays. + * Thus, we assert() if something is non-static here. + * Right now, this poses no problem because this type of expression is impossible: + * (a() ? return_array() : return_array()) ? return_array() : return_array() + */ + +void dynarray_from_heaplist(heapuse_list_t *heap) +{ + heapuse_t *use=heap->head; + heapuse_t *tmp; + long total=0; + while (use) { + assert(use->type==HEAPUSE_STATIC); + total+=use->size; + tmp=use->prev; + free(use); + use=tmp; + } + free(heap); + if (total) + setheap_save(total); +} + static int hier13(value *lval) { int lvalue=plnge1(hier12,lval); @@ -1055,7 +1176,9 @@ static int hier13(value *lval) int flab2=getlabel(); value lval2={0}; int array1,array2; - + heapuse_list_t *heap; + + pushheaplist(); if (lvalue) { rvalue(lval); } else if (lval->ident==iCONSTEXPR) { @@ -1072,6 +1195,9 @@ static int hier13(value *lval) sc_allowtags=(short)POPSTK_I(); /* restore */ jumplabel(flab2); setlabel(flab1); + heap=popsaveheaplist(); + dynarray_from_heaplist(heap); + pushheaplist(); needtoken(':'); if (hier13(&lval2)) rvalue(&lval2); @@ -1090,6 +1216,11 @@ static int hier13(value *lval) if (!matchtag(lval->tag,lval2.tag,FALSE)) error(213); /* tagname mismatch ('true' and 'false' expressions) */ setlabel(flab2); + heap=popsaveheaplist(); + dynarray_from_heaplist(heap); + if (array1 && array2) { + markheap(HEAPUSE_DYNAMIC, 0); + } if (lval->ident==iARRAY) lval->ident=iREFARRAY; /* iARRAY becomes iREFARRAY */ else if (lval->ident!=iREFARRAY) @@ -1871,6 +2002,7 @@ static void setdefarray(cell *string,cell size,cell array_sz,cell *dataaddr,int */ assert(array_sz>=size); modheap((int)array_sz*sizeof(cell)); + markheap(HEAPUSE_STATIC, array_sz); /* ??? should perhaps fill with zeros first */ memcopy(size*sizeof(cell)); moveto1(); @@ -1914,7 +2046,6 @@ static void callfunction(symbol *sym,value *lval_result,int matchparanthesis) { static long nest_stkusage=0L; static int nesting=0; - int locheap; int close,lvalue; int argpos; /* index in the output stream (argpos==nargs if positional parameters) */ int argidx=0; /* index in "arginfo" list */ @@ -1946,12 +2077,12 @@ static int nesting=0; assert(retsize>0); modheap(retsize*sizeof(cell));/* address is in ALT */ pushreg(sALT); /* pass ALT as the last (hidden) parameter */ - decl_heap+=retsize; + markheap(HEAPUSE_STATIC, retsize); /* also mark the ident of the result as "array" */ lval_result->ident=iREFARRAY; lval_result->sym=symret; } /* if */ - locheap=decl_heap; + pushheaplist(); nesting++; assert(nest_stkusage>=0); @@ -2054,14 +2185,14 @@ static int nesting=0; } else { rvalue(&lval); /* get value in PRI */ setheap_pri(); /* address of the value on the heap in PRI */ - heapalloc++; + heapalloc+=markheap(HEAPUSE_STATIC, 1); nest_stkusage++; } /* if */ } else if (lvalue) { address(lval.sym,sPRI); } else { setheap_pri(); /* address of the value on the heap in PRI */ - heapalloc++; + heapalloc+=markheap(HEAPUSE_STATIC, 1); nest_stkusage++; } /* if */ } else if (lval.ident==iCONSTEXPR || lval.ident==iEXPRESSION @@ -2073,7 +2204,7 @@ static int nesting=0; /* allocate a cell on the heap and store the * value (already in PRI) there */ setheap_pri(); /* address of the value on the heap in PRI */ - heapalloc++; + heapalloc+=markheap(HEAPUSE_STATIC, 1); nest_stkusage++; } /* if */ /* ??? handle const array passed by reference */ @@ -2111,7 +2242,7 @@ static int nesting=0; address(lval.sym,sPRI); } else { setheap_pri(); /* address of the value on the heap in PRI */ - heapalloc++; + heapalloc+=markheap(HEAPUSE_STATIC, 1); nest_stkusage++; } /* if */ } /* if */ @@ -2258,7 +2389,7 @@ static int nesting=0; } else if (arg[argidx].ident==iREFERENCE) { setheap(arg[argidx].defvalue.val); /* address of the value on the heap in PRI */ - heapalloc++; + heapalloc+=markheap(HEAPUSE_STATIC, 1); nest_stkusage++; } else { int dummytag=arg[argidx].tags[0]; @@ -2329,7 +2460,6 @@ static int nesting=0; markusage(sym,uREAD); /* do not mark as "used" when this call itself is skipped */ if ((sym->usage & uNATIVE)!=0 &&sym->x.lib!=NULL) sym->x.lib->value += 1; /* increment "usage count" of the library */ - modheap(-heapalloc*sizeof(cell)); if (symret!=NULL) popreg(sPRI); /* pop hidden parameter as function result */ sideeffect=TRUE; /* assume functions carry out a side-effect */ @@ -2339,7 +2469,7 @@ static int nesting=0; /* maintain max. amount of memory used */ { long totalsize; - totalsize=declared+decl_heap+1; /* local variables & return value size, + totalsize=declared+heapalloc+1; /* local variables & return value size, * +1 for PROC opcode */ if (lval_result->ident==iREFARRAY) totalsize++; /* add hidden parameter (on the stack) */ @@ -2361,9 +2491,7 @@ static int nesting=0; * this function has as a result (in other words, scrap all arrays on the * heap that caused by expressions in the function arguments) */ - assert(decl_heap>=locheap); - modheap((locheap-decl_heap)*sizeof(cell)); /* remove heap space, so negative delta */ - decl_heap=locheap; + popheaplist(); nesting--; } diff --git a/sourcepawn/compiler/sc4.c b/sourcepawn/compiler/sc4.c index e271037d..d0d11830 100644 --- a/sourcepawn/compiler/sc4.c +++ b/sourcepawn/compiler/sc4.c @@ -844,6 +844,26 @@ SC_FUNC void modheap(int delta) } /* if */ } +SC_FUNC void modheap_i() +{ + stgwrite("\theap.i\n"); + code_idx+=opcodes(1); +} + +SC_FUNC void setheap_save(cell value) +{ + pushreg(sPRI); + pushreg(sALT); + stgwrite("\tconst.pri "); + outval(value, TRUE); + code_idx+=opcodes(1)+opargs(1); + stgwrite("\theap "); + outval(sizeof(cell), TRUE); + stgwrite("\tstor.pri\n"); + popreg(sALT); + popreg(sPRI); +} + SC_FUNC void setheap_pri(void) { stgwrite("\theap "); /* ALT = HEA++ */ diff --git a/sourcepawn/compiler/sc6.c b/sourcepawn/compiler/sc6.c index 73845bf0..c8ad1082 100644 --- a/sourcepawn/compiler/sc6.c +++ b/sourcepawn/compiler/sc6.c @@ -493,6 +493,7 @@ static OPCODEC opcodelist[] = { { 99, "grtr", sIN_CSEG, parm0 }, {120, "halt", sIN_CSEG, parm1 }, { 45, "heap", sIN_CSEG, parm1 }, + {160, "heap.i", sIN_CSEG, parm0 }, { 27, "idxaddr", sIN_CSEG, parm0 }, { 28, "idxaddr.b", sIN_CSEG, parm1 }, {109, "inc", sIN_CSEG, parm1 },