]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - prvm_exec.c
Merge PR 'Use the text from modinfo.txt as the mod menu entry'
[xonotic/darkplaces.git] / prvm_exec.c
index 823c2d38144618172e7ecb4619dde488c99df534..d29aa7f559f7c68d44c382d53ce8eedbab0672c4 100644 (file)
@@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "quakedef.h"
 #include "progsvm.h"
 
-const char *prvm_opnames[] =
+static const char *prvm_opnames[] =
 {
 "^5DONE",
 
@@ -107,7 +107,186 @@ const char *prvm_opnames[] =
 "^2OR",
 
 "BITAND",
-"BITOR"
+"BITOR",
+
+
+
+
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+
+"STORE_I",
+
+NULL,
+NULL,
+
+"ADD_I",
+"ADD_FI",
+"ADD_IF",
+
+"SUB_I",
+"SUB_FI",
+"SUB_IF",
+"CONV_IF",
+"CONV_FI",
+
+NULL,
+NULL,
+
+"LOAD_I",
+"STOREP_I",
+
+NULL,
+NULL,
+
+"BITAND_I",
+"BITOR_I",
+
+"MUL_I",
+"DIV_I",
+"EQ_I",
+"NE_I",
+
+NULL,
+NULL,
+
+"NOT_I",
+
+"DIV_VF",
+
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+
+"STORE_P",
+
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+NULL,
+
+"LE_I",
+"GE_I",
+"LT_I",
+"GT_I",
+
+"LE_IF",
+"GE_IF",
+"LT_IF",
+"GT_IF",
+
+"LE_FI",
+"GE_FI",
+"LT_FI",
+"GT_FI",
+
+"EQ_IF",
+"EQ_FI",
+
+NULL,
+NULL,
+NULL,
+NULL,
+
+"MUL_IF",
+"MUL_FI",
+"MUL_VI",
+
+NULL,
+
+"DIV_IF",
+"DIV_FI",
+"BITAND_IF",
+"BITOR_IF",
+"BITAND_FI",
+"BITOR_FI",
+"AND_I",
+"OR_I",
+"AND_IF",
+"OR_IF",
+"AND_FI",
+"OR_FI",
+"NE_IF",
+"NE_FI",
+
+"GSTOREP_I",
+"GSTOREP_F",
+"GSTOREP_ENT",
+"GSTOREP_FLD",
+"GSTOREP_S",
+"GSTOREP_FNC",
+"GSTOREP_V",
+"GADDRESS",
+"GLOAD_I",
+"GLOAD_F",
+"GLOAD_FLD",
+"GLOAD_ENT",
+"GLOAD_S",
+"GLOAD_FNC",
+"BOUNDCHECK",
+NULL,
+NULL,
+NULL,
+NULL,
+"GLOAD_V",
 };
 
 
@@ -119,6 +298,7 @@ const char *prvm_opnames[] =
 PRVM_PrintStatement
 =================
 */
+extern cvar_t prvm_coverage;
 extern cvar_t prvm_statementprofiling;
 extern cvar_t prvm_timeprofiling;
 static void PRVM_PrintStatement(prvm_prog_t *prog, mstatement_t *s)
@@ -126,24 +306,32 @@ static void PRVM_PrintStatement(prvm_prog_t *prog, mstatement_t *s)
        size_t i;
        int opnum = (int)(s - prog->statements);
        char valuebuf[MAX_INPUTLINE];
+       const char *opname;
 
        Con_Printf("s%i: ", opnum);
        if( prog->statement_linenums )
-               Con_Printf( "%s:%i: ", PRVM_GetString( prog, prog->xfunction->s_file ), prog->statement_linenums[ opnum ] );
+       {
+               if ( prog->statement_columnnums )
+                       Con_Printf( "%s:%i:%i: ", PRVM_GetString( prog, prog->xfunction->s_file ), prog->statement_linenums[ opnum ], prog->statement_columnnums[ opnum ] );
+               else
+                       Con_Printf( "%s:%i: ", PRVM_GetString( prog, prog->xfunction->s_file ), prog->statement_linenums[ opnum ] );
+       }
 
        if (prvm_statementprofiling.integer)
                Con_Printf("%7.0f ", prog->statement_profile[s - prog->statements]);
 
-       if ( (unsigned)s->op < sizeof(prvm_opnames)/sizeof(prvm_opnames[0]))
-       {
-               Con_Printf("%s ",  prvm_opnames[s->op]);
-               i = strlen(prvm_opnames[s->op]);
-               // don't count a preceding color tag when padding the name
-               if (prvm_opnames[s->op][0] == STRING_COLOR_TAG)
-                       i -= 2;
-               for ( ; i<10 ; i++)
-                       Con_Print(" ");
-       }
+       if ( (unsigned)s->op < sizeof(prvm_opnames)/sizeof(prvm_opnames[0]) && prvm_opnames[s->op])
+               opname = prvm_opnames[s->op];
+       else
+               opname = valuebuf, dpsnprintf(valuebuf, sizeof(valuebuf), "OPCODE_%u", (unsigned)s->op);
+       Con_Printf("%s ",  opname);
+       i = strlen(opname);
+       // don't count a preceding color tag when padding the name
+       if (opname[0] == STRING_COLOR_TAG)
+               i -= 2;
+       for ( ; i<10 ; i++)
+               Con_Print(" ");
+
        if (s->operand[0] >= 0) Con_Printf(  "%s", PRVM_GlobalString(prog, s->operand[0], valuebuf, sizeof(valuebuf)));
        if (s->operand[1] >= 0) Con_Printf(", %s", PRVM_GlobalString(prog, s->operand[1], valuebuf, sizeof(valuebuf)));
        if (s->operand[2] >= 0) Con_Printf(", %s", PRVM_GlobalString(prog, s->operand[2], valuebuf, sizeof(valuebuf)));
@@ -180,8 +368,11 @@ void PRVM_PrintFunctionStatements (prvm_prog_t *prog, const char *name)
        for (i = firststatement;i < endstatement;i++)
        {
                PRVM_PrintStatement(prog, prog->statements + i);
-               prog->statement_profile[i] = 0;
+               if (!(prvm_coverage.integer & 4))
+                       prog->statement_profile[i] = 0;
        }
+       if (prvm_coverage.integer & 4)
+               Con_Printf("Collecting statement coverage, not flushing statement profile.\n");
 }
 
 /*
@@ -190,19 +381,19 @@ PRVM_PrintFunction_f
 
 ============
 */
-void PRVM_PrintFunction_f (void)
+void PRVM_PrintFunction_f(cmd_state_t *cmd)
 {
        prvm_prog_t *prog;
-       if (Cmd_Argc() != 3)
+       if (Cmd_Argc(cmd) != 3)
        {
                Con_Printf("usage: prvm_printfunction <program name> <function name>\n");
                return;
        }
 
-       if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
+       if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
                return;
 
-       PRVM_PrintFunctionStatements(prog, Cmd_Argv(2));
+       PRVM_PrintFunctionStatements(prog, Cmd_Argv(cmd, 2));
 }
 
 /*
@@ -226,7 +417,12 @@ void PRVM_StackTrace (prvm_prog_t *prog)
                else
                {
                        if (prog->statement_linenums)
-                               Con_Printf("%12s:%i : %s : statement %i\n", PRVM_GetString(prog, f->s_file), prog->statement_linenums[prog->stack[i].s], PRVM_GetString(prog, f->s_name), prog->stack[i].s - f->first_statement);
+                       {
+                               if (prog->statement_columnnums)
+                                       Con_Printf("%12s:%i:%i : %s : statement %i\n", PRVM_GetString(prog, f->s_file), prog->statement_linenums[prog->stack[i].s], prog->statement_columnnums[prog->stack[i].s], PRVM_GetString(prog, f->s_name), prog->stack[i].s - f->first_statement);
+                               else
+                                       Con_Printf("%12s:%i : %s : statement %i\n", PRVM_GetString(prog, f->s_file), prog->statement_linenums[prog->stack[i].s], PRVM_GetString(prog, f->s_name), prog->stack[i].s - f->first_statement);
+                       }
                        else
                                Con_Printf("%12s : %s : statement %i\n", PRVM_GetString(prog, f->s_file), PRVM_GetString(prog, f->s_name), prog->stack[i].s - f->first_statement);
                }
@@ -235,17 +431,19 @@ void PRVM_StackTrace (prvm_prog_t *prog)
 
 void PRVM_ShortStackTrace(prvm_prog_t *prog, char *buf, size_t bufsize)
 {
-       mfunction_t     *f;
-       int                     i;
+       mfunction_t *f;
+       int i;
        char vabuf[1024];
+       char *p;
 
        if(prog)
        {
-               dpsnprintf(buf, bufsize, "(%s) ", prog->name);
+               i = dpsnprintf(buf, bufsize, "(%s) ", prog->name);
+               p = buf + max(0, i);
        }
        else
        {
-               strlcpy(buf, "<NO PROG>", bufsize);
+               dp_strlcpy(buf, "<NO PROG>", bufsize);
                return;
        }
 
@@ -254,13 +452,14 @@ void PRVM_ShortStackTrace(prvm_prog_t *prog, char *buf, size_t bufsize)
        for (i = prog->depth;i > 0;i--)
        {
                f = prog->stack[i].f;
-
-               if(strlcat(buf,
+               p = dp_stpecpy(
+                       p,
+                       buf + bufsize,
                        f
                                ? va(vabuf, sizeof(vabuf), "%s:%s(%i) ", PRVM_GetString(prog, f->s_file), PRVM_GetString(prog, f->s_name), prog->stack[i].s - f->first_statement)
-                               : "<NULL> ",
-                       bufsize
-               ) >= bufsize)
+                               : "<NULL> "
+                       );
+               if (p == buf + bufsize)
                        break;
        }
 }
@@ -419,16 +618,16 @@ PRVM_CallProfile_f
 
 ============
 */
-void PRVM_CallProfile_f (void)
+void PRVM_CallProfile_f(cmd_state_t *cmd)
 {
        prvm_prog_t *prog;
-       if (Cmd_Argc() != 2)
+       if (Cmd_Argc(cmd) != 2)
        {
                Con_Print("prvm_callprofile <program name>\n");
                return;
        }
 
-       if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
+       if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
                return;
 
        PRVM_CallProfile(prog);
@@ -440,41 +639,53 @@ PRVM_Profile_f
 
 ============
 */
-void PRVM_Profile_f (void)
+void PRVM_Profile_f(cmd_state_t *cmd)
 {
        prvm_prog_t *prog;
        int howmany;
 
+       if (prvm_coverage.integer & 1)
+       {
+               Con_Printf("Collecting function coverage, cannot profile - sorry!\n");
+               return;
+       }
+
        howmany = 1<<30;
-       if (Cmd_Argc() == 3)
-               howmany = atoi(Cmd_Argv(2));
-       else if (Cmd_Argc() != 2)
+       if (Cmd_Argc(cmd) == 3)
+               howmany = atoi(Cmd_Argv(cmd, 2));
+       else if (Cmd_Argc(cmd) != 2)
        {
                Con_Print("prvm_profile <program name>\n");
                return;
        }
 
-       if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
+       if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
                return;
 
        PRVM_Profile(prog, howmany, 0, 0);
 }
 
-void PRVM_ChildProfile_f (void)
+void PRVM_ChildProfile_f(cmd_state_t *cmd)
 {
        prvm_prog_t *prog;
        int howmany;
 
+       if (prvm_coverage.integer & 1)
+       {
+               Con_Printf("Collecting function coverage, cannot profile - sorry!\n");
+               return;
+       }
+
        howmany = 1<<30;
-       if (Cmd_Argc() == 3)
-               howmany = atoi(Cmd_Argv(2));
-       else if (Cmd_Argc() != 2)
+       if (Cmd_Argc(cmd) == 3)
+               howmany = atoi(Cmd_Argv(cmd, 2));
+       else if (Cmd_Argc(cmd) != 2)
        {
                Con_Print("prvm_childprofile <program name>\n");
                return;
        }
 
-       if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
+       if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
                return;
 
        PRVM_Profile(prog, howmany, 0, 1);
@@ -504,37 +715,38 @@ void PRVM_PrintState(prvm_prog_t *prog, int stack_index)
 }
 
 extern cvar_t prvm_errordump;
-void PRVM_Crash(prvm_prog_t *prog)
+void PRVM_Crash(void)
 {
+       prvm_prog_t *prog;
        char vabuf[1024];
-       if (prog == NULL)
-               return;
-       if (!prog->loaded)
-               return;
+       int i;
 
-       PRVM_serverfunction(SV_Shutdown) = 0; // don't call SV_Shutdown on crash
+       // determine which program crashed
+       for (i = 0; i < PRVM_PROG_MAX; ++i)
+               if (PRVM_GetProg(i)->loaded && PRVM_GetProg(i)->depth > 0)
+                       break;
+       if (i >= PRVM_PROG_MAX)
+               return; // none of them crashed
+       prog = PRVM_GetProg(i);
 
-       if( prog->depth > 0 )
-       {
-               Con_Printf("QuakeC crash report for %s:\n", prog->name);
-               PRVM_PrintState(prog, 0);
-       }
+       Con_Printf("QuakeC crash report for %s:\n", prog->name);
+       PRVM_PrintState(prog, 0);
+
+       // don't call graceful shutdown on crash
+       if (prog == SVVM_prog)
+               PRVM_serverfunction(SV_Shutdown) = 0;
+       else if (prog == CLVM_prog)
+               PRVM_clientfunction(CSQC_Shutdown) = 0;
 
-       if(prvm_errordump.integer)
+       if(prvm_errordump.integer && (prog == SVVM_prog || prog == CLVM_prog))
        {
                // make a savegame
-               Host_Savegame_to(prog, va(vabuf, sizeof(vabuf), "crash-%s.dmp", prog->name));
+               SV_Savegame_to(prog, va(vabuf, sizeof(vabuf), "crash-%s.dmp", prog->name));
        }
 
        // dump the stack so host_error can shutdown functions
        prog->depth = 0;
        prog->localstack_used = 0;
-
-       // delete all tempstrings (FIXME: is this safe in VM->engine->VM recursion?)
-       prog->tempstringsbuf.cursize = 0;
-
-       // reset the prog pointer
-       prog = NULL;
 }
 
 /*
@@ -654,14 +866,57 @@ void PRVM_Init_Exec(prvm_prog_t *prog)
        // nothing here yet
 }
 
-#define OPA ((prvm_eval_t *)&prog->globals.fp[st->operand[0]])
-#define OPB ((prvm_eval_t *)&prog->globals.fp[st->operand[1]])
-#define OPC ((prvm_eval_t *)&prog->globals.fp[st->operand[2]])
+/*
+==================
+Coverage
+==================
+*/
+// Note: in these two calls, prog->xfunction is assumed to be sane.
+static const char *PRVM_WhereAmI(char *buf, size_t bufsize, prvm_prog_t *prog, mfunction_t *func, int statement)
+{
+       if (prog->statement_linenums)
+       {
+               if (prog->statement_columnnums)
+                       return va(buf, bufsize, "%s:%i:%i(%s, %i)", PRVM_GetString(prog, func->s_file), prog->statement_linenums[statement], prog->statement_columnnums[statement], PRVM_GetString(prog, func->s_name), statement - func->first_statement);
+               else
+                       return va(buf, bufsize, "%s:%i(%s, %i)", PRVM_GetString(prog, func->s_file), prog->statement_linenums[statement], PRVM_GetString(prog, func->s_name), statement - func->first_statement);
+       }
+       else
+               return va(buf, bufsize, "%s(%s, %i)", PRVM_GetString(prog, func->s_file), PRVM_GetString(prog, func->s_name), statement - func->first_statement);
+}
+static void PRVM_FunctionCoverageEvent(prvm_prog_t *prog, mfunction_t *func)
+{
+       ++prog->functions_covered;
+       Con_Printf("prvm_coverage: %s just called %s for the first time. Coverage: %.2f%%.\n", prog->name, PRVM_GetString(prog, func->s_name), prog->functions_covered * 100.0 / prog->numfunctions);
+}
+void PRVM_ExplicitCoverageEvent(prvm_prog_t *prog, mfunction_t *func, int statement)
+{
+       char vabuf[128];
+       ++prog->explicit_covered;
+       Con_Printf("prvm_coverage: %s just executed a coverage() statement at %s for the first time. Coverage: %.2f%%.\n", prog->name, PRVM_WhereAmI(vabuf, sizeof(vabuf), prog, func, statement), prog->explicit_covered * 100.0 / prog->numexplicitcoveragestatements);
+}
+static void PRVM_StatementCoverageEvent(prvm_prog_t *prog, mfunction_t *func, int statement)
+{
+       char vabuf[128];
+       ++prog->statements_covered;
+       Con_Printf("prvm_coverage: %s just executed a statement at %s for the first time. Coverage: %.2f%%.\n", prog->name, PRVM_WhereAmI(vabuf, sizeof(vabuf), prog, func, statement), prog->statements_covered * 100.0 / prog->numstatements);
+}
+
+#if defined (__GNUC__) || (__clang__) || (__TINYC__)
+#  ifndef CONFIG_PEDANTIC
+#  define HAVE_COMPUTED_GOTOS 1
+#  endif
+#endif
+
+#define OPA ((prvm_eval_t *)&globals[st->operand[0]])
+#define OPB ((prvm_eval_t *)&globals[st->operand[1]])
+#define OPC ((prvm_eval_t *)&globals[st->operand[2]])
 extern cvar_t prvm_traceqc;
 extern cvar_t prvm_statementprofiling;
-extern qboolean prvm_runawaycheck;
+extern qbool prvm_runawaycheck;
 
 #ifdef PROFILING
+#ifdef CONFIG_MENU
 /*
 ====================
 MVM_ExecuteProgram
@@ -670,7 +925,7 @@ MVM_ExecuteProgram
 void MVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
 {
        mstatement_t    *st, *startst;
-       mfunction_t     *f, *newf;
+       mfunction_t             *func, *enterfunc;
        prvm_edict_t    *ed;
        prvm_eval_t     *ptr;
        int             jumpcount, cachedpr_trace, exitdepth;
@@ -679,7 +934,7 @@ void MVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessag
        double tm, starttm;
        prvm_vec_t tempfloat;
        // these may become out of date when a builtin is called, and are updated accordingly
-       prvm_vec_t *cached_edictsfields = prog->edictsfields;
+       prvm_vec_t *cached_edictsfields = prog->edictsfields.fp;
        unsigned int cached_entityfields = prog->entityfields;
        unsigned int cached_entityfields_3 = prog->entityfields - 3;
        unsigned int cached_entityfieldsarea = prog->entityfieldsarea;
@@ -689,9 +944,11 @@ void MVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessag
        unsigned int cached_max_edicts = prog->max_edicts;
        // these do not change
        mstatement_t *cached_statements = prog->statements;
-       qboolean cached_allowworldwrites = prog->allowworldwrites;
+       qbool cached_allowworldwrites = prog->allowworldwrites;
        unsigned int cached_flag = prog->flag;
 
+       prvm_vec_t *globals = prog->globals.fp;
+
        calltime = Sys_DirtyTime();
 
        if (!fnum || fnum >= (unsigned int)prog->numfunctions)
@@ -701,7 +958,7 @@ void MVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessag
                prog->error_cmd("MVM_ExecuteProgram: %s", errormessage);
        }
 
-       f = &prog->functions[fnum];
+       func = &prog->functions[fnum];
 
        // after executing this function, delete all tempstrings it created
        restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
@@ -712,7 +969,7 @@ void MVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessag
        exitdepth = prog->depth;
 
 // make a stack frame
-       st = &prog->statements[PRVM_EnterFunction(prog, f)];
+       st = &prog->statements[PRVM_EnterFunction(prog, func)];
        // save the starting statement pointer for profiling
        // (when the function exits or jumps, the (st - startst) integer value is
        // added to the function's profile counter)
@@ -721,11 +978,12 @@ void MVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessag
        // instead of counting instructions, we count jumps
        jumpcount = 0;
        // add one to the callcount of this function because otherwise engine-called functions aren't counted
-       prog->xfunction->callcount++;
+       if (prog->xfunction->callcount++ == 0 && (prvm_coverage.integer & 1))
+               PRVM_FunctionCoverageEvent(prog, prog->xfunction);
 
 chooseexecprogram:
        cachedpr_trace = prog->trace;
-       if (prvm_statementprofiling.integer || prog->trace || prog->watch_global >= 0 || prog->watch_edict >= 0 || prog->break_statement >= 0)
+       if (prog->trace || prog->watch_global_type != ev_void || prog->watch_field_type != ev_void || prog->break_statement >= 0)
        {
 #define PRVMSLOWINTERPRETER 1
                if (prvm_timeprofiling.integer)
@@ -761,11 +1019,12 @@ cleanup:
        prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
 
        tm = Sys_DirtyTime() - calltime;if (tm < 0 || tm >= 1800) tm = 0;
-       f->totaltime += tm;
+       func->totaltime += tm;
 
        if (prog == SVVM_prog)
                SV_FlushBroadcastMessages();
 }
+#endif // CONFIG_MENU
 
 /*
 ====================
@@ -775,7 +1034,7 @@ CLVM_ExecuteProgram
 void CLVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
 {
        mstatement_t    *st, *startst;
-       mfunction_t     *f, *newf;
+       mfunction_t             *func, *enterfunc;
        prvm_edict_t    *ed;
        prvm_eval_t     *ptr;
        int             jumpcount, cachedpr_trace, exitdepth;
@@ -784,7 +1043,7 @@ void CLVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessa
        double tm, starttm;
        prvm_vec_t tempfloat;
        // these may become out of date when a builtin is called, and are updated accordingly
-       prvm_vec_t *cached_edictsfields = prog->edictsfields;
+       prvm_vec_t *cached_edictsfields = prog->edictsfields.fp;
        unsigned int cached_entityfields = prog->entityfields;
        unsigned int cached_entityfields_3 = prog->entityfields - 3;
        unsigned int cached_entityfieldsarea = prog->entityfieldsarea;
@@ -794,9 +1053,11 @@ void CLVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessa
        unsigned int cached_max_edicts = prog->max_edicts;
        // these do not change
        mstatement_t *cached_statements = prog->statements;
-       qboolean cached_allowworldwrites = prog->allowworldwrites;
+       qbool cached_allowworldwrites = prog->allowworldwrites;
        unsigned int cached_flag = prog->flag;
 
+       prvm_vec_t *globals = prog->globals.fp;
+
        calltime = Sys_DirtyTime();
 
        if (!fnum || fnum >= (unsigned int)prog->numfunctions)
@@ -806,7 +1067,7 @@ void CLVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessa
                prog->error_cmd("CLVM_ExecuteProgram: %s", errormessage);
        }
 
-       f = &prog->functions[fnum];
+       func = &prog->functions[fnum];
 
        // after executing this function, delete all tempstrings it created
        restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
@@ -817,7 +1078,7 @@ void CLVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessa
        exitdepth = prog->depth;
 
 // make a stack frame
-       st = &prog->statements[PRVM_EnterFunction(prog, f)];
+       st = &prog->statements[PRVM_EnterFunction(prog, func)];
        // save the starting statement pointer for profiling
        // (when the function exits or jumps, the (st - startst) integer value is
        // added to the function's profile counter)
@@ -826,11 +1087,12 @@ void CLVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessa
        // instead of counting instructions, we count jumps
        jumpcount = 0;
        // add one to the callcount of this function because otherwise engine-called functions aren't counted
-       prog->xfunction->callcount++;
+       if (prog->xfunction->callcount++ == 0 && (prvm_coverage.integer & 1))
+               PRVM_FunctionCoverageEvent(prog, prog->xfunction);
 
 chooseexecprogram:
        cachedpr_trace = prog->trace;
-       if (prvm_statementprofiling.integer || prog->trace || prog->watch_global >= 0 || prog->watch_edict >= 0 || prog->break_statement >= 0)
+       if (prog->trace || prog->watch_global_type != ev_void || prog->watch_field_type != ev_void || prog->break_statement >= 0)
        {
 #define PRVMSLOWINTERPRETER 1
                if (prvm_timeprofiling.integer)
@@ -866,12 +1128,12 @@ cleanup:
        prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
 
        tm = Sys_DirtyTime() - calltime;if (tm < 0 || tm >= 1800) tm = 0;
-       f->totaltime += tm;
+       func->totaltime += tm;
 
        if (prog == SVVM_prog)
                SV_FlushBroadcastMessages();
 }
-#endif
+#endif // PROFILING
 
 /*
 ====================
@@ -885,7 +1147,7 @@ void PRVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessa
 #endif
 {
        mstatement_t    *st, *startst;
-       mfunction_t     *f, *newf;
+       mfunction_t             *func, *enterfunc;
        prvm_edict_t    *ed;
        prvm_eval_t     *ptr;
        int             jumpcount, cachedpr_trace, exitdepth;
@@ -894,7 +1156,7 @@ void PRVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessa
        double tm, starttm;
        prvm_vec_t tempfloat;
        // these may become out of date when a builtin is called, and are updated accordingly
-       prvm_vec_t *cached_edictsfields = prog->edictsfields;
+       prvm_vec_t *cached_edictsfields = prog->edictsfields.fp;
        unsigned int cached_entityfields = prog->entityfields;
        unsigned int cached_entityfields_3 = prog->entityfields - 3;
        unsigned int cached_entityfieldsarea = prog->entityfieldsarea;
@@ -904,9 +1166,11 @@ void PRVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessa
        unsigned int cached_max_edicts = prog->max_edicts;
        // these do not change
        mstatement_t *cached_statements = prog->statements;
-       qboolean cached_allowworldwrites = prog->allowworldwrites;
+       qbool cached_allowworldwrites = prog->allowworldwrites;
        unsigned int cached_flag = prog->flag;
 
+       prvm_vec_t *globals = prog->globals.fp;
+
        calltime = Sys_DirtyTime();
 
        if (!fnum || fnum >= (unsigned int)prog->numfunctions)
@@ -916,7 +1180,7 @@ void PRVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessa
                prog->error_cmd("SVVM_ExecuteProgram: %s", errormessage);
        }
 
-       f = &prog->functions[fnum];
+       func = &prog->functions[fnum];
 
        // after executing this function, delete all tempstrings it created
        restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
@@ -927,7 +1191,7 @@ void PRVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessa
        exitdepth = prog->depth;
 
 // make a stack frame
-       st = &prog->statements[PRVM_EnterFunction(prog, f)];
+       st = &prog->statements[PRVM_EnterFunction(prog, func)];
        // save the starting statement pointer for profiling
        // (when the function exits or jumps, the (st - startst) integer value is
        // added to the function's profile counter)
@@ -936,11 +1200,12 @@ void PRVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessa
        // instead of counting instructions, we count jumps
        jumpcount = 0;
        // add one to the callcount of this function because otherwise engine-called functions aren't counted
-       prog->xfunction->callcount++;
+       if (prog->xfunction->callcount++ == 0 && (prvm_coverage.integer & 1))
+               PRVM_FunctionCoverageEvent(prog, prog->xfunction);
 
 chooseexecprogram:
        cachedpr_trace = prog->trace;
-       if (prvm_statementprofiling.integer || prog->trace || prog->watch_global >= 0 || prog->watch_edict >= 0 || prog->break_statement >= 0)
+       if (prog->trace || prog->watch_global_type != ev_void || prog->watch_field_type != ev_void || prog->break_statement >= 0)
        {
 #define PRVMSLOWINTERPRETER 1
                if (prvm_timeprofiling.integer)
@@ -976,7 +1241,7 @@ cleanup:
        prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
 
        tm = Sys_DirtyTime() - calltime;if (tm < 0 || tm >= 1800) tm = 0;
-       f->totaltime += tm;
+       func->totaltime += tm;
 
        if (prog == SVVM_prog)
                SV_FlushBroadcastMessages();