From 9e2969390d3d90f56bce71296112e3bc85950b4b Mon Sep 17 00:00:00 2001 From: Spoike Date: Tue, 12 Mar 2013 22:32:25 +0000 Subject: [PATCH] ------------------------------------------------------------------------ r4174 | acceptthis | 2013-01-25 15:56:53 +0000 (Fri, 25 Jan 2013) | 8 lines fix fteqcc 'tui'. Added new warning to detect non-utf-8 strings. Doesn't check for overlong. Added new warning to detect uninitialised locals. Fix ///* inside #if. Renamed -Olm to -Olo, and fixed the bugs that stopped it from working. Uses the uninit locals check to prevent breakages. Optimised -Os. 'local const foo' definitions no longer sit within the local variables block. Fixed confusion between OP_BOUNDCHECK and jumps. ------------------------------------------------------------------------ git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4172 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/qclib/execloop.h | 6 + engine/qclib/qcc.h | 17 +- engine/qclib/qcc_cmdlib.c | 20 +- engine/qclib/qcc_pr_comp.c | 656 +++++++++++++++++++++---------------- engine/qclib/qcc_pr_lex.c | 50 ++- engine/qclib/qccgui.c | 6 +- engine/qclib/qccmain.c | 98 ++++-- engine/qclib/qcctui.c | 20 +- 8 files changed, 527 insertions(+), 346 deletions(-) diff --git a/engine/qclib/execloop.h b/engine/qclib/execloop.h index 45392a4b..4fccd240 100644 --- a/engine/qclib/execloop.h +++ b/engine/qclib/execloop.h @@ -541,7 +541,10 @@ reeval: case OP_LOAD_S: case OP_LOAD_FNC: if ((unsigned)OPA->edict >= (unsigned)maxedicts) + { + pr_xstatement = st-pr_statements; PR_RunError (&progfuncs->funcs, "OP_LOAD references invalid entity in %s", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name)); + } ed = PROG_TO_EDICT(progfuncs, OPA->edict); #ifdef PARANOID NUM_FOR_EDICT(ed); // make sure it's in range @@ -552,7 +555,10 @@ reeval: case OP_LOAD_V: if ((unsigned)OPA->edict >= (unsigned)maxedicts) + { + pr_xstatement = st-pr_statements; PR_RunError (&progfuncs->funcs, "OP_LOAD_V references invalid entity in %s", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name)); + } ed = PROG_TO_EDICT(progfuncs, OPA->edict); #ifdef PARANOID NUM_FOR_EDICT(ed); // make sure it's in range diff --git a/engine/qclib/qcc.h b/engine/qclib/qcc.h index 72ab9e65..1e40030a 100644 --- a/engine/qclib/qcc.h +++ b/engine/qclib/qcc.h @@ -45,6 +45,8 @@ extern float (*PRLittleFloat) (float l); #define MAX_NAME 256 // chars long extern unsigned int MAX_REGS; +extern unsigned int MAX_LOCALS; +extern unsigned int MAX_TEMPS; extern int MAX_STRINGS; extern int MAX_GLOBALS; @@ -509,7 +511,7 @@ extern pbool opt_return_only; extern pbool opt_compound_jumps; //extern pbool opt_comexprremoval; extern pbool opt_stripfunctions; -extern pbool opt_locals_marshalling; +extern pbool opt_locals_overlapping; extern pbool opt_logicops; extern pbool opt_vectorcalls; @@ -531,7 +533,7 @@ extern int optres_return_only; extern int optres_compound_jumps; //extern int optres_comexprremoval; extern int optres_stripfunctions; -extern int optres_locals_marshalling; +extern int optres_locals_overlapping; extern int optres_logicops; pbool CompileParams(progfuncs_t *progfuncs, int doall, int nump, char **parms); @@ -582,6 +584,7 @@ enum { WARN_TOOFEWPARAMS, WARN_TOOMANYPARAMS, WARN_UNEXPECTEDPUNCT, + WARN_UNINITIALIZED, WARN_ASSIGNMENTTOCONSTANT, WARN_ASSIGNMENTTOCONSTANTFUNC, WARN_MISSINGRETURNVALUE, @@ -596,6 +599,7 @@ enum { WARN_STRINGTOOLONG, WARN_BADTARGET, WARN_BADPRAGMA, + WARN_NOTUTF8, WARN_HANGINGSLASHR, WARN_NOTDEFINED, WARN_NOTCONSTANT, @@ -782,7 +786,11 @@ extern QCC_def_t *pr_scope; extern int pr_error_count, pr_warning_count; void QCC_PR_NewLine (pbool incomment); -QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, QCC_def_t *scope, pbool allocate, int arraysize, pbool saved); +#define GDF_NONE 0 +#define GDF_SAVED 1 +#define GDF_STATIC 2 +#define GDF_CONST 4 +QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, QCC_def_t *scope, pbool allocate, int arraysize, unsigned int flags); void QCC_PR_PrintDefs (void); @@ -822,6 +830,9 @@ void QCC_PR_EmitClassFromFunction(QCC_def_t *scope, char *tname); void PostCompile(void); pbool PreCompile(void); + +#define FIRST_LOCAL (MAX_REGS) +#define FIRST_TEMP (MAX_REGS+MAX_LOCALS) //============================================================================= extern char pr_immediate_string[8192]; diff --git a/engine/qclib/qcc_cmdlib.c b/engine/qclib/qcc_cmdlib.c index 807e01eb..b173b8e8 100644 --- a/engine/qclib/qcc_cmdlib.c +++ b/engine/qclib/qcc_cmdlib.c @@ -304,13 +304,12 @@ skipwhite: if (len >= sizeof(qcc_token)-1) ; else - qcc_token[len] = c; + qcc_token[len++] = c; data++; - len++; c = *data; if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':' || c=='\"' || c==',') break; - } while (!qcc_iswhite(c)); + } while (c && !qcc_iswhite(c)); qcc_token[len] = 0; return data; @@ -367,8 +366,7 @@ skipwhite: if (len >= sizeof(qcc_token)-1) ; else - qcc_token[len] = c; - len++; + qcc_token[len++] = c; } while (1); } @@ -383,9 +381,11 @@ skipwhite: data++; for(;;) { //parse regular number - qcc_token[len] = c; + if (len >= sizeof(qcc_token)-1) + ; + else + qcc_token[len++] = c; data++; - len++; c = *data; if ((c<'0'|| c>'9') && (c<'a'||c>'f') && (c<'A'||c>'F') && c != '.') break; @@ -399,9 +399,8 @@ skipwhite: if (len >= sizeof(qcc_token)-1) ; else - qcc_token[len] = c; + qcc_token[len++] = c; data++; - len++; c = *data; if ((c<'0'|| c>'9') && c != '.') break; @@ -419,9 +418,8 @@ skipwhite: if (len >= sizeof(qcc_token)-1) ; else - qcc_token[len] = c; + qcc_token[len++] = c; data++; - len++; c = *data; } while ((c>= 'a' && c <= 'z') || (c>= 'A' && c <= 'Z') || c == '_'); diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index 020f409e..4fe4d137 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -97,7 +97,7 @@ pbool opt_dupconstdefs; //float X = 5; and float Y = 5; occupy the same global pbool opt_return_only; //RETURN; DONE; at the end of a function strips out the done statement if there is no way to get to it. pbool opt_compound_jumps; //jumps to jump statements jump to the final point. pbool opt_stripfunctions; //if a functions is only ever called directly or by exe, don't emit the def. -pbool opt_locals_marshalling; //make the local vars of all functions occupy the same globals. +pbool opt_locals_overlapping; //make the local vars of all functions occupy the same globals. pbool opt_logicops; //don't make conditions enter functions if the return value will be discarded due to a previous value. (C style if statements) pbool opt_vectorcalls; //vectors can be packed into 3 floats, which can yield lower numpr_globals, but cost two more statements per call (only works for q1 calling conventions). pbool opt_simplifiedifs; //if (f != 0) -> if (f). if (f == 0) -> ifnot (f) @@ -122,7 +122,7 @@ int optres_return_only; int optres_compound_jumps; //int optres_comexprremoval; int optres_stripfunctions; -int optres_locals_marshalling; +int optres_locals_overlapping; int optres_logicops; int optres_test1; @@ -132,12 +132,13 @@ void *(*pHash_Get)(hashtable_t *table, const char *name); void *(*pHash_GetNext)(hashtable_t *table, const char *name, void *old); void *(*pHash_Add)(hashtable_t *table, const char *name, void *data, bucket_t *); -QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_def_t *scope, int arraysize, unsigned int ofs, int referable, pbool saved); +QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_def_t *scope, int arraysize, unsigned int ofs, int referable, unsigned int flags); QCC_type_t *QCC_PR_FindType (QCC_type_t *type); QCC_type_t *QCC_PR_PointerType (QCC_type_t *pointsto); QCC_type_t *QCC_PR_FieldType (QCC_type_t *pointsto); QCC_def_t *QCC_PR_Term (int exprflags); QCC_def_t *QCC_PR_GenerateFunctionCall (QCC_def_t *func, QCC_def_t *arglist[], int argcount); +void QCC_Marshal_Locals(int firststatement, int laststatement); void QCC_PR_ParseState (void); pbool simplestore; @@ -165,6 +166,8 @@ void QCC_PR_ParseDefs (char *classname); pbool qcc_usefulstatement; +pbool debug_armour_defined; + int max_breaks; int max_continues; int max_cases; @@ -336,14 +339,14 @@ QCC_opcode_t pr_opcodes[] = {7, "", "RANDV1", -1, ASSOC_LEFT, &type_vector, &type_void, &type_vector}, {7, "", "RANDV2", -1, ASSOC_LEFT, &type_vector, &type_vector, &type_vector}, - {7, "", "SWITCH_F", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, - {7, "", "SWITCH_V", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, - {7, "", "SWITCH_S", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, - {7, "", "SWITCH_E", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, - {7, "", "SWITCH_FNC", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, + {7, "", "SWITCH_F", -1, ASSOC_RIGHT, &type_void, NULL, &type_void}, + {7, "", "SWITCH_V", -1, ASSOC_RIGHT, &type_void, NULL, &type_void}, + {7, "", "SWITCH_S", -1, ASSOC_RIGHT, &type_void, NULL, &type_void}, + {7, "", "SWITCH_E", -1, ASSOC_RIGHT, &type_void, NULL, &type_void}, + {7, "", "SWITCH_FNC", -1, ASSOC_RIGHT, &type_void, NULL, &type_void}, - {7, "", "CASE", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, - {7, "", "CASERANGE", -1, ASSOC_LEFT, &type_void, &type_void, NULL}, + {7, "", "CASE", -1, ASSOC_RIGHT, &type_void, NULL, &type_void}, + {7, "", "CASERANGE", -1, ASSOC_RIGHT, &type_void, &type_void, NULL}, //Later are additions by DMW. @@ -1196,53 +1199,30 @@ QCC_def_t *QCC_MakeFloatConst(float value); QCC_def_t *QCC_MakeIntConst(int value); QCC_def_t *QCC_MakeVectorConst(float a, float b, float c); -typedef struct freeoffset_s { - struct freeoffset_s *next; - gofs_t ofs; - unsigned int size; -} freeoffset_t; - -freeoffset_t *freeofs; - +int tempsused; //assistant functions. This can safly be bipassed with the old method for more complex things. -gofs_t QCC_GetFreeOffsetSpace(unsigned int size) +gofs_t QCC_GetFreeLocalOffsetSpace(unsigned int size) +{ + gofs_t ofs = locals_end; + locals_end += size; + return ofs; +} +gofs_t QCC_GetFreeTempOffsetSpace(unsigned int size) +{ + gofs_t ofs = FIRST_TEMP + tempsused; + tempsused += size; + return ofs; +} +gofs_t QCC_GetFreeGlobalOffsetSpace(unsigned int size) { int ofs; - if (opt_locals_marshalling) - { - freeoffset_t *fofs, *prev; - for (fofs = freeofs, prev = NULL; fofs; fofs=fofs->next) - { - if (fofs->size == size) - { - if (prev) - prev->next = fofs->next; - else - freeofs = fofs->next; - - return fofs->ofs; - } - prev = fofs; - } - for (fofs = freeofs, prev = NULL; fofs; fofs=fofs->next) - { - if (fofs->size > size) - { - fofs->size -= size; - fofs->ofs += size; - - return fofs->ofs-size; - } - prev = fofs; - } - } ofs = numpr_globals; numpr_globals+=size; if (numpr_globals >= MAX_REGS) { - if (!opt_overlaptemps || !opt_locals_marshalling) + if (!opt_overlaptemps || !opt_locals_overlapping) QCC_Error(ERR_TOOMANYGLOBALS, "numpr_globals exceeded MAX_REGS - you'll need to use more optimisations"); else QCC_Error(ERR_TOOMANYGLOBALS, "numpr_globals exceeded MAX_REGS"); @@ -1251,40 +1231,6 @@ gofs_t QCC_GetFreeOffsetSpace(unsigned int size) return ofs; } -void QCC_FreeOffset(gofs_t ofs, unsigned int size) -{ - freeoffset_t *fofs; - if (ofs+size == numpr_globals) - { //fixme: is this a bug? - numpr_globals -= size; - return; - } - - for (fofs = freeofs; fofs; fofs=fofs->next) - { - //fixme: if this means the last block becomes free, free them all. - if (fofs->ofs == ofs + size) - { - fofs->ofs -= size; - fofs->size += size; - return; - } - if (fofs->ofs+fofs->size == ofs) - { - fofs->size += size; - return; - } - } - - fofs = qccHunkAlloc(sizeof(freeoffset_t)); - fofs->next = freeofs; - fofs->ofs = ofs; - fofs->size = size; - - freeofs = fofs; - return; -} - static QCC_def_t *QCC_GetTemp(QCC_type_t *type) { //#define CRAZYTEMPOPTS //not worth it. saves 2 temps with hexen2 (without even touching numpr_globals) @@ -1316,7 +1262,7 @@ static QCC_def_t *QCC_GetTemp(QCC_type_t *type) t = best; #endif if (t && t->scope && t->scope != pr_scope) - QCC_Error(ERR_INTERNAL, "Internal error temp has scope not equal to current scope"); + QCC_Error(ERR_INTERNAL, "Internal error: temp has scope not equal to current scope"); if (!t) { @@ -1326,7 +1272,7 @@ static QCC_def_t *QCC_GetTemp(QCC_type_t *type) t->next = functemps; functemps = t; - t->ofs = QCC_GetFreeOffsetSpace(t->size); + t->ofs = QCC_GetFreeTempOffsetSpace(t->size); numtemps+=t->size; } @@ -1337,27 +1283,10 @@ static QCC_def_t *QCC_GetTemp(QCC_type_t *type) var_c->temp = t; t->lastfunc = pr_scope; } - else if (opt_locals_marshalling) - { - //allocate a new one - t = qccHunkAlloc(sizeof(temp_t)); - t->size = type->size; - - t->next = functemps; - functemps = t; - - t->ofs = QCC_GetFreeOffsetSpace(t->size); - - numtemps+=t->size; - - var_c->ofs = t->ofs; - var_c->temp = t; - t->lastfunc = pr_scope; - } else { // we're not going to reallocate any temps so allocate permanently - var_c->ofs = QCC_GetFreeOffsetSpace(type->size); + var_c->ofs = QCC_GetFreeTempOffsetSpace(type->size); numtemps+=type->size; } @@ -1460,12 +1389,11 @@ static void QCC_RemapLockedTemp(temp_t *t, int firststatement, int laststatement { if (!newofs) { - newofs = QCC_GetFreeOffsetSpace(t->size); + newofs = QCC_GetFreeLocalOffsetSpace(t->size); numtemps+=t->size; - def = QCC_PR_DummyDef(type_float, NULL, pr_scope, t->size, newofs, false, false); + def = QCC_PR_DummyDef(type_float, NULL, pr_scope, t->size, newofs, false, 0); def->nextlocal = pr.localvars; - def->constant = false; #ifdef WRITEASM sprintf(buffer, "locked_%i", t->ofs); def->name = qccHunkAlloc(strlen(buffer)+1); @@ -1479,12 +1407,11 @@ static void QCC_RemapLockedTemp(temp_t *t, int firststatement, int laststatement { if (!newofs) { - newofs = QCC_GetFreeOffsetSpace(t->size); + newofs = QCC_GetFreeLocalOffsetSpace(t->size); numtemps+=t->size; - def = QCC_PR_DummyDef(type_float, NULL, pr_scope, t->size, newofs, false, false); + def = QCC_PR_DummyDef(type_float, NULL, pr_scope, t->size, newofs, false, 0); def->nextlocal = pr.localvars; - def->constant = false; #ifdef WRITEASM sprintf(buffer, "locked_%i", t->ofs); def->name = qccHunkAlloc(strlen(buffer)+1); @@ -1498,12 +1425,11 @@ static void QCC_RemapLockedTemp(temp_t *t, int firststatement, int laststatement { if (!newofs) { - newofs = QCC_GetFreeOffsetSpace(t->size); + newofs = QCC_GetFreeLocalOffsetSpace(t->size); numtemps+=t->size; - def = QCC_PR_DummyDef(type_float, NULL, pr_scope, t->size, newofs, false, false); + def = QCC_PR_DummyDef(type_float, NULL, pr_scope, t->size, newofs, false, 0); def->nextlocal = pr.localvars; - def->constant = false; #ifdef WRITEASM sprintf(buffer, "locked_%i", t->ofs); def->name = qccHunkAlloc(strlen(buffer)+1); @@ -1523,7 +1449,7 @@ static void QCC_RemapLockedTemps(int firststatement, int laststatement) t = functemps; while(t) { - if (t->scope || opt_locals_marshalling) + if (t->scope) { QCC_RemapLockedTemp(t, firststatement, laststatement); t->scope = NULL; @@ -2859,7 +2785,7 @@ QCC_def_t *QCC_PR_ParseImmediate (void) cn->scope = NULL; // always share immediates // copy the immediate to the global area - cn->ofs = QCC_GetFreeOffsetSpace(type_size[pr_immediate_type->type]); + cn->ofs = QCC_GetFreeGlobalOffsetSpace(type_size[pr_immediate_type->type]); if (pr_immediate_type == type_string) pr_immediate.string = QCC_CopyString (pr_immediate_string); @@ -3207,7 +3133,10 @@ QCC_def_t *QCC_PR_GenerateFunctionCall (QCC_def_t *func, QCC_def_t *arglist[], i //restore the class owner if (oself) + { QCC_PR_SimpleStatement(OP_STORE_ENT, oself->ofs, d->ofs, 0, false); + QCC_FreeTemp(oself); + } if (oldret) { @@ -3813,7 +3742,7 @@ QCC_def_t *QCC_PR_ParseFunctionCall (QCC_def_t *func) //warning, the func could //with vectorcalls, we store the vector into the args as individual floats //this allows better reuse of vector constants. //copy it into the offset now, because we can. - if (opt_vectorcalls && pr_token_type == tt_immediate && pr_immediate_type == type_vector && arg < MAX_PARMS && !def_parms[arg].temp->used) + if (opt_vectorcalls && pr_token_type == tt_immediate && pr_immediate_type == type_vector && arg < MAX_PARMS && (!def_parms[arg].temp || !def_parms[arg].temp->used)) { e = &def_parms[arg]; @@ -3853,7 +3782,7 @@ QCC_def_t *QCC_PR_ParseFunctionCall (QCC_def_t *func) //warning, the func could { d = (QCC_def_t *) qccHunkAlloc (sizeof(QCC_def_t)); d->name = "extra parm"; - d->ofs = QCC_GetFreeOffsetSpace (3); + d->ofs = QCC_GetFreeGlobalOffsetSpace (3); extra_parms[arg - MAX_PARMS] = d; } d = extra_parms[arg - MAX_PARMS]; @@ -3900,20 +3829,35 @@ QCC_def_t *QCC_PR_ParseFunctionCall (QCC_def_t *func) //warning, the func could e = QCC_PR_Statement(pr_opcodes+OP_CONV_FTOI, e, NULL, NULL); else if (p->type == ev_float && e->type->type == ev_integer) //convert float -> int... is this a constant? e = QCC_PR_Statement(pr_opcodes+OP_CONV_ITOF, e, NULL, NULL); - else if ((p->type == ev_function || p->type == ev_string || p->type == ev_pointer) && e->type->type == ev_integer && e->constant && !((int*)qcc_pr_globals)[e->ofs]) - { //you're allowed to use int 0 to pass a null function/string/pointer + else if ((p->type == ev_function || p->type == ev_field || p->type == ev_string || p->type == ev_pointer) && e->type->type == ev_integer && e->constant && !((int*)qcc_pr_globals)[e->ofs]) + { //you're allowed to use int 0 to pass a null function/field/string/pointer //this is basically because __NULL__ is defined as 0i (int 0) //note that we don't allow passing 0.0f for null. + //WARNING: field 0 is actually a valid field, and is commonly modelindex. + } + else if ((p->type == ev_vector) && e->type->type == ev_integer && e->constant && !((int*)qcc_pr_globals)[e->ofs]) + { + //also allow it for vector types too, but make sure the entire vector is valid. + e = QCC_MakeVectorConst(0, 0, 0); } else if (p->type != ev_variant && e->type->type != ev_variant) //can cast to variant whatever happens { - if (flag_laxcasts || (p->type == ev_function && e->type->type == ev_function)) + QCC_type_t *inh; + for (inh = e->type->parentclass; inh; inh = inh->parentclass) { - QCC_PR_ParseWarning(WARN_LAXCAST, "type mismatch on parm %i - (%s should be %s)", arg+1, TypeName(e->type), TypeName(p)); - QCC_PR_ParsePrintDef(WARN_LAXCAST, func); + if (typecmp(inh, p)) + break; + } + if (!inh) + { + if (flag_laxcasts || (p->type == ev_function && e->type->type == ev_function)) + { + QCC_PR_ParseWarning(WARN_LAXCAST, "type mismatch on parm %i - (%s should be %s)", arg+1, TypeName(e->type), TypeName(p)); + QCC_PR_ParsePrintDef(WARN_LAXCAST, func); + } + else + QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHPARM, func, "type mismatch on parm %i - (%s should be %s)", arg+1, TypeName(e->type), TypeName(p)); } - else - QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHPARM, func, "type mismatch on parm %i - (%s should be %s)", arg+1, TypeName(e->type), TypeName(p)); } } @@ -3996,7 +3940,7 @@ QCC_def_t *QCC_MakeIntConst(int value) else { // copy the immediate to the global area - cn->ofs = QCC_GetFreeOffsetSpace (type_size[type_integer->type]); + cn->ofs = QCC_GetFreeGlobalOffsetSpace (type_size[type_integer->type]); G_INT(cn->ofs) = value; } @@ -4044,7 +3988,7 @@ QCC_def_t *QCC_MakeVectorConst(float a, float b, float c) cn->arraysize = 0; // copy the immediate to the global area - cn->ofs = QCC_GetFreeOffsetSpace (type_size[type_vector->type]); + cn->ofs = QCC_GetFreeGlobalOffsetSpace (type_size[type_vector->type]); G_FLOAT(cn->ofs+0) = a; G_FLOAT(cn->ofs+1) = b; @@ -4085,7 +4029,7 @@ QCC_def_t *QCC_MakeFloatConst(float value) cn->arraysize = 0; // copy the immediate to the global area - cn->ofs = QCC_GetFreeOffsetSpace (type_size[type_integer->type]); + cn->ofs = QCC_GetFreeGlobalOffsetSpace (type_size[type_integer->type]); Hash_AddKey(&floatconstdefstable, fi.i, cn, qccHunkAlloc(sizeof(bucket_t))); @@ -4131,7 +4075,7 @@ static QCC_def_t *QCC_MakeStringConstInternal(char *value, pbool translate) cn->arraysize = 0; // copy the immediate to the global area - cn->ofs = QCC_GetFreeOffsetSpace (type_size[type_integer->type]); + cn->ofs = QCC_GetFreeGlobalOffsetSpace (type_size[type_integer->type]); string = QCC_CopyString (value); @@ -4244,8 +4188,8 @@ void QCC_PR_EmitFieldsForMembers(QCC_type_t *clas) for (a = 0; a < (m->arraysize?m->arraysize:1); a++) { - /*if it was already set, don't go recursive and generate 500 fields for a one-member class that was intheritted from 500 times*/ - if (((int *)qcc_pr_globals)[o+a*mt->size+m->ofs]) + /*if it was already set, don't go recursive and generate 500 fields for a one-member class that was inheritted from 500 times*/ + if (((int *)qcc_pr_globals)[0+a*mt->size+m->ofs]) continue; //we need the type in here so saved games can still work without saving ints as floats. (would be evil) @@ -4349,7 +4293,7 @@ void QCC_PR_EmitClassFromFunction(QCC_def_t *scope, char *tname) df->first_statement = numstatements; df->parm_size[0] = 1; df->numparms = 0; - df->parm_start = numpr_globals; + locals_start = locals_end = FIRST_LOCAL; G_FUNCTION(scope->ofs) = df - functions; @@ -4384,11 +4328,10 @@ void QCC_PR_EmitClassFromFunction(QCC_def_t *scope, char *tname) QCC_WriteAsmFunction(scope, df->first_statement, df->parm_start); - pr.localvars = NULL; - - locals_end = numpr_globals + basetype->size; - df->locals = locals_end - df->parm_start; + QCC_Marshal_Locals(df->first_statement, numstatements); + df->parm_start = locals_start; + df->numparms = locals_end - locals_start; } QCC_def_t *QCC_PR_ParseArrayPointer (QCC_def_t *d, pbool allowarrayassign) @@ -4662,6 +4605,12 @@ QCC_def_t *QCC_PR_ParseArrayPointer (QCC_def_t *d, pbool allowarrayassign) /*hexen2 format has opcodes to read arrays (but has no way to write)*/ switch(t->type) { + case ev_field: + case ev_pointer: + case ev_integer: + d = QCC_PR_Statement(&pr_opcodes[OP_FETCH_GBL_F], d, QCC_SupplyConversion(idx, ev_float, true), &st); //get pointer to precise def. + d->type = t; + break; case ev_float: d = QCC_PR_Statement(&pr_opcodes[OP_FETCH_GBL_F], d, QCC_SupplyConversion(idx, ev_float, true), &st); //get pointer to precise def. // st->a = d->ofs; @@ -4896,14 +4845,14 @@ QCC_def_t *QCC_PR_ParseValue (QCC_type_t *assumeclass, pbool allowarrayassign) if (!pr_classtype) QCC_PR_ParseError(ERR_NOTANAME, "Cannot use 'this' outside of an OO function\n"); od = QCC_PR_GetDef(NULL, "self", NULL, true, 0, false); - d = QCC_PR_DummyDef(pr_classtype, "this", pr_scope, 0, od->ofs, true, false); + d = QCC_PR_DummyDef(pr_classtype, "this", pr_scope, 0, od->ofs, true, GDF_CONST); } else if (keyword_class && !strcmp(name, "super")) { if (!pr_classtype) QCC_PR_ParseError(ERR_NOTANAME, "Cannot use 'super' outside of an OO function\n"); od = QCC_PR_GetDef(NULL, "self", NULL, true, 0, false); - d = QCC_PR_DummyDef(pr_classtype, "super", pr_scope, 0, od->ofs, true, false); + d = QCC_PR_DummyDef(pr_classtype, "super", pr_scope, 0, od->ofs, true, GDF_CONST); } else { @@ -5391,10 +5340,10 @@ int QCC_canConv(QCC_def_t *from, etype_t to) if (QCC_ShouldConvert(from, to)>=0) return 1; */ - if (from->type->type == ev_integer && to == ev_function) - return 1; +// if (from->type->type == ev_integer && to == ev_function) +// return 1; - if (from->constant && from->arraysize == 0 && (from->type->type == ev_integer || from->type->type == ev_float) && !G_INT(from->ofs)) + if (from->constant && from->arraysize == 0 && from->type->size == ((to==ev_vector)?3:1) && (from->type->type == ev_integer || from->type->type == ev_float) && !G_INT(from->ofs)) return 2; return -100; @@ -6207,7 +6156,6 @@ void QCC_PR_ParseStatement (void) // QCC_PR_ParseWarning("local vars after temp vars\n"); QCC_PR_ParseDefs (NULL); pr_classtype = functionsclasstype; - locals_end = numpr_globals; return; } @@ -6222,10 +6170,7 @@ void QCC_PR_ParseStatement (void) (keyword_class && !STRCMP ("class", pr_token)) || (keyword_const && !STRCMP ("const", pr_token))) { -// if (locals_end != numpr_globals) //is this breaking because of locals? -// QCC_PR_ParseWarning("local vars after temp vars\n"); QCC_PR_ParseDefs (NULL); - locals_end = numpr_globals; return; } @@ -6907,7 +6852,6 @@ void QCC_PR_ParseAsm(void) if (QCC_PR_CheckKeyword(keyword_local, "local")) { QCC_PR_ParseDefs (NULL); - locals_end = numpr_globals; return; } @@ -7258,20 +7202,23 @@ void QCC_CheckForDeadAndMissingReturns(int first, int last, int rettype) //make sure something goes to just after this return. for (st2 = first; st2 < last; st2++) { - if (pr_opcodes[statements[st2].op].type_a == NULL) + if (pr_opcodes[statements[st2].op].associative == ASSOC_RIGHT) { - if (st2 + (signed)statements[st2].a == st) - break; - } - if (pr_opcodes[statements[st2].op].type_b == NULL) - { - if (st2 + (signed)statements[st2].b == st) - break; - } - if (pr_opcodes[statements[st2].op].type_c == NULL) - { - if (st2 + (signed)statements[st2].c == st) - break; + if (pr_opcodes[statements[st2].op].type_a == NULL) + { + if (st2 + (signed)statements[st2].a == st) + break; + } + if (pr_opcodes[statements[st2].op].type_b == NULL) + { + if (st2 + (signed)statements[st2].b == st) + break; + } + if (pr_opcodes[statements[st2].op].type_c == NULL) + { + if (st2 + (signed)statements[st2].c == st) + break; + } } } if (st2 == last) @@ -7280,7 +7227,7 @@ void QCC_CheckForDeadAndMissingReturns(int first, int last, int rettype) } continue; } - if (rettype != ev_void) + if (rettype != ev_void && pr_opcodes[statements[st].op].associative == ASSOC_RIGHT) { if (pr_opcodes[statements[st].op].type_a == NULL) { @@ -7429,6 +7376,144 @@ void QCC_CommonSubExpressionRemoval(int first, int last) } */ +#define OpAssignsToA(op) false + +//follow branches (by recursing). +//stop on first read(error, return statement) or write(no error, return -1) +//end-of-block returns 0, done/return/goto returns -2 +int QCC_CheckOneUninitialised(int firststatement, int laststatement, int min, int max) +{ + int ret; + int i; + QCC_dstatement32_t *st; + + for (i = firststatement; i < laststatement; i++) + { + st = &statements[i]; + + if (st->op == OP_DONE || st->op == OP_RETURN) + { + if (st->a >= min && st->a < max) + return i; + return -2; + } + +// this code catches gotos, but can cause issues with while statements. +// if (st->op == OP_GOTO && (int)st->a < 1) +// return -2; + + if (pr_opcodes[st->op].type_a) + { + if (st->a >= min && st->a < max) + { + if (OpAssignsToA(st->op)) + return -1; + + return i; + } + } + else if (pr_opcodes[st->op].associative == ASSOC_RIGHT && (int)st->a > 0) + { + int jump = i + (int)st->a; + ret = QCC_CheckOneUninitialised(i + 1, jump, min, max); + if (ret > 0) + return ret; + i = jump-1; + } + + if (pr_opcodes[st->op].type_b) + { + if (st->b >= min && st->b < max) + { + if (OpAssignsToB(st->op)) + return -1; + + return i; + } + } + else if (pr_opcodes[st->op].associative == ASSOC_RIGHT && (int)st->b > 0) + { + int jump = i + (int)st->b; + //check if there's an else. + st = &statements[jump-1]; + if (st->op == OP_GOTO && (int)st->a > 0) + { + int jump2 = jump-1 + st->a; + int rett = QCC_CheckOneUninitialised(i + 1, jump - 1, min, max); + if (rett > 0) + return rett; + ret = QCC_CheckOneUninitialised(jump, jump2, min, max); + if (ret > 0) + return ret; + if (rett < 0 && ret < 0) + return (rett == ret)?ret:-1; //inited or aborted in both, don't need to continue along this branch + i = jump2-1; + } + else + { + ret = QCC_CheckOneUninitialised(i + 1, jump, min, max); + if (ret > 0) + return ret; + i = jump-1; + } + continue; + } + + if (pr_opcodes[st->op].type_c && st->c >= min && st->c < max) + { + if (OpAssignsToC(st->op)) + return -1; + + return i; + } + else if (pr_opcodes[st->op].associative == ASSOC_RIGHT && (int)st->c > 0) + { + int jump = i + (int)st->c; + ret = QCC_CheckOneUninitialised(i + 1, jump, min, max); + if (ret > 0) + return ret; + i = jump-1; + + continue; + } + } + + return 0; +} + +pbool QCC_CheckUninitialised(int firststatement, int laststatement) +{ + QCC_def_t *local; + int i; + int min,max; + pbool result = false; + int paramend = FIRST_LOCAL; + QCC_type_t *type = pr_scope->type; + int err; + + for (type = pr_scope->type, i = type->num_parms, type = type->param; i > 0; i--, type = type->next) + paramend += type->size; + + for (local = pr.localvars; local; local = local->nextlocal) + { + if (local->constant) + continue; //will get some other warning, so we don't care. + if (local->ofs < paramend) + continue; + min = local->ofs; + max = local->ofs + (local->type->size * (local->arraysize?local->arraysize:1)); + err = QCC_CheckOneUninitialised(firststatement, laststatement, min, max); + if (err > 0) + { + QCC_PR_Warning(WARN_UNINITIALIZED, strings+s_file, statement_linenums[err], "Potentially uninitialised variable %s", local->name); + result = true; +// break; + } + } + + return result; +} + void QCC_RemapOffsets(unsigned int firststatement, unsigned int laststatement, unsigned int min, unsigned int max, unsigned int newmin) { QCC_dstatement_t *st; @@ -7445,19 +7530,11 @@ void QCC_RemapOffsets(unsigned int firststatement, unsigned int laststatement, u } } -void QCC_Marshal_Locals(int first, int laststatement) +void QCC_Marshal_Locals(int firststatement, int laststatement) { QCC_def_t *local; - unsigned int newofs; - int size; - -// if (!opt_overlaptemps) //clear these after each function. we arn't overlapping them so why do we need to keep track of them? -// { -// temp_t *t; -// for (t = functemps; t; t = t->next) -// QCC_FreeOffset(t->ofs, t->size); -// functemps = NULL; -// } + pbool error = false; + int localsused = locals_end - locals_start; if (!pr.localvars) //nothing to marshal { @@ -7466,59 +7543,65 @@ void QCC_Marshal_Locals(int first, int laststatement) return; } - if (!opt_locals_marshalling) + if (!opt_locals_overlapping) { - pr.localvars = NULL; - return; + if (qccwarningaction[WARN_UNINITIALIZED]) + QCC_CheckUninitialised(firststatement, laststatement); + error = true; //always use the legacy behaviour + } + else if (QCC_CheckUninitialised(firststatement, laststatement)) + { + error = true; +// QCC_PR_Note(ERR_INTERNAL, strings+s_file, pr_source_line, "Not overlapping locals from %s due to uninitialised locals", pr_scope->name); + } + else + { + //make sure we're allowed to marshall this function's locals + for (local = pr.localvars; local; local = local->nextlocal) + { + //FIXME: check for uninitialised locals. + //these matter when the function goes recursive (and locals marshalling counts as recursive every time). + if (((int*)qcc_pr_globals)[local->ofs]) + { + QCC_PR_Note(ERR_INTERNAL, strings+local->s_file, local->s_line, "Marshaling non-const initialised %s", local->name); + error = true; + } + + if (local->constant) + { + QCC_PR_Note(ERR_INTERNAL, strings+local->s_file, local->s_line, "Marshaling const %s", local->name); + error = true; + } + } } - //initial backwards bounds. - locals_start = MAX_REGS; - locals_end = 0; - - newofs = MAX_REGS; //this is a handy place to put it. :) - - //the params need to be in the order that they were allocated - //so we allocate in a backwards order. - for (local = pr.localvars; local; local = local->nextlocal) + if (error) { - if (local->constant) - continue; + //move all the locals into a vanilla-style single block, per function. + locals_start = QCC_GetFreeGlobalOffsetSpace(localsused); + locals_end = locals_start + localsused; - size = local->type->size*(local->arraysize?local->arraysize:1); - if (local->arraysize) - size++; + QCC_RemapOffsets(firststatement, laststatement, FIRST_LOCAL, FIRST_LOCAL+localsused, locals_start); + memcpy(qcc_pr_globals+locals_start, qcc_pr_globals+FIRST_LOCAL, localsused*sizeof(float)); + memset(qcc_pr_globals+FIRST_LOCAL, 0, localsused*sizeof(float)); + for (local = pr.localvars; local; local = local->nextlocal) + { + if (local->ofs >= FIRST_LOCAL) + local->ofs = local->ofs - FIRST_LOCAL + locals_start; + } + for (local = pr.def_tail; local; local = local->next) + { + if (local->scope != pr_scope) + break; - newofs += size; + if (local->ofs >= FIRST_LOCAL) + local->ofs = local->ofs - FIRST_LOCAL + locals_start; + } } - - locals_start = MAX_REGS; - locals_end = newofs; - - - optres_locals_marshalling+=newofs-MAX_REGS; - - for (local = pr.localvars; local; local = local->nextlocal) + else { - if (local->constant) - continue; - - if (((int*)qcc_pr_globals)[local->ofs]) - QCC_PR_ParseError(ERR_INTERNAL, "Marshall of a set value"); - - size = local->type->size*(local->arraysize?local->arraysize:1); - if (local->arraysize) - size++; - - newofs -= size; - - QCC_RemapOffsets(first, laststatement, local->ofs, local->ofs+size, newofs); - QCC_FreeOffset(local->ofs, size); - - local->ofs = newofs; +// QCC_PR_Note(ERR_INTERNAL, strings+s_file, pr_source_line, "Overlapping %s", pr_scope->name); } - - pr.localvars = NULL; } @@ -7629,13 +7712,11 @@ QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_type_t *type) QCC_type_t *parm; pbool needsdone=false; - freeoffset_t *oldfofs; conditional = 0; expandedemptymacro = false; - f = (void *)qccHunkAlloc (sizeof(QCC_function_t)); // @@ -7681,10 +7762,7 @@ QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_type_t *type) // define the parms // - locals_start = locals_end = numpr_globals; - - oldfofs = freeofs; - freeofs = NULL; + locals_start = locals_end = FIRST_LOCAL; parm = type->param; for (i=0 ; inum_parms ; i++) @@ -7705,11 +7783,6 @@ QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_type_t *type) parm = parm->next; } - if (type->num_parms) - locals_start = locals_end = defs[0]->ofs; - - freeofs = oldfofs; - f->code = numstatements; if (type->num_parms > MAX_PARMS) @@ -7720,7 +7793,7 @@ QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_type_t *type) { e2 = (QCC_def_t *) qccHunkAlloc (sizeof(QCC_def_t)); e2->name = "extra parm"; - e2->ofs = QCC_GetFreeOffsetSpace(3); + e2->ofs = QCC_GetFreeGlobalOffsetSpace(3); extra_parms[i - MAX_PARMS] = e2; } extra_parms[i - MAX_PARMS]->type = defs[i]->type; @@ -7836,19 +7909,18 @@ QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_type_t *type) QCC_CheckForDeadAndMissingReturns(f->code, numstatements, type->aux_type->type); - if (opt_compound_jumps) - QCC_CompoundJumps(f->code, numstatements); // if (opt_comexprremoval) // QCC_CommonSubExpressionRemoval(f->code, numstatements); QCC_RemapLockedTemps(f->code, numstatements); - locals_end = numpr_globals; - QCC_WriteAsmFunction(pr_scope, f->code, locals_start); QCC_Marshal_Locals(f->code, numstatements); + if (opt_compound_jumps) + QCC_CompoundJumps(f->code, numstatements); + if (num_labels) num_labels = 0; @@ -7972,12 +8044,10 @@ QCC_def_t *QCC_PR_EmitArrayGetVector(QCC_def_t *array) df->first_statement = numstatements; df->parm_size[0] = 1; df->numparms = 1; - df->parm_start = numpr_globals; + locals_start = locals_end = FIRST_LOCAL; index = QCC_PR_GetDef(type_float, "index___", func, true, 0, false); index->references++; temp = QCC_PR_GetDef(type_float, "div3___", func, true, 0, false); - locals_end = numpr_globals; - df->locals = locals_end - df->parm_start; QCC_PR_Statement3(pr_opcodes+OP_DIV_F, index, QCC_MakeFloatConst(3), temp, false); QCC_PR_Statement3(pr_opcodes+OP_BITAND_F, temp, temp, temp, false);//round down to int @@ -7988,6 +8058,12 @@ QCC_def_t *QCC_PR_EmitArrayGetVector(QCC_def_t *array) G_FUNCTION(func->ofs) = df - functions; func->initialized = 1; + + QCC_WriteAsmFunction(pr_scope, df->first_statement, df->parm_start); + QCC_Marshal_Locals(df->first_statement, numstatements); + QCC_FreeTemps(); + df->parm_start = locals_start; + df->numparms = locals_end - locals_start; return func; } @@ -8033,7 +8109,7 @@ void QCC_PR_EmitArrayGetFunction(QCC_def_t *scope, char *arrayname) df->first_statement = numstatements; df->parm_size[0] = 1; df->numparms = 1; - df->parm_start = numpr_globals; + locals_start = locals_end = FIRST_LOCAL; index = QCC_PR_GetDef(type_float, "indexg___", def, true, 0, false); G_FUNCTION(scope->ofs) = df - functions; @@ -8118,13 +8194,11 @@ void QCC_PR_EmitArrayGetFunction(QCC_def_t *scope, char *arrayname) QCC_PR_Statement(pr_opcodes+OP_DONE, 0, 0, NULL); - locals_end = numpr_globals; - df->locals = locals_end - df->parm_start; - - QCC_WriteAsmFunction(pr_scope, df->first_statement, df->parm_start); - + QCC_Marshal_Locals(df->first_statement, numstatements); QCC_FreeTemps(); + df->parm_start = locals_start; + df->numparms = locals_end - locals_start; } void QCC_PR_ArraySetRecurseDivide(QCC_def_t *array, QCC_def_t *index, QCC_def_t *value, int min, int max) @@ -8203,11 +8277,9 @@ void QCC_PR_EmitArraySetFunction(QCC_def_t *scope, char *arrayname) df->parm_size[0] = 1; df->parm_size[1] = def->type->size; df->numparms = 2; - df->parm_start = numpr_globals; + locals_start = locals_end = FIRST_LOCAL; index = QCC_PR_GetDef(type_float, "indexs___", def, true, 0, false); value = QCC_PR_GetDef(def->type, "value___", def, true, 0, false); - locals_end = numpr_globals; - df->locals = locals_end - df->parm_start; G_FUNCTION(scope->ofs) = df - functions; @@ -8241,15 +8313,17 @@ void QCC_PR_EmitArraySetFunction(QCC_def_t *scope, char *arrayname) QCC_WriteAsmFunction(pr_scope, df->first_statement, df->parm_start); - + QCC_Marshal_Locals(df->first_statement, numstatements); QCC_FreeTemps(); + df->parm_start = locals_start; + df->numparms = locals_end - locals_start; } //register a def, and all of it's sub parts. //only the main def is of use to the compiler. //the subparts are emitted to the compiler and allow correct saving/loading //be careful with fields, this doesn't allocated space, so will it allocate fields. It only creates defs at specified offsets. -QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_def_t *scope, int arraysize, unsigned int ofs, int referable, pbool saved) +QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_def_t *scope, int arraysize, unsigned int ofs, int referable, unsigned int flags) { char array[64]; char newname[256]; @@ -8315,13 +8389,10 @@ QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_def_t *scope, int a def->type = type; def->scope = scope; - def->saved = saved; + def->saved = !!(flags & GDF_SAVED); + def->constant = !!(flags & GDF_CONST); + def->isstatic = !!(flags & GDF_STATIC); - //if (type->type = ev_field) - def->constant = true; - - if (ofs + type->size*a >= MAX_REGS) - QCC_Error(ERR_TOOMANYGLOBALS, "MAX_REGS is too small"); def->ofs = ofs + type->size*a; if (!first) first = def; @@ -8339,14 +8410,14 @@ QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_def_t *scope, int a { case ev_vector: sprintf(newname, "%s%s.%s", name, array, parttype->name); - QCC_PR_DummyDef(parttype, newname, scope, 0, ofs + type->size*a + parttype->ofs, false, saved); + QCC_PR_DummyDef(parttype, newname, scope, 0, ofs + type->size*a + parttype->ofs, false, flags | GDF_CONST); sprintf(newname, "%s%s.%s_x", name, array, parttype->name); - QCC_PR_DummyDef(type_float, newname, scope, 0, ofs + type->size*a + parttype->ofs, false, false); + QCC_PR_DummyDef(type_float, newname, scope, 0, ofs + type->size*a + parttype->ofs, false, flags | GDF_CONST); sprintf(newname, "%s%s.%s_y", name, array, parttype->name); - QCC_PR_DummyDef(type_float, newname, scope, 0, ofs + type->size*a + parttype->ofs+1, false, false); + QCC_PR_DummyDef(type_float, newname, scope, 0, ofs + type->size*a + parttype->ofs+1, false, flags | GDF_CONST); sprintf(newname, "%s%s.%s_z", name, array, parttype->name); - QCC_PR_DummyDef(type_float, newname, scope, 0, ofs + type->size*a + parttype->ofs+2, false, false); + QCC_PR_DummyDef(type_float, newname, scope, 0, ofs + type->size*a + parttype->ofs+2, false, flags | GDF_CONST); break; case ev_float: @@ -8359,12 +8430,12 @@ QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_def_t *scope, int a case ev_union: case ev_variant: //for lack of any better alternative sprintf(newname, "%s%s.%s", name, array, parttype->name); - QCC_PR_DummyDef(parttype, newname, scope, 0, ofs + type->size*a + parttype->ofs, false, saved); + QCC_PR_DummyDef(parttype, newname, scope, 0, ofs + type->size*a + parttype->ofs, false, flags); break; case ev_function: sprintf(newname, "%s%s.%s", name, array, parttype->name); - QCC_PR_DummyDef(parttype, newname, scope, 0, ofs + type->size*a +parttype->ofs, false, saved)->initialized = true; + QCC_PR_DummyDef(parttype, newname, scope, 0, ofs + type->size*a +parttype->ofs, false, flags)->initialized = true; break; case ev_void: break; @@ -8375,11 +8446,11 @@ QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_def_t *scope, int a else if (type->type == ev_vector) { //do the vector thing. sprintf(newname, "%s%s_x", name, array); - QCC_PR_DummyDef(type_float, newname, scope, 0, ofs + type->size*a+0, referable, false); + QCC_PR_DummyDef(type_float, newname, scope, 0, ofs + type->size*a+0, referable, flags | GDF_CONST); sprintf(newname, "%s%s_y", name, array); - QCC_PR_DummyDef(type_float, newname, scope, 0, ofs + type->size*a+1, referable, false); + QCC_PR_DummyDef(type_float, newname, scope, 0, ofs + type->size*a+1, referable, flags | GDF_CONST); sprintf(newname, "%s%s_z", name, array); - QCC_PR_DummyDef(type_float, newname, scope, 0, ofs + type->size*a+2, referable, false); + QCC_PR_DummyDef(type_float, newname, scope, 0, ofs + type->size*a+2, referable, flags | GDF_CONST); } else if (type->type == ev_field) { @@ -8387,11 +8458,11 @@ QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_def_t *scope, int a { //do the vector thing. sprintf(newname, "%s%s_x", name, array); - QCC_PR_DummyDef(type_floatfield, newname, scope, 0, ofs + type->size*a+0, referable, false); + QCC_PR_DummyDef(type_floatfield, newname, scope, 0, ofs + type->size*a+0, referable, flags | GDF_CONST); sprintf(newname, "%s%s_y", name, array); - QCC_PR_DummyDef(type_floatfield, newname, scope, 0, ofs + type->size*a+1, referable, false); + QCC_PR_DummyDef(type_floatfield, newname, scope, 0, ofs + type->size*a+1, referable, flags | GDF_CONST); sprintf(newname, "%s%s_z", name, array); - QCC_PR_DummyDef(type_floatfield, newname, scope, 0, ofs + type->size*a+2, referable, false); + QCC_PR_DummyDef(type_floatfield, newname, scope, 0, ofs + type->size*a+2, referable, flags | GDF_CONST); } } first->deftail = pr.def_tail; @@ -8427,12 +8498,11 @@ If arraysize<0, its an array with undefined size - GetDef will fail if its not a ============ */ -QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, QCC_def_t *scope, pbool allocate, int arraysize, pbool saved) +QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, QCC_def_t *scope, pbool allocate, int arraysize, unsigned int flags) { int ofs; QCC_def_t *def; // char element[MAX_NAME]; - unsigned int i; QCC_def_t *foundstatic = NULL; if (!allocate) @@ -8620,7 +8690,10 @@ QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, QCC_def_t *scope, pbool ofs = numpr_globals; if (arraysize) { //write the array size - ofs = QCC_GetFreeOffsetSpace(1 + (type->size * arraysize)); + if (scope && !(flags & (GDF_CONST|GDF_STATIC))) + ofs = QCC_GetFreeLocalOffsetSpace(1 + (type->size * arraysize)); + else + ofs = QCC_GetFreeGlobalOffsetSpace(1 + (type->size * arraysize)); //An array needs the size written first. This is a hexen2 opcode thing. //note that for struct emulation, union and struct arrays, the size is the total size of global slots, rather than array elements @@ -8630,21 +8703,14 @@ QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, QCC_def_t *scope, pbool ((int *)qcc_pr_globals)[ofs] = (arraysize*type->size)-1; ofs++; } + else if (scope && !(flags & (GDF_CONST|GDF_STATIC))) + ofs = QCC_GetFreeLocalOffsetSpace(type->size); else - ofs = QCC_GetFreeOffsetSpace(type->size); + ofs = QCC_GetFreeGlobalOffsetSpace(type->size); - def = QCC_PR_DummyDef(type, name, scope, arraysize, ofs, true, saved); + def = QCC_PR_DummyDef(type, name, scope, arraysize, ofs, true, flags); - //fix up fields. - if (type->type == ev_field && allocate != 2) - { - for (i = 0; i < type->size*(arraysize?arraysize:1); i++) //make arrays of fields work. - *(int *)&qcc_pr_globals[def->ofs+i] = pr.size_fields+i; - - pr.size_fields += i; - } - - if (scope) + if (scope && !(flags & (GDF_CONST|GDF_STATIC))) { def->nextlocal = pr.localvars; pr.localvars = def; @@ -8694,7 +8760,7 @@ QCC_def_t *QCC_PR_DummyFieldDef(QCC_type_t *type, char *name, QCC_def_t *scope, def->scope = scope; - def->ofs = QCC_GetFreeOffsetSpace(1); + def->ofs = scope?QCC_GetFreeLocalOffsetSpace(1):QCC_GetFreeGlobalOffsetSpace(1); ((int *)qcc_pr_globals)[def->ofs] = *fieldofs; fieldofs++; if (!first) @@ -8906,7 +8972,7 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *def, QCC_type_t *type { QCC_def_t *dt; sprintf(trname, "dotranslate_%i", ++dotranslate_count); - dt = QCC_PR_DummyDef(type_string, trname, pr_scope, 0, offset, true, true); + dt = QCC_PR_DummyDef(type_string, trname, pr_scope, 0, offset, true, GDF_CONST); dt->references = 1; dt->constant = 1; dt->initialized = 1; @@ -8956,7 +9022,7 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *def, QCC_type_t *type if (!tmp->constant) QCC_PR_ParseErrorPrintDef (ERR_BADIMMEDIATETYPE, def, "initializer is not constant"); - if (def->initialized) + if (def->initialized && def->initialized != 3) { for (i = 0; (unsigned)i < type->size; i++) if (G_INT(offset+i) != G_INT(tmp->ofs+i)) @@ -9001,9 +9067,8 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *def, QCC_type_t *type } } -void QCC_PR_ParseInitializerDef(QCC_def_t *def, pbool isvar, pbool isconst) +void QCC_PR_ParseInitializerDef(QCC_def_t *def) { - def->constant = (isconst || (!isvar && !pr_scope)); QCC_PR_ParseInitializerType(def->arraysize, def, def->type, def->ofs); if (!def->initialized) def->initialized = 1; @@ -9034,8 +9099,8 @@ void QCC_PR_ParseDefs (char *classname) pbool nosave = false; pbool allocatenew = true; pbool inlinefunction = false; - gofs_t oldglobals; int arraysize; + unsigned int gd_flags; while (QCC_PR_CheckToken(";")) ; @@ -9629,15 +9694,25 @@ void QCC_PR_ParseDefs (char *classname) else pr_classtype = NULL; - oldglobals = numpr_globals; + gd_flags = 0; + if (isstatic) + gd_flags |= GDF_STATIC; + if (isconstant) + gd_flags |= GDF_CONST; + if (!nosave) + gd_flags |= GDF_SAVED; - def = QCC_PR_GetDef (type, name, pr_scope, allocatenew, arraysize, !nosave); + def = QCC_PR_GetDef (type, name, pr_scope, allocatenew, arraysize, gd_flags); if (!def) QCC_PR_ParseError(ERR_NOTANAME, "%s is not part of class %s", name, classname); if (noref) + { + if (type->type == ev_function && !def->initialized) + def->initialized = 3; def->references++; + } if (!def->initialized && shared) //shared count as initiialised { @@ -9730,7 +9805,8 @@ void QCC_PR_ParseDefs (char *classname) continue; } - QCC_PR_ParseInitializerDef(def, isvar, isconstant); + def->constant = (isconstant || (!isvar && !pr_scope)); + QCC_PR_ParseInitializerDef(def); } else { @@ -9742,12 +9818,22 @@ void QCC_PR_ParseDefs (char *classname) if (type->type == ev_field) { + //fields are const by default, even when not initialised (as they are initialised behind the scenes) if (isconstant) def->constant = 2; //special flag on fields, 2, makes the pointer obtained from them also constant. - else if (isvar) + else if (isvar || (pr_scope && !isstatic)) def->constant = 0; else def->constant = 1; + + if (def->constant) + { + unsigned int i; + for (i = 0; i < type->size*(arraysize?arraysize:1); i++) //make arrays of fields work. + *(int *)&qcc_pr_globals[def->ofs+i] = pr.size_fields+i; + + pr.size_fields += i; + } } else def->constant = isconstant; diff --git a/engine/qclib/qcc_pr_lex.c b/engine/qclib/qcc_pr_lex.c index ce53aa52..6973370c 100644 --- a/engine/qclib/qcc_pr_lex.c +++ b/engine/qclib/qcc_pr_lex.c @@ -369,9 +369,10 @@ int ParsePrecompilerIf(int level) static void QCC_PR_SkipToEndOfLine(void) { + pbool handlecomments = true; while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line { - if (*pr_file_p == '/' && pr_file_p[1] == '*') + if (*pr_file_p == '/' && pr_file_p[1] == '*' && handlecomments) { pr_file_p += 2; while(*pr_file_p) @@ -386,6 +387,11 @@ static void QCC_PR_SkipToEndOfLine(void) *pr_file_p++; } } + else if (*pr_file_p == '/' && pr_file_p[1] == '/' && handlecomments) + { + handlecomments = false; + pr_file_p += 2; + } else if (*pr_file_p == '\\' && pr_file_p[1] == '\r' && pr_file_p[2] == '\n') { /*windows endings*/ pr_file_p+=3; @@ -569,7 +575,7 @@ pbool QCC_PR_Precompiler(void) if (!strncmp(pr_file_p, "endif", 5)) level--; if (!strncmp(pr_file_p, "if", 2)) - level++; + level++; if (!strncmp(pr_file_p, "else", 4) && level == 1) { ifs+=1; @@ -1402,6 +1408,46 @@ void QCC_PR_LexString (void) pr_token_type = tt_immediate; pr_immediate_type = type_string; strcpy (pr_immediate_string, pr_token); + + if (qccwarningaction[WARN_NOTUTF8]) + { + len = 0; + //this doesn't do over-long checks. + for (c = 0; pr_token[c]; c++) + { + if (len) + { + if ((pr_token[c] & 0xc0) != 0x80) + break; + len--; + } + else if (pr_token[c] & 0x80) + { + if (!(pr_token[c] & 0x40)) + { + //error. + len = 1; + break; + } + else if (!(pr_token[c] & 0x20)) + len = 2; + else if (!(pr_token[c] & 0x10)) + len = 3; + else if (!(pr_token[c] & 0x08)) + len = 4; + else if (!(pr_token[c] & 0x04)) + len = 5; + else if (!(pr_token[c] & 0x02)) + len = 6; + else if (!(pr_token[c] & 0x01)) + len = 7; + else + len = 8; + } + } + if (len) + QCC_PR_ParseWarning(WARN_NOTUTF8, "String constant is not valid utf-8\n"); + } return; } else if (c == '#') diff --git a/engine/qclib/qccgui.c b/engine/qclib/qccgui.c index 2911ed60..1640725b 100644 --- a/engine/qclib/qccgui.c +++ b/engine/qclib/qccgui.c @@ -15,7 +15,7 @@ LoadFile ============== */ -unsigned char *QCC_ReadFile (const char *fname, void *buffer, int len) +unsigned char *PDECL QCC_ReadFile (const char *fname, void *buffer, int len) { long length; FILE *f; @@ -30,7 +30,7 @@ unsigned char *QCC_ReadFile (const char *fname, void *buffer, int len) return buffer; } -int QCC_FileSize (const char *fname) +int PDECL QCC_FileSize (const char *fname) { long length; FILE *f; @@ -44,7 +44,7 @@ int QCC_FileSize (const char *fname) return length; } -pbool QCC_WriteFile (const char *name, void *data, int len) +pbool PDECL QCC_WriteFile (const char *name, void *data, int len) { long length; FILE *f; diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index e8a3695c..6c2c7dca 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -28,6 +28,8 @@ void *FS_ReadToMem(char *fname, void *membuf, int *len); void FS_CloseFromMem(void *mem); unsigned int MAX_REGS; +unsigned int MAX_LOCALS = 0x10000; +unsigned int MAX_TEMPS = 0x10000; int MAX_STRINGS; int MAX_GLOBALS; @@ -175,6 +177,8 @@ struct { {" Q111", WARN_DUPLICATELABEL}, {" Q201", WARN_ASSIGNMENTINCONDITIONAL}, {" F300", WARN_DEADCODE}, + {" F301", WARN_NOTUTF8}, + {" F302", WARN_UNINITIALIZED}, //frikqcc errors //Q608: PrecacheSound: numsounds @@ -234,11 +238,11 @@ optimisations_t optimisations[] = {&opt_overlaptemps, "r", 1, FLAG_ASDEFAULT, "overlaptemps", "Optimises the pr_globals count by overlapping temporaries. In QC, every multiplication, division or operation in general produces a temporary variable. This optimisation prevents excess, and in the case of Hexen2's gamecode, reduces the count by 50k. This is the most important optimisation, ever."}, {&opt_constantarithmatic, "a", 1, FLAG_ASDEFAULT, "constantarithmatic", "5*6 actually emits an operation into the progs. This prevents that happening, effectivly making the compiler see 30"}, {&opt_precache_file, "pf", 2, 0, "precache_file", "Strip out stuff wasted used in function calls and strings to the precache_file builtin (which is actually a stub in quake)."}, - {&opt_return_only, "ro", 3, FLAG_KILLSDEBUGGERS, "return_only", "Functions ending in a return statement do not need a done statement at the end of the function. This can confuse some decompilers, making functions appear larger than they were."}, - {&opt_compound_jumps, "cj", 3, FLAG_KILLSDEBUGGERS, "compound_jumps", "This optimisation plays an effect mostly with nested if/else statements, instead of jumping to an unconditional jump statement, it'll jump to the final destination instead. This will bewilder decompilers."}, + {&opt_return_only, "ro", 3, FLAG_KILLSDEBUGGERS, "return_only", "Functions ending in a return statement do not need a done statement at the end of the function. This can confuse some decompilers, making functions appear larger than they were."}, + {&opt_compound_jumps, "cj", 3, FLAG_KILLSDEBUGGERS, "compound_jumps", "This optimisation plays an effect mostly with nested if/else statements, instead of jumping to an unconditional jump statement, it'll jump to the final destination instead. This will bewilder decompilers."}, // {&opt_comexprremoval, "cer", 4, 0, "expression_removal", "Eliminate common sub-expressions"}, //this would be too hard... {&opt_stripfunctions, "sf", 4, 0, "strip_functions", "Strips out the 'defs' of functions that were only ever called directly. This does not affect saved games. This can affect FTE_MULTIPROGS."}, - {&opt_locals_marshalling, "lm", 4, FLAG_KILLSDEBUGGERS, "locals_marshalling", "Store all locals in one section of the pr_globals. Vastly reducing it. This effectivly does the job of overlaptemps. It's been noticed as buggy by a few, however, and the curcumstances where it causes problems are not yet known."}, + {&opt_locals_overlapping, "lo", 4, FLAG_KILLSDEBUGGERS, "locals_overlapping", "Store all locals in a single section of the pr_globals. Vastly reducing it. This effectivly does the job of overlaptemps.\nHowever, locals are no longer automatically initialised to 0 (and never were in the case of recursion, but at least then its the same type).\nIf locals appear uninitialised, fteqcc will disable this optimisation for the affected functions, you can optionally get a warning about these locals using: #pragma warning enable F302"}, {&opt_vectorcalls, "vc", 4, FLAG_KILLSDEBUGGERS, "vectorcalls", "Where a function is called with just a vector, this causes the function call to store three floats instead of one vector. This can save a good number of pr_globals where those vectors contain many duplicate coordinates but do not match entirly."}, {NULL} }; @@ -366,11 +370,17 @@ void QCC_BspModels (void) */ } +typedef struct stringtab_s +{ + struct stringtab_s *prev; + char *ofs; + int len; +} stringtab_t; +stringtab_t *stringtablist[256]; // CopyString returns an offset from the string heap int QCC_CopyString (char *str) { int old; - char *s; if (!str) return 0; @@ -379,12 +389,35 @@ int QCC_CopyString (char *str) if (opt_noduplicatestrings) { +#if 1 + //more scalable (faster) version. + stringtab_t *t; + int len = strlen(str); + unsigned char key = str[len-1]; + for (t = stringtablist[key]; t; t = t->prev) + { + if (t->len >= len) + if (!strcmp(t->ofs + t->len - len, str)) + { + optres_noduplicatestrings += len; + return (t->ofs + t->len - len)-strings; + } + } + t = qccHunkAlloc(sizeof(*t)); + t->prev = stringtablist[key]; + stringtablist[key] = t; + t->ofs = strings+strofs; + t->len = len; +#elif 1 + //bruteforce + char *s; for (s = strings; s < strings+strofs; s++) if (!strcmp(s, str)) { optres_noduplicatestrings += strlen(str); return s-strings; } +#endif } old = strofs; @@ -542,6 +575,7 @@ void QCC_InitData (void) qcc_sourcefile = NULL; + memset(stringtablist, 0, sizeof(stringtablist)); numstatements = 1; strofs = 2; numfunctions = 1; @@ -588,42 +622,43 @@ int WriteBodylessFuncs (int handle) return ret; } -//marshalled locals remaps all the functions to use the range MAX_REGS onwards for the offset to their locals. -//this function remaps all the locals back into the function. +//marshalled locals still point to the FIRST_LOCAL range. +//this function remaps all the locals back into actual usable defs. void QCC_UnmarshalLocals(void) { QCC_def_t *def; unsigned int ofs; unsigned int maxo; int i; + extern int tempsused; ofs = numpr_globals; maxo = ofs; for (def = pr.def_head.next ; def ; def = def->next) { - if (def->ofs >= MAX_REGS) //unmap defs. + if (def->ofs >= FIRST_LOCAL) //unmap defs. { - def->ofs = def->ofs + ofs - MAX_REGS; - if (maxo < def->ofs) - maxo = def->ofs; + def->ofs = def->ofs + ofs - FIRST_LOCAL; + if (maxo < def->ofs + def->type->size) + maxo = def->ofs + def->type->size; } } for (i = 0; i < numfunctions; i++) { - if (functions[i].parm_start == MAX_REGS) + if (functions[i].parm_start == FIRST_LOCAL) functions[i].parm_start = ofs; } - QCC_RemapOffsets(0, numstatements, MAX_REGS, MAX_REGS + maxo-numpr_globals + 3, ofs); + QCC_RemapOffsets(0, numstatements, FIRST_LOCAL, FIRST_LOCAL+(maxo-ofs), ofs); + QCC_RemapOffsets(0, numstatements, FIRST_TEMP, FIRST_TEMP+tempsused, maxo); - numpr_globals = maxo+3; + numpr_globals = maxo+tempsused; if (numpr_globals > MAX_REGS) QCC_Error(ERR_TOOMANYGLOBALS, "Too many globals are in use to unmarshal all locals"); - if (maxo-ofs) - printf("Total of %i marshalled globals\n", maxo-ofs); + printf("Total of %i locals, %i temps\n", maxo-ofs, tempsused); } CompilerConstant_t *QCC_PR_CheckCompConstDefined(char *def); @@ -825,7 +860,7 @@ pbool QCC_WriteData (int crc) if (def->type->type == ev_function) { - if (opt_function_names && functions[G_FUNCTION(def->ofs)].first_statement<0) + if (opt_function_names && def->initialized && functions[G_FUNCTION(def->ofs)].first_statement<0) { optres_function_names++; def->name = ""; @@ -847,7 +882,7 @@ pbool QCC_WriteData (int crc) // numfunctions++; } - else if (def->type->type == ev_field && def->constant) + else if (def->type->type == ev_field && def->constant && (!def->scope || def->isstatic)) { if (numfielddefs >= MAX_FIELDS) QCC_PR_ParseError(0, "Too many fields. Limit is %u\n", MAX_FIELDS); @@ -1018,8 +1053,6 @@ strofs = (strofs+3)&~3; } } - for (i=0 ; iReadFile = QCC_ReadFile; - funcs.parms->FileSize = QCC_FileSize; - funcs.parms->WriteFile = QCC_WriteFile; - funcs.parms->printf = logprintf; - funcs.parms->Sys_Error = Sys_Error; + funcs.funcs.parms->ReadFile = QCC_ReadFile; + funcs.funcs.parms->FileSize = QCC_FileSize; + funcs.funcs.parms->WriteFile = QCC_WriteFile; + funcs.funcs.parms->Printf = logprintf; + funcs.funcs.parms->Sys_Error = Sys_Error; logfile = fopen("fteqcc.log", "wt"); sucess = CompileParams(&funcs, true, argc, argv); qccClearHunk();