From 43a1890bcc203f6b2a7b4b0260cb3f43d6505c46 Mon Sep 17 00:00:00 2001 From: Spoike Date: Wed, 14 Sep 2005 04:36:07 +0000 Subject: [PATCH] Fixed an issue with fteqw's pr_fixbrokenqccarrays cvar when used with mutators. Also fixed field remapping with regard to multiple fields with the same offsets. Played around with classes a bit. Should work a bit better now. Added an extra compiler flag, to allow for fast-tracking arrays in supported engines. It uses a global to detect it, but we don't yet set that global anywhere in the loader. This will probably have issues in DP. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@1325 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/qclib/execloop.h | 6 +++ engine/qclib/pr_comp.h | 2 +- engine/qclib/pr_edict.c | 28 ++++--------- engine/qclib/pr_exec.c | 2 +- engine/qclib/pr_multi.c | 82 +++++++++++++++++++++++++++----------- engine/qclib/qcc.h | 1 + engine/qclib/qcc_pr_comp.c | 65 ++++++++++++++++++++++++++++-- engine/qclib/qcc_pr_lex.c | 1 - engine/qclib/qccmain.c | 1 + 9 files changed, 138 insertions(+), 50 deletions(-) diff --git a/engine/qclib/execloop.h b/engine/qclib/execloop.h index 3df6b7aa..f7186ec7 100644 --- a/engine/qclib/execloop.h +++ b/engine/qclib/execloop.h @@ -388,6 +388,8 @@ reeval: //get a pointer to a field var case OP_ADDRESS: + if ((unsigned)OPA->edict >= maxedicts) + PR_RunError (progfuncs, "OP_ADDRESS references invalid entity in %s", progfuncs->stringtable + pr_xfunction->s_name); ed = PROG_TO_EDICT(progfuncs, OPA->edict); #ifdef PARANOID NUM_FOR_EDICT(ed); // make sure it's in range @@ -407,6 +409,8 @@ reeval: case OP_LOAD_ENT: case OP_LOAD_S: case OP_LOAD_FNC: + if ((unsigned)OPA->edict >= maxedicts) + PR_RunError (progfuncs, "OP_LOAD references invalid entity in %s", progfuncs->stringtable + pr_xfunction->s_name); ed = PROG_TO_EDICT(progfuncs, OPA->edict); #ifdef PARANOID NUM_FOR_EDICT(ed); // make sure it's in range @@ -416,6 +420,8 @@ reeval: break; case OP_LOAD_V: + if ((unsigned)OPA->edict >= maxedicts) + PR_RunError (progfuncs, "OP_LOAD_V references invalid entity in %s", progfuncs->stringtable + 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/pr_comp.h b/engine/qclib/pr_comp.h index 54434a8d..f1506c9a 100644 --- a/engine/qclib/pr_comp.h +++ b/engine/qclib/pr_comp.h @@ -360,7 +360,7 @@ typedef struct fdef_s unsigned int type; // if DEF_SAVEGLOBAL bit is set // the variable needs to be saved in savegames unsigned int ofs; - unsigned int requestedofs; + unsigned int progsofs; //used at loading time, so maching field offsets (unions/members) are positioned at the same runtime offset. char * name; } fdef_t; diff --git a/engine/qclib/pr_edict.c b/engine/qclib/pr_edict.c index 971576e1..dff4e1e5 100644 --- a/engine/qclib/pr_edict.c +++ b/engine/qclib/pr_edict.c @@ -1439,28 +1439,13 @@ char *SaveCallStack (progfuncs_t *progfuncs, char *s) sprintf(buffer, "\t\tofs%i %i // %f\n", f->parm_start+arg, *(int *)(globalbase - f->locals+arg), *(float *)(globalbase - f->locals+arg) ); else { -//__try -//{ if (local->type == ev_entity) - { //go safly. - int n; - sprintf(buffer, "\t\t\"%s\"\t\"entity INVALID POINTER\"\n", local->s_name+progfuncs->stringtable); - for (n = 0; n < sv_num_edicts; n++) - { - if (prinst->edicttable[n] == (struct edict_s *)PROG_TO_EDICT(progfuncs, ((eval_t*)(globalbase - f->locals+arg))->edict)) - { - sprintf(buffer, "\t\t\"%s\" \"entity %i\"\n", local->s_name+progfuncs->stringtable, n); - break; - } - } + { + sprintf(buffer, "\t\t\"%s\" \"entity %i\"\n", local->s_name+progfuncs->stringtable, ((eval_t*)(globalbase - f->locals+arg))->edict); } else sprintf(buffer, "\t\t\"%s\"\t\"%s\"\n", local->s_name+progfuncs->stringtable, PR_ValueString(progfuncs, local->type, (eval_t*)(globalbase - f->locals+arg))); -//} -//__except(EXCEPTION_EXECUTE_HANDLER) -//{ -// sprintf(buffer, "\t\t\"%s\" \"ILLEGAL POINTER\"\n", local->s_name+progfuncs->stringtable); -//} + if (local->type == ev_vector) arg+=2; } @@ -2632,6 +2617,7 @@ retry: if (reorg) reorg = (headercrc != -1); + QC_FlushProgsOffsets(progfuncs); switch(current_progstate->intsize) { case 24: @@ -2662,7 +2648,7 @@ retry: else type = fld16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL); - if (progfuncs->fieldadjust) //we need to make sure all fields appear in thier original place. + if (progfuncs->fieldadjust && !pr_typecurrent) //we need to make sure all fields appear in thier original place. QC_RegisterFieldVar(progfuncs, type, fld16[i].s_name+pr_strings, 4*(fld16[i].ofs+progfuncs->fieldadjust), -1); else if (type == ev_vector) //emit vector vars early, so thier fields cannot be alocated before the vector itself. (useful against scramblers) { @@ -2697,7 +2683,9 @@ retry: type = pr_types[pr_fielddefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type; else type = pr_fielddefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL); - if (type == ev_vector) + if (progfuncs->fieldadjust && !pr_typecurrent) //we need to make sure all fields appear in thier original place. + QC_RegisterFieldVar(progfuncs, type, fld16[i].s_name+pr_strings, 4*(fld16[i].ofs+progfuncs->fieldadjust), -1); + else if (type == ev_vector) QC_RegisterFieldVar(progfuncs, type, pr_fielddefs32[i].s_name+pr_strings, -1, -1); } pr_fielddefs32[i].s_name += stringadjust; diff --git a/engine/qclib/pr_exec.c b/engine/qclib/pr_exec.c index 308875a7..134208f4 100644 --- a/engine/qclib/pr_exec.c +++ b/engine/qclib/pr_exec.c @@ -212,7 +212,7 @@ void VARGS PR_RunError (progfuncs_t *progfuncs, char *error, ...) // pr_depth = 0; // dump the stack so host_error can shutdown functions // prinst->exitdepth = 0; - Abort (string); + Abort ("%s", string); } /* diff --git a/engine/qclib/pr_multi.c b/engine/qclib/pr_multi.c index 1ceaf6fa..fc0a76b2 100644 --- a/engine/qclib/pr_multi.c +++ b/engine/qclib/pr_multi.c @@ -180,13 +180,27 @@ void QC_InitShares(progfuncs_t *progfuncs) progfuncs->fieldadjust = 0; } +void QC_FlushProgsOffsets(progfuncs_t *progfuncs) +{ //sets the fields up for loading a new progs. + //fields are matched by name to other progs + //not by offset + unsigned int i; + for (i = 0; i < numfields; i++) + field[i].progsofs = -1; +} + //called if a global is defined as a field //returns offset. //vectors must be added before any of thier corresponding _x/y/z vars //in this way, even screwed up progs work. -int QC_RegisterFieldVar(progfuncs_t *progfuncs, unsigned int type, char *name, int requestedpos, int originalofs) + +//requestedpos is the offset the engine WILL put it at. +//origionaloffs is used to track matching field offsets. fields with the same progs offset overlap + +//note: we probably suffer from progs with renamed system globals. +int QC_RegisterFieldVar(progfuncs_t *progfuncs, unsigned int type, char *name, int engineofs, int progsofs) { // progstate_t *p; // int pnum; @@ -215,13 +229,13 @@ int QC_RegisterFieldVar(progfuncs_t *progfuncs, unsigned int type, char *name, i printf("Field type mismatch on %s\n", name); continue; } - if (!progfuncs->fieldadjust && requestedpos>=0) - if ((unsigned)requestedpos != field[i].ofs) + if (!progfuncs->fieldadjust && engineofs>=0) + if ((unsigned)engineofs != field[i].ofs) Sys_Error("Field %s at wrong offset", name); - if (field[i].requestedofs == -1) - field[i].requestedofs = originalofs; - return field[i].ofs; //got a match + if (field[i].progsofs == -1) + field[i].progsofs = progsofs; + return field[i].ofs-progfuncs->fieldadjust; //got a match } } @@ -240,29 +254,31 @@ int QC_RegisterFieldVar(progfuncs_t *progfuncs, unsigned int type, char *name, i fnum = numfields; numfields++; field[fnum].name = name; - if (type == ev_vector) //resize with the following floats (this is where I think I went wrong) + if (type == ev_vector) { char *n; namelen = strlen(name)+5; n=PRHunkAlloc(progfuncs, namelen); sprintf(n, "%s_x", name); - ofs = QC_RegisterFieldVar(progfuncs, ev_float, n, requestedpos, -1); + ofs = QC_RegisterFieldVar(progfuncs, ev_float, n, engineofs, progsofs); field[fnum].ofs = ofs; n=PRHunkAlloc(progfuncs, namelen); sprintf(n, "%s_y", name); - QC_RegisterFieldVar(progfuncs, ev_float, n, (requestedpos==-1)?-1:(requestedpos+4), -1); + QC_RegisterFieldVar(progfuncs, ev_float, n, (engineofs==-1)?-1:(engineofs+4), (progsofs==-1)?-1:progsofs+1); n=PRHunkAlloc(progfuncs, namelen); sprintf(n, "%s_z", name); - QC_RegisterFieldVar(progfuncs, ev_float, n, (requestedpos==-1)?-1:(requestedpos+8), -1); + QC_RegisterFieldVar(progfuncs, ev_float, n, (engineofs==-1)?-1:(engineofs+8), (progsofs==-1)?-1:progsofs+2); } - else if (requestedpos >= 0) - { + else if (engineofs >= 0) + { //the engine is setting up a list of required field indexes. + + //paranoid checking of the offset. for (i = 0; i < numfields-1; i++) { - if (field[i].ofs == (unsigned)requestedpos) + if (field[i].ofs == ((unsigned)engineofs)*4) { if (type == ev_float && field[i].type == ev_vector) //check names { @@ -273,12 +289,27 @@ int QC_RegisterFieldVar(progfuncs_t *progfuncs, unsigned int type, char *name, i Sys_Error("Duplicated offset"); } } - if (requestedpos&3) - Sys_Error("field %s is %i&3", name, requestedpos); - field[fnum].ofs = ofs = requestedpos/4; + if (engineofs&3) + Sys_Error("field %s is %i&3", name, engineofs); + field[fnum].ofs = ofs = engineofs/4; } else - field[fnum].ofs = ofs = fields_size/4; + { //we just found a new fieldname inside a progs + field[fnum].ofs = ofs = fields_size/4; //add on the end + + //if the progs field offset matches annother offset in the same progs, make it match up with the earlier one. + if (progsofs>=0) + { + for (i = 0; i < numfields-1; i++) + { + if (field[i].progsofs == (unsigned)progsofs) + { + field[fnum].ofs = ofs = field[i].ofs; + break; + } + } + } + } // if (type != ev_vector) if (fields_size < (ofs+type_size[type])*4) fields_size = (ofs+type_size[type])*4; @@ -287,10 +318,10 @@ int QC_RegisterFieldVar(progfuncs_t *progfuncs, unsigned int type, char *name, i Sys_Error("Allocated too many additional fields after ents were inited."); field[fnum].type = type; - field[fnum].requestedofs = originalofs; + field[fnum].progsofs = progsofs; //we've finished setting the structure - return ofs; + return ofs - progfuncs->fieldadjust; } @@ -326,7 +357,10 @@ void QC_AddSharedFieldVar(progfuncs_t *progfuncs, int num, char *stringtable) { if (!strcmp(pr_fielddefs16[i].s_name+stringtable, pr_globaldefs16[num].s_name+stringtable)) { - *(int *)&pr_globals[pr_globaldefs16[num].ofs] = QC_RegisterFieldVar(progfuncs, pr_fielddefs16[i].type, pr_globaldefs16[num].s_name+stringtable, -1, *(int *)&pr_globals[pr_globaldefs16[num].ofs])-progfuncs->fieldadjust; + int old = *(int *)&pr_globals[pr_globaldefs16[num].ofs]; + *(int *)&pr_globals[pr_globaldefs16[num].ofs] = QC_RegisterFieldVar(progfuncs, pr_fielddefs16[i].type, pr_globaldefs16[num].s_name+stringtable, -1, *(int *)&pr_globals[pr_globaldefs16[num].ofs]); + + printf("Field %s %i -> %i\n", pr_globaldefs16[num].s_name+stringtable, old, *(int *)&pr_globals[pr_globaldefs16[num].ofs]); return; } } @@ -335,10 +369,12 @@ void QC_AddSharedFieldVar(progfuncs_t *progfuncs, int num, char *stringtable) for (i = 0; i < numfields; i++) { - o = field[i].requestedofs; + o = field[i].progsofs; if (o == *(unsigned int *)&pr_globals[pr_globaldefs16[num].ofs]) { + int old = *(int *)&pr_globals[pr_globaldefs16[num].ofs]; *(int *)&pr_globals[pr_globaldefs16[num].ofs] = field[i].ofs-progfuncs->fieldadjust; + printf("Field %s %i -> %i\n", pr_globaldefs16[num].s_name+stringtable, old, *(int *)&pr_globals[pr_globaldefs16[num].ofs]); return; } } @@ -352,7 +388,7 @@ void QC_AddSharedFieldVar(progfuncs_t *progfuncs, int num, char *stringtable) { if (!strcmp(pr_fielddefs32[i].s_name+stringtable, pr_globaldefs32[num].s_name+stringtable)) { - *(int *)&pr_globals[pr_globaldefs32[num].ofs] = QC_RegisterFieldVar(progfuncs, pr_fielddefs32[i].type, pr_globaldefs32[num].s_name+stringtable, -1, *(int *)&pr_globals[pr_globaldefs32[num].ofs])-progfuncs->fieldadjust; + *(int *)&pr_globals[pr_globaldefs32[num].ofs] = QC_RegisterFieldVar(progfuncs, pr_fielddefs32[i].type, pr_globaldefs32[num].s_name+stringtable, -1, *(int *)&pr_globals[pr_globaldefs32[num].ofs]); return; } } @@ -361,7 +397,7 @@ void QC_AddSharedFieldVar(progfuncs_t *progfuncs, int num, char *stringtable) for (i = 0; i < numfields; i++) { - o = field[i].requestedofs; + o = field[i].progsofs; if (o == *(unsigned int *)&pr_globals[pr_globaldefs32[num].ofs]) { *(int *)&pr_globals[pr_globaldefs32[num].ofs] = field[i].ofs-progfuncs->fieldadjust; diff --git a/engine/qclib/qcc.h b/engine/qclib/qcc.h index 547f17f1..01af34f5 100644 --- a/engine/qclib/qcc.h +++ b/engine/qclib/qcc.h @@ -476,6 +476,7 @@ extern pbool flag_acc; extern pbool flag_caseinsensative; extern pbool flag_laxcasts; extern pbool flag_hashonly; +extern pbool flag_fasttrackarrays; extern pbool opt_overlaptemps; extern pbool opt_shortenifnots; diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index 8c34a945..165b3110 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -72,6 +72,7 @@ pbool flag_acc; //reacc like behaviour of src files (finds *.qc in start dir pbool flag_caseinsensative; //symbols will be matched to an insensative case if the specified case doesn't exist. This should b usable for any mod pbool flag_laxcasts; //Allow lax casting. This'll produce loadsa warnings of course. But allows compilation of certain dodgy code. pbool flag_hashonly; //Allows use of only #constant for precompiler constants, allows certain preqcc using mods to compile +pbool flag_fasttrackarrays; //Faster arrays, dynamically detected, activated only in supporting engines. pbool opt_overlaptemps; //reduce numpr_globals by reuse of temps. When they are not needed they are freed for reuse. The way this is implemented is better than frikqcc's. (This is the single most important optimisation) pbool opt_assignments; //STORE_F isn't used if an operation wrote to a temp. @@ -3141,6 +3142,14 @@ void QCC_PR_EmitClassFromFunction(QCC_def_t *scope, char *tname) if (!basetype) QCC_PR_ParseError(ERR_INTERNAL, "Type %s was not defined...", tname); + + pr_scope = NULL; + memset(basictypefield, 0, sizeof(basictypefield)); + QCC_PR_EmitFieldsForMembers(basetype); + + + + pr_scope = scope; df = &functions[numfunctions]; @@ -3184,10 +3193,7 @@ void QCC_PR_EmitClassFromFunction(QCC_def_t *scope, char *tname) QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_DONE], NULL, NULL, NULL)); - pr_scope = NULL; - memset(basictypefield, 0, sizeof(basictypefield)); - QCC_PR_EmitFieldsForMembers(basetype); - pr_scope = scope; + QCC_WriteAsmFunction(scope, df->first_statement, df->parm_start); pr.localvars = NULL; @@ -6568,6 +6574,13 @@ void QCC_PR_EmitArrayGetFunction(QCC_def_t *scope, char *arrayname) QCC_dstatement_t *st; QCC_def_t *eq; + QCC_def_t *fasttrackpossible; + + if (flag_fasttrackarrays) + fasttrackpossible = QCC_PR_GetDef(NULL, "__ext__fasttrackarrays", NULL, true, 1); + else + fasttrackpossible = NULL; + def = QCC_PR_GetDef(NULL, arrayname, NULL, false, 0); if (def->arraysize >= 15 && def->type->size == 1) @@ -6592,6 +6605,22 @@ void QCC_PR_EmitArrayGetFunction(QCC_def_t *scope, char *arrayname) G_FUNCTION(scope->ofs) = df - functions; + if (fasttrackpossible) + { + QCC_PR_Statement(pr_opcodes+OP_IFNOT, fasttrackpossible, NULL, &st); + //fetch_gbl takes: (float size, variant array[]), float index, variant pos + //note that the array size is coded into the globals, one index before the array. + def->ofs--; + if (def->type->size >= 3) + QCC_PR_Statement3(&pr_opcodes[OP_FETCH_GBL_V], def, index, &def_ret); + else + QCC_PR_Statement3(&pr_opcodes[OP_FETCH_GBL_F], def, index, &def_ret); + def->ofs++; + + //finish the jump + st->b = &statements[numstatements] - st; + } + if (vectortrick) { QCC_def_t *div3, *intdiv3, *ret; @@ -6704,6 +6733,13 @@ void QCC_PR_EmitArraySetFunction(QCC_def_t *scope, char *arrayname) QCC_dfunction_t *df; QCC_def_t *def, *index, *value; + QCC_def_t *fasttrackpossible; + + if (flag_fasttrackarrays) + fasttrackpossible = QCC_PR_GetDef(NULL, "__ext__fasttrackarrays", NULL, true, 1); + else + fasttrackpossible = NULL; + def = QCC_PR_GetDef(NULL, arrayname, NULL, false, 0); pr_scope = scope; @@ -6724,6 +6760,27 @@ void QCC_PR_EmitArraySetFunction(QCC_def_t *scope, char *arrayname) G_FUNCTION(scope->ofs) = df - functions; + if (fasttrackpossible) + { + QCC_dstatement_t *st; + + QCC_PR_Statement(pr_opcodes+OP_IFNOT, fasttrackpossible, NULL, &st); + //note that the array size is coded into the globals, one index before the array. + + QCC_PR_Statement3(&pr_opcodes[OP_CONV_FTOI], index, NULL, index); //address stuff is integer based, but standard qc (which this accelerates in supported engines) only supports floats + QCC_PR_SimpleStatement (OP_BOUNDCHECK, index->ofs, 0, ((int*)qcc_pr_globals)[def->ofs-1]);//annoy the programmer. :p + if (def->type->size != 1)//shift it upwards for larger types + QCC_PR_Statement3(&pr_opcodes[OP_MUL_I], index, QCC_MakeIntDef(def->type->size), index); + QCC_PR_Statement3(&pr_opcodes[OP_GLOBALADDRESS], def, index, index); //comes with built in add + if (def->type->size >= 3) + QCC_PR_Statement3(&pr_opcodes[OP_STOREP_V], value, index, NULL); //*b = a + else + QCC_PR_Statement3(&pr_opcodes[OP_STOREP_F], value, index, NULL); + + //finish the jump + st->b = &statements[numstatements] - st; + } + QCC_PR_Statement3(pr_opcodes+OP_BITAND, index, index, index); QCC_PR_ArraySetRecurseDivide(def, index, value, 0, def->arraysize); diff --git a/engine/qclib/qcc_pr_lex.c b/engine/qclib/qcc_pr_lex.c index 103fd98a..95f3b3ae 100644 --- a/engine/qclib/qcc_pr_lex.c +++ b/engine/qclib/qcc_pr_lex.c @@ -555,7 +555,6 @@ pbool QCC_PR_Precompiler(void) } else if (!strncmp(directive, "include", 7)) { - int rellen; char sm; pr_file_p=directive+7; diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index 2f3a4a70..57bc92c6 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -221,6 +221,7 @@ compiler_flag_t compiler_flag[] = { {&flag_laxcasts, FLAG_MIDCOMPILE,"lax", "Lax type checks", "Disables many errors (generating warnings instead) when function calls or operations refer to two normally incompatable types. This is required for reacc support, and can also allow certain (evil) mods to compile that were originally written for frikqcc."}, //Allow lax casting. This'll produce loadsa warnings of course. But allows compilation of certain dodgy code. {&flag_hashonly, FLAG_MIDCOMPILE,"hashonly", "Hash-only constants", "Allows use of only #constant for precompiler constants, allows certain preqcc using mods to compile"}, {&opt_logicops, FLAG_MIDCOMPILE,"lo", "Logic ops", "This changes the behaviour of your code. It generates additional if operations to early-out in if statements. With this flag, the line if (0 && somefunction()) will never call the function. It can thus be considered an optimisation. However, due to the change of behaviour, it is not considered so by fteqcc. Note that due to inprecisions with floats, this flag can cause runaway loop errors within the player walk and run functions. This code is advised:\nplayer_stand1:\n if (self.velocity_x || self.velocity_y)\nplayer_run\n if (!(self.velocity_x || self.velocity_y))"}, + {&flag_fasttrackarrays, FLAG_MIDCOMPILE,"fastarrays", "fast arrays where possible", "Generates extra instructions inside array handling functions to detect engine and use extension opcodes in supporting engines.\nAdds a global which is set by the engine if the engine supports the extra opcodes. Note that this applies to al lor none."}, //correction for if(string) no-ifstring to get the standard behaviour. {NULL} };