]> git.xonotic.org Git - xonotic/darkplaces.git/blob - prvm_exec.c
major overhaul for thread-safety - many global variables and static
[xonotic/darkplaces.git] / prvm_exec.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22 #include "progsvm.h"
23
24 const char *prvm_opnames[] =
25 {
26 "^5DONE",
27
28 "MUL_F",
29 "MUL_V",
30 "MUL_FV",
31 "MUL_VF",
32
33 "DIV",
34
35 "ADD_F",
36 "ADD_V",
37
38 "SUB_F",
39 "SUB_V",
40
41 "^2EQ_F",
42 "^2EQ_V",
43 "^2EQ_S",
44 "^2EQ_E",
45 "^2EQ_FNC",
46
47 "^2NE_F",
48 "^2NE_V",
49 "^2NE_S",
50 "^2NE_E",
51 "^2NE_FNC",
52
53 "^2LE",
54 "^2GE",
55 "^2LT",
56 "^2GT",
57
58 "^6FIELD_F",
59 "^6FIELD_V",
60 "^6FIELD_S",
61 "^6FIELD_ENT",
62 "^6FIELD_FLD",
63 "^6FIELD_FNC",
64
65 "^1ADDRESS",
66
67 "STORE_F",
68 "STORE_V",
69 "STORE_S",
70 "STORE_ENT",
71 "STORE_FLD",
72 "STORE_FNC",
73
74 "^1STOREP_F",
75 "^1STOREP_V",
76 "^1STOREP_S",
77 "^1STOREP_ENT",
78 "^1STOREP_FLD",
79 "^1STOREP_FNC",
80
81 "^5RETURN",
82
83 "^2NOT_F",
84 "^2NOT_V",
85 "^2NOT_S",
86 "^2NOT_ENT",
87 "^2NOT_FNC",
88
89 "^5IF",
90 "^5IFNOT",
91
92 "^3CALL0",
93 "^3CALL1",
94 "^3CALL2",
95 "^3CALL3",
96 "^3CALL4",
97 "^3CALL5",
98 "^3CALL6",
99 "^3CALL7",
100 "^3CALL8",
101
102 "^1STATE",
103
104 "^5GOTO",
105
106 "^2AND",
107 "^2OR",
108
109 "BITAND",
110 "BITOR"
111 };
112
113
114
115 //=============================================================================
116
117 /*
118 =================
119 PRVM_PrintStatement
120 =================
121 */
122 extern cvar_t prvm_statementprofiling;
123 extern cvar_t prvm_timeprofiling;
124 static void PRVM_PrintStatement(prvm_prog_t *prog, mstatement_t *s)
125 {
126         size_t i;
127         int opnum = (int)(s - prog->statements);
128         char valuebuf[MAX_INPUTLINE];
129
130         Con_Printf("s%i: ", opnum);
131         if( prog->statement_linenums )
132                 Con_Printf( "%s:%i: ", PRVM_GetString( prog, prog->xfunction->s_file ), prog->statement_linenums[ opnum ] );
133
134         if (prvm_statementprofiling.integer)
135                 Con_Printf("%7.0f ", prog->statement_profile[s - prog->statements]);
136
137         if ( (unsigned)s->op < sizeof(prvm_opnames)/sizeof(prvm_opnames[0]))
138         {
139                 Con_Printf("%s ",  prvm_opnames[s->op]);
140                 i = strlen(prvm_opnames[s->op]);
141                 // don't count a preceding color tag when padding the name
142                 if (prvm_opnames[s->op][0] == STRING_COLOR_TAG)
143                         i -= 2;
144                 for ( ; i<10 ; i++)
145                         Con_Print(" ");
146         }
147         if (s->operand[0] >= 0) Con_Printf(  "%s", PRVM_GlobalString(prog, s->operand[0], valuebuf, sizeof(valuebuf)));
148         if (s->operand[1] >= 0) Con_Printf(", %s", PRVM_GlobalString(prog, s->operand[1], valuebuf, sizeof(valuebuf)));
149         if (s->operand[2] >= 0) Con_Printf(", %s", PRVM_GlobalString(prog, s->operand[2], valuebuf, sizeof(valuebuf)));
150         if (s->jumpabsolute >= 0) Con_Printf(", statement %i", s->jumpabsolute);
151         Con_Print("\n");
152 }
153
154 void PRVM_PrintFunctionStatements (prvm_prog_t *prog, const char *name)
155 {
156         int i, firststatement, endstatement;
157         mfunction_t *func;
158         func = PRVM_ED_FindFunction (prog, name);
159         if (!func)
160         {
161                 Con_Printf("%s progs: no function named %s\n", prog->name, name);
162                 return;
163         }
164         firststatement = func->first_statement;
165         if (firststatement < 0)
166         {
167                 Con_Printf("%s progs: function %s is builtin #%i\n", prog->name, name, -firststatement);
168                 return;
169         }
170
171         // find the end statement
172         endstatement = prog->numstatements;
173         for (i = 0;i < prog->numfunctions;i++)
174                 if (endstatement > prog->functions[i].first_statement && firststatement < prog->functions[i].first_statement)
175                         endstatement = prog->functions[i].first_statement;
176
177         // now print the range of statements
178         Con_Printf("%s progs: disassembly of function %s (statements %i-%i, locals %i-%i):\n", prog->name, name, firststatement, endstatement, func->parm_start, func->parm_start + func->locals - 1);
179         for (i = firststatement;i < endstatement;i++)
180         {
181                 PRVM_PrintStatement(prog, prog->statements + i);
182                 prog->statement_profile[i] = 0;
183         }
184 }
185
186 /*
187 ============
188 PRVM_PrintFunction_f
189
190 ============
191 */
192 void PRVM_PrintFunction_f (void)
193 {
194         prvm_prog_t *prog;
195         if (Cmd_Argc() != 3)
196         {
197                 Con_Printf("usage: prvm_printfunction <program name> <function name>\n");
198                 return;
199         }
200
201         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
202                 return;
203
204         PRVM_PrintFunctionStatements(prog, Cmd_Argv(2));
205 }
206
207 /*
208 ============
209 PRVM_StackTrace
210 ============
211 */
212 void PRVM_StackTrace (prvm_prog_t *prog)
213 {
214         mfunction_t     *f;
215         int                     i;
216
217         prog->stack[prog->depth].s = prog->xstatement;
218         prog->stack[prog->depth].f = prog->xfunction;
219         for (i = prog->depth;i > 0;i--)
220         {
221                 f = prog->stack[i].f;
222
223                 if (!f)
224                         Con_Print("<NULL FUNCTION>\n");
225                 else
226                         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);
227         }
228 }
229
230 void PRVM_ShortStackTrace(prvm_prog_t *prog, char *buf, size_t bufsize)
231 {
232         mfunction_t     *f;
233         int                     i;
234         char vabuf[1024];
235
236         if(prog)
237         {
238                 dpsnprintf(buf, bufsize, "(%s) ", prog->name);
239         }
240         else
241         {
242                 strlcpy(buf, "<NO PROG>", bufsize);
243                 return;
244         }
245
246         prog->stack[prog->depth].s = prog->xstatement;
247         prog->stack[prog->depth].f = prog->xfunction;
248         for (i = prog->depth;i > 0;i--)
249         {
250                 f = prog->stack[i].f;
251
252                 if(strlcat(buf,
253                         f
254                                 ? 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)
255                                 : "<NULL> ",
256                         bufsize
257                 ) >= bufsize)
258                         break;
259         }
260 }
261
262
263 static void PRVM_CallProfile (prvm_prog_t *prog)
264 {
265         mfunction_t *f, *best;
266         int i;
267         double max;
268         double sum;
269         double newprofiletime;
270
271         Con_Printf( "%s Call Profile:\n", prog->name );
272
273         sum = 0;
274         do
275         {
276                 max = 0;
277                 best = NULL;
278                 for (i=0 ; i<prog->numfunctions ; i++)
279                 {
280                         f = &prog->functions[i];
281                         if (max < f->totaltime)
282                         {
283                                 max = f->totaltime;
284                                 best = f;
285                         }
286                 }
287                 if (best)
288                 {
289                         sum += best->totaltime;
290                         Con_Printf("%9.4f %s\n", best->totaltime, PRVM_GetString(prog, best->s_name));
291                         best->totaltime = 0;
292                 }
293         } while (best);
294
295         newprofiletime = Sys_DirtyTime();
296         Con_Printf("Total time since last profile reset: %9.4f\n", newprofiletime - prog->profiletime);
297         Con_Printf("       - used by QC code of this VM: %9.4f\n", sum);
298
299         prog->profiletime = newprofiletime;
300 }
301
302 void PRVM_Profile (prvm_prog_t *prog, int maxfunctions, double mintime, int sortby)
303 {
304         mfunction_t *f, *best;
305         int i, num;
306         double max;
307
308         if(!prvm_timeprofiling.integer)
309                 mintime *= 10000000; // count each statement as about 0.1µs
310
311         if(prvm_timeprofiling.integer)
312                 Con_Printf( "%s Profile:\n[CallCount]      [Time] [BuiltinTm] [Statement] [BuiltinCt] [TimeTotal] [StmtTotal] [BltnTotal] [self]\n", prog->name );
313                 //                        12345678901 12345678901 12345678901 12345678901 12345678901 12345678901 12345678901 123.45%
314         else
315                 Con_Printf( "%s Profile:\n[CallCount] [Statement] [BuiltinCt] [StmtTotal] [BltnTotal] [self]\n", prog->name );
316                 //                        12345678901 12345678901 12345678901 12345678901 12345678901 123.45%
317
318         num = 0;
319         do
320         {
321                 max = 0;
322                 best = NULL;
323                 for (i=0 ; i<prog->numfunctions ; i++)
324                 {
325                         f = &prog->functions[i];
326                         if(prvm_timeprofiling.integer)
327                         {
328                                 if(sortby)
329                                 {
330                                         if(f->first_statement < 0)
331                                         {
332                                                 if (max < f->tprofile)
333                                                 {
334                                                         max = f->tprofile;
335                                                         best = f;
336                                                 }
337                                         }
338                                         else
339                                         {
340                                                 if (max < f->tprofile_total)
341                                                 {
342                                                         max = f->tprofile_total;
343                                                         best = f;
344                                                 }
345                                         }
346                                 }
347                                 else
348                                 {
349                                         if (max < f->tprofile + f->tbprofile)
350                                         {
351                                                 max = f->tprofile + f->tbprofile;
352                                                 best = f;
353                                         }
354                                 }
355                         }
356                         else
357                         {
358                                 if(sortby)
359                                 {
360                                         if (max < f->profile_total + f->builtinsprofile_total + f->callcount)
361                                         {
362                                                 max = f->profile_total + f->builtinsprofile_total + f->callcount;
363                                                 best = f;
364                                         }
365                                 }
366                                 else
367                                 {
368                                         if (max < f->profile + f->builtinsprofile + f->callcount)
369                                         {
370                                                 max = f->profile + f->builtinsprofile + f->callcount;
371                                                 best = f;
372                                         }
373                                 }
374                         }
375                 }
376                 if (best)
377                 {
378                         if (num < maxfunctions && max > mintime)
379                         {
380                                 if(prvm_timeprofiling.integer)
381                                 {
382                                         if (best->first_statement < 0)
383                                                 Con_Printf("%11.0f %11.6f ------------- builtin ------------- %11.6f ----------- builtin ----------- %s\n", best->callcount, best->tprofile, best->tprofile, PRVM_GetString(prog, best->s_name));
384                                         //                 %11.6f 12345678901 12345678901 12345678901 %11.6f 12345678901 12345678901 123.45%
385                                         else
386                                                 Con_Printf("%11.0f %11.6f %11.6f %11.0f %11.0f %11.6f %11.0f %11.0f %6.2f%% %s\n", best->callcount, best->tprofile, best->tbprofile, best->profile, best->builtinsprofile, best->tprofile_total, best->profile_total, best->builtinsprofile_total, (best->tprofile_total > 0) ? ((best->tprofile) * 100.0 / (best->tprofile_total)) : -99.99, PRVM_GetString(prog, best->s_name));
387                                 }
388                                 else
389                                 {
390                                         if (best->first_statement < 0)
391                                                 Con_Printf("%11.0f ----------------------- builtin ----------------------- %s\n", best->callcount, PRVM_GetString(prog, best->s_name));
392                                         //                 12345678901 12345678901 12345678901 12345678901 123.45%
393                                         else
394                                                 Con_Printf("%11.0f %11.0f %11.0f %11.0f %11.0f %6.2f%% %s\n", best->callcount, best->profile, best->builtinsprofile, best->profile_total, best->builtinsprofile_total, (best->profile + best->builtinsprofile) * 100.0 / (best->profile_total + best->builtinsprofile_total), PRVM_GetString(prog, best->s_name));
395                                 }
396                         }
397                         num++;
398                         best->profile = 0;
399                         best->tprofile = 0;
400                         best->tbprofile = 0;
401                         best->builtinsprofile = 0;
402                         best->profile_total = 0;
403                         best->tprofile_total = 0;
404                         best->builtinsprofile_total = 0;
405                         best->callcount = 0;
406                 }
407         } while (best);
408 }
409
410 /*
411 ============
412 PRVM_CallProfile_f
413
414 ============
415 */
416 void PRVM_CallProfile_f (void)
417 {
418         prvm_prog_t *prog;
419         if (Cmd_Argc() != 2)
420         {
421                 Con_Print("prvm_callprofile <program name>\n");
422                 return;
423         }
424
425         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
426                 return;
427
428         PRVM_CallProfile(prog);
429 }
430
431 /*
432 ============
433 PRVM_Profile_f
434
435 ============
436 */
437 void PRVM_Profile_f (void)
438 {
439         prvm_prog_t *prog;
440         int howmany;
441
442         howmany = 1<<30;
443         if (Cmd_Argc() == 3)
444                 howmany = atoi(Cmd_Argv(2));
445         else if (Cmd_Argc() != 2)
446         {
447                 Con_Print("prvm_profile <program name>\n");
448                 return;
449         }
450
451         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
452                 return;
453
454         PRVM_Profile(prog, howmany, 0, 0);
455 }
456
457 void PRVM_ChildProfile_f (void)
458 {
459         prvm_prog_t *prog;
460         int howmany;
461
462         howmany = 1<<30;
463         if (Cmd_Argc() == 3)
464                 howmany = atoi(Cmd_Argv(2));
465         else if (Cmd_Argc() != 2)
466         {
467                 Con_Print("prvm_childprofile <program name>\n");
468                 return;
469         }
470
471         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
472                 return;
473
474         PRVM_Profile(prog, howmany, 0, 1);
475 }
476
477 void PRVM_PrintState(prvm_prog_t *prog)
478 {
479         int i;
480         if (prog->statestring)
481         {
482                 Con_Printf("Caller-provided information: %s\n", prog->statestring);
483         }
484         if (prog->xfunction)
485         {
486                 for (i = -7; i <= 0;i++)
487                         if (prog->xstatement + i >= prog->xfunction->first_statement)
488                                 PRVM_PrintStatement(prog, prog->statements + prog->xstatement + i);
489         }
490         else
491                 Con_Print("null function executing??\n");
492         PRVM_StackTrace(prog);
493 }
494
495 extern cvar_t prvm_errordump;
496 void PRVM_Crash(prvm_prog_t *prog)
497 {
498         char vabuf[1024];
499         if (prog == NULL)
500                 return;
501         if (!prog->loaded)
502                 return;
503
504         PRVM_serverfunction(SV_Shutdown) = 0; // don't call SV_Shutdown on crash
505
506         if( prog->depth > 0 )
507         {
508                 Con_Printf("QuakeC crash report for %s:\n", prog->name);
509                 PRVM_PrintState(prog);
510         }
511
512         if(prvm_errordump.integer)
513         {
514                 // make a savegame
515                 Host_Savegame_to(prog, va(vabuf, sizeof(vabuf), "crash-%s.dmp", prog->name));
516         }
517
518         // dump the stack so host_error can shutdown functions
519         prog->depth = 0;
520         prog->localstack_used = 0;
521
522         // delete all tempstrings (FIXME: is this safe in VM->engine->VM recursion?)
523         prog->tempstringsbuf.cursize = 0;
524
525         // reset the prog pointer
526         prog = NULL;
527 }
528
529 /*
530 ============================================================================
531 PRVM_ExecuteProgram
532
533 The interpretation main loop
534 ============================================================================
535 */
536
537 /*
538 ====================
539 PRVM_EnterFunction
540
541 Returns the new program statement counter
542 ====================
543 */
544 static int PRVM_EnterFunction (prvm_prog_t *prog, mfunction_t *f)
545 {
546         int             i, j, c, o;
547
548         if (!f)
549                 prog->error_cmd("PRVM_EnterFunction: NULL function in %s", prog->name);
550
551         prog->stack[prog->depth].s = prog->xstatement;
552         prog->stack[prog->depth].f = prog->xfunction;
553         prog->stack[prog->depth].profile_acc = -f->profile;
554         prog->stack[prog->depth].tprofile_acc = -f->tprofile + -f->tbprofile;
555         prog->stack[prog->depth].builtinsprofile_acc = -f->builtinsprofile;
556         prog->depth++;
557         if (prog->depth >=PRVM_MAX_STACK_DEPTH)
558                 prog->error_cmd("stack overflow");
559
560 // save off any locals that the new function steps on
561         c = f->locals;
562         if (prog->localstack_used + c > PRVM_LOCALSTACK_SIZE)
563                 prog->error_cmd("PRVM_ExecuteProgram: locals stack overflow in %s", prog->name);
564
565         for (i=0 ; i < c ; i++)
566                 prog->localstack[prog->localstack_used+i] = ((int *)prog->globals.generic)[f->parm_start + i];
567         prog->localstack_used += c;
568
569 // copy parameters
570         o = f->parm_start;
571         for (i=0 ; i<f->numparms ; i++)
572         {
573                 for (j=0 ; j<f->parm_size[i] ; j++)
574                 {
575                         ((int *)prog->globals.generic)[o] = ((int *)prog->globals.generic)[OFS_PARM0+i*3+j];
576                         o++;
577                 }
578         }
579
580         ++f->recursion;
581         prog->xfunction = f;
582         return f->first_statement - 1;  // offset the s++
583 }
584
585 /*
586 ====================
587 PRVM_LeaveFunction
588 ====================
589 */
590 static int PRVM_LeaveFunction (prvm_prog_t *prog)
591 {
592         int             i, c;
593         mfunction_t *f;
594
595         if (prog->depth <= 0)
596                 prog->error_cmd("prog stack underflow in %s", prog->name);
597
598         if (!prog->xfunction)
599                 prog->error_cmd("PR_LeaveFunction: NULL function in %s", prog->name);
600 // restore locals from the stack
601         c = prog->xfunction->locals;
602         prog->localstack_used -= c;
603         if (prog->localstack_used < 0)
604                 prog->error_cmd("PRVM_ExecuteProgram: locals stack underflow in %s", prog->name);
605
606         for (i=0 ; i < c ; i++)
607                 ((int *)prog->globals.generic)[prog->xfunction->parm_start + i] = prog->localstack[prog->localstack_used+i];
608
609 // up stack
610         prog->depth--;
611         f = prog->xfunction;
612         --f->recursion;
613         prog->xfunction = prog->stack[prog->depth].f;
614         prog->stack[prog->depth].profile_acc += f->profile;
615         prog->stack[prog->depth].tprofile_acc += f->tprofile + f->tbprofile;
616         prog->stack[prog->depth].builtinsprofile_acc += f->builtinsprofile;
617         if(prog->depth > 0)
618         {
619                 prog->stack[prog->depth-1].profile_acc += prog->stack[prog->depth].profile_acc;
620                 prog->stack[prog->depth-1].tprofile_acc += prog->stack[prog->depth].tprofile_acc;
621                 prog->stack[prog->depth-1].builtinsprofile_acc += prog->stack[prog->depth].builtinsprofile_acc;
622         }
623         if(!f->recursion)
624         {
625                 // if f is already on the call stack...
626                 // we cannot add this profile data to it now
627                 // or we would add it more than once
628                 // so, let's only add to the function's profile if it is the outermost call
629                 f->profile_total += prog->stack[prog->depth].profile_acc;
630                 f->tprofile_total += prog->stack[prog->depth].tprofile_acc;
631                 f->builtinsprofile_total += prog->stack[prog->depth].builtinsprofile_acc;
632         }
633         
634         return prog->stack[prog->depth].s;
635 }
636
637 void PRVM_Init_Exec(prvm_prog_t *prog)
638 {
639         // dump the stack
640         prog->depth = 0;
641         prog->localstack_used = 0;
642         // reset the string table
643         // nothing here yet
644 }
645
646 #define OPA ((prvm_eval_t *)&prog->globals.generic[st->operand[0]])
647 #define OPB ((prvm_eval_t *)&prog->globals.generic[st->operand[1]])
648 #define OPC ((prvm_eval_t *)&prog->globals.generic[st->operand[2]])
649 extern cvar_t prvm_traceqc;
650 extern cvar_t prvm_statementprofiling;
651 extern qboolean prvm_runawaycheck;
652
653 #ifdef PROFILING
654 /*
655 ====================
656 MVM_ExecuteProgram
657 ====================
658 */
659 void MVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
660 {
661         mstatement_t    *st, *startst;
662         mfunction_t     *f, *newf;
663         prvm_edict_t    *ed;
664         prvm_eval_t     *ptr;
665         int             jumpcount, cachedpr_trace, exitdepth;
666         int             restorevm_tempstringsbuf_cursize;
667         double  calltime;
668         double tm, starttm;
669
670         calltime = Sys_DirtyTime();
671
672         if (!fnum || fnum >= (unsigned int)prog->numfunctions)
673         {
674                 if (PRVM_allglobaledict(self))
675                         PRVM_ED_Print(prog, PRVM_PROG_TO_EDICT(PRVM_allglobaledict(self)), NULL);
676                 prog->error_cmd("MVM_ExecuteProgram: %s", errormessage);
677         }
678
679         f = &prog->functions[fnum];
680
681         // after executing this function, delete all tempstrings it created
682         restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
683
684         prog->trace = prvm_traceqc.integer;
685
686         // we know we're done when pr_depth drops to this
687         exitdepth = prog->depth;
688
689 // make a stack frame
690         st = &prog->statements[PRVM_EnterFunction(prog, f)];
691         // save the starting statement pointer for profiling
692         // (when the function exits or jumps, the (st - startst) integer value is
693         // added to the function's profile counter)
694         startst = st;
695         starttm = calltime;
696         // instead of counting instructions, we count jumps
697         jumpcount = 0;
698         // add one to the callcount of this function because otherwise engine-called functions aren't counted
699         prog->xfunction->callcount++;
700
701 chooseexecprogram:
702         cachedpr_trace = prog->trace;
703         if (prvm_statementprofiling.integer || prog->trace)
704         {
705 #define PRVMSLOWINTERPRETER 1
706                 if (prvm_timeprofiling.integer)
707                 {
708 #define PRVMTIMEPROFILING 1
709 #include "prvm_execprogram.h"
710 #undef PRVMTIMEPROFILING
711                 }
712                 else
713                 {
714 #include "prvm_execprogram.h"
715                 }
716 #undef PRVMSLOWINTERPRETER
717         }
718         else
719         {
720                 if (prvm_timeprofiling.integer)
721                 {
722 #define PRVMTIMEPROFILING 1
723 #include "prvm_execprogram.h"
724 #undef PRVMTIMEPROFILING
725                 }
726                 else
727                 {
728 #include "prvm_execprogram.h"
729                 }
730         }
731
732 cleanup:
733         if (developer_insane.integer && prog->tempstringsbuf.cursize > restorevm_tempstringsbuf_cursize)
734                 Con_DPrintf("MVM_ExecuteProgram: %s used %i bytes of tempstrings\n", PRVM_GetString(prog, prog->functions[fnum].s_name), prog->tempstringsbuf.cursize - restorevm_tempstringsbuf_cursize);
735         // delete tempstrings created by this function
736         prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
737
738         tm = Sys_DirtyTime() - calltime;if (tm < 0 || tm >= 1800) tm = 0;
739         f->totaltime += tm;
740
741         if (prog == SVVM_prog)
742                 SV_FlushBroadcastMessages();
743 }
744
745 /*
746 ====================
747 CLVM_ExecuteProgram
748 ====================
749 */
750 void CLVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
751 {
752         mstatement_t    *st, *startst;
753         mfunction_t     *f, *newf;
754         prvm_edict_t    *ed;
755         prvm_eval_t     *ptr;
756         int             jumpcount, cachedpr_trace, exitdepth;
757         int             restorevm_tempstringsbuf_cursize;
758         double  calltime;
759         double tm, starttm;
760
761         calltime = Sys_DirtyTime();
762
763         if (!fnum || fnum >= (unsigned int)prog->numfunctions)
764         {
765                 if (PRVM_allglobaledict(self))
766                         PRVM_ED_Print(prog, PRVM_PROG_TO_EDICT(PRVM_allglobaledict(self)), NULL);
767                 prog->error_cmd("CLVM_ExecuteProgram: %s", errormessage);
768         }
769
770         f = &prog->functions[fnum];
771
772         // after executing this function, delete all tempstrings it created
773         restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
774
775         prog->trace = prvm_traceqc.integer;
776
777         // we know we're done when pr_depth drops to this
778         exitdepth = prog->depth;
779
780 // make a stack frame
781         st = &prog->statements[PRVM_EnterFunction(prog, f)];
782         // save the starting statement pointer for profiling
783         // (when the function exits or jumps, the (st - startst) integer value is
784         // added to the function's profile counter)
785         startst = st;
786         starttm = calltime;
787         // instead of counting instructions, we count jumps
788         jumpcount = 0;
789         // add one to the callcount of this function because otherwise engine-called functions aren't counted
790         prog->xfunction->callcount++;
791
792 chooseexecprogram:
793         cachedpr_trace = prog->trace;
794         if (prvm_statementprofiling.integer || prog->trace)
795         {
796 #define PRVMSLOWINTERPRETER 1
797                 if (prvm_timeprofiling.integer)
798                 {
799 #define PRVMTIMEPROFILING 1
800 #include "prvm_execprogram.h"
801 #undef PRVMTIMEPROFILING
802                 }
803                 else
804                 {
805 #include "prvm_execprogram.h"
806                 }
807 #undef PRVMSLOWINTERPRETER
808         }
809         else
810         {
811                 if (prvm_timeprofiling.integer)
812                 {
813 #define PRVMTIMEPROFILING 1
814 #include "prvm_execprogram.h"
815 #undef PRVMTIMEPROFILING
816                 }
817                 else
818                 {
819 #include "prvm_execprogram.h"
820                 }
821         }
822
823 cleanup:
824         if (developer_insane.integer && prog->tempstringsbuf.cursize > restorevm_tempstringsbuf_cursize)
825                 Con_DPrintf("CLVM_ExecuteProgram: %s used %i bytes of tempstrings\n", PRVM_GetString(prog, prog->functions[fnum].s_name), prog->tempstringsbuf.cursize - restorevm_tempstringsbuf_cursize);
826         // delete tempstrings created by this function
827         prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
828
829         tm = Sys_DirtyTime() - calltime;if (tm < 0 || tm >= 1800) tm = 0;
830         f->totaltime += tm;
831
832         if (prog == SVVM_prog)
833                 SV_FlushBroadcastMessages();
834 }
835 #endif
836
837 /*
838 ====================
839 SVVM_ExecuteProgram
840 ====================
841 */
842 #ifdef PROFILING
843 void SVVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
844 #else
845 void PRVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
846 #endif
847 {
848         mstatement_t    *st, *startst;
849         mfunction_t     *f, *newf;
850         prvm_edict_t    *ed;
851         prvm_eval_t     *ptr;
852         int             jumpcount, cachedpr_trace, exitdepth;
853         int             restorevm_tempstringsbuf_cursize;
854         double  calltime;
855         double tm, starttm;
856
857         calltime = Sys_DirtyTime();
858
859         if (!fnum || fnum >= (unsigned int)prog->numfunctions)
860         {
861                 if (PRVM_allglobaledict(self))
862                         PRVM_ED_Print(prog, PRVM_PROG_TO_EDICT(PRVM_allglobaledict(self)), NULL);
863                 prog->error_cmd("SVVM_ExecuteProgram: %s", errormessage);
864         }
865
866         f = &prog->functions[fnum];
867
868         // after executing this function, delete all tempstrings it created
869         restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
870
871         prog->trace = prvm_traceqc.integer;
872
873         // we know we're done when pr_depth drops to this
874         exitdepth = prog->depth;
875
876 // make a stack frame
877         st = &prog->statements[PRVM_EnterFunction(prog, f)];
878         // save the starting statement pointer for profiling
879         // (when the function exits or jumps, the (st - startst) integer value is
880         // added to the function's profile counter)
881         startst = st;
882         starttm = calltime;
883         // instead of counting instructions, we count jumps
884         jumpcount = 0;
885         // add one to the callcount of this function because otherwise engine-called functions aren't counted
886         prog->xfunction->callcount++;
887
888 chooseexecprogram:
889         cachedpr_trace = prog->trace;
890         if (prvm_statementprofiling.integer || prog->trace)
891         {
892 #define PRVMSLOWINTERPRETER 1
893                 if (prvm_timeprofiling.integer)
894                 {
895 #define PRVMTIMEPROFILING 1
896 #include "prvm_execprogram.h"
897 #undef PRVMTIMEPROFILING
898                 }
899                 else
900                 {
901 #include "prvm_execprogram.h"
902                 }
903 #undef PRVMSLOWINTERPRETER
904         }
905         else
906         {
907                 if (prvm_timeprofiling.integer)
908                 {
909 #define PRVMTIMEPROFILING 1
910 #include "prvm_execprogram.h"
911 #undef PRVMTIMEPROFILING
912                 }
913                 else
914                 {
915 #include "prvm_execprogram.h"
916                 }
917         }
918
919 cleanup:
920         if (developer_insane.integer && prog->tempstringsbuf.cursize > restorevm_tempstringsbuf_cursize)
921                 Con_DPrintf("SVVM_ExecuteProgram: %s used %i bytes of tempstrings\n", PRVM_GetString(prog, prog->functions[fnum].s_name), prog->tempstringsbuf.cursize - restorevm_tempstringsbuf_cursize);
922         // delete tempstrings created by this function
923         prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
924
925         tm = Sys_DirtyTime() - calltime;if (tm < 0 || tm >= 1800) tm = 0;
926         f->totaltime += tm;
927
928         if (prog == SVVM_prog)
929                 SV_FlushBroadcastMessages();
930 }