]> git.xonotic.org Git - xonotic/darkplaces.git/blob - prvm_exec.c
PRVM: refactor error handling
[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 static 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 NULL,
116 NULL,
117 NULL,
118 NULL,
119 NULL,
120 NULL,
121 NULL,
122 NULL,
123 NULL,
124 NULL,
125 NULL,
126 NULL,
127 NULL,
128 NULL,
129 NULL,
130 NULL,
131 NULL,
132 NULL,
133 NULL,
134 NULL,
135 NULL,
136 NULL,
137 NULL,
138 NULL,
139 NULL,
140 NULL,
141 NULL,
142 NULL,
143 NULL,
144 NULL,
145 NULL,
146 NULL,
147 NULL,
148 NULL,
149 NULL,
150 NULL,
151 NULL,
152 NULL,
153 NULL,
154 NULL,
155 NULL,
156 NULL,
157 NULL,
158 NULL,
159 NULL,
160 NULL,
161 NULL,
162
163 "STORE_I",
164
165 NULL,
166 NULL,
167
168 "ADD_I",
169 "ADD_FI",
170 "ADD_IF",
171
172 "SUB_I",
173 "SUB_FI",
174 "SUB_IF",
175 "CONV_IF",
176 "CONV_FI",
177
178 NULL,
179 NULL,
180
181 "LOAD_I",
182 "STOREP_I",
183
184 NULL,
185 NULL,
186
187 "BITAND_I",
188 "BITOR_I",
189
190 "MUL_I",
191 "DIV_I",
192 "EQ_I",
193 "NE_I",
194
195 NULL,
196 NULL,
197
198 "NOT_I",
199
200 "DIV_VF",
201
202 NULL,
203 NULL,
204 NULL,
205 NULL,
206 NULL,
207 NULL,
208 NULL,
209 NULL,
210 NULL,
211 NULL,
212 NULL,
213 NULL,
214
215 "STORE_P",
216
217 NULL,
218 NULL,
219 NULL,
220 NULL,
221 NULL,
222 NULL,
223 NULL,
224 NULL,
225
226 "LE_I",
227 "GE_I",
228 "LT_I",
229 "GT_I",
230
231 "LE_IF",
232 "GE_IF",
233 "LT_IF",
234 "GT_IF",
235
236 "LE_FI",
237 "GE_FI",
238 "LT_FI",
239 "GT_FI",
240
241 "EQ_IF",
242 "EQ_FI",
243
244 NULL,
245 NULL,
246 NULL,
247 NULL,
248
249 "MUL_IF",
250 "MUL_FI",
251 "MUL_VI",
252
253 NULL,
254
255 "DIV_IF",
256 "DIV_FI",
257 "BITAND_IF",
258 "BITOR_IF",
259 "BITAND_FI",
260 "BITOR_FI",
261 "AND_I",
262 "OR_I",
263 "AND_IF",
264 "OR_IF",
265 "AND_FI",
266 "OR_FI",
267 "NE_IF",
268 "NE_FI",
269
270 "GSTOREP_I",
271 "GSTOREP_F",
272 "GSTOREP_ENT",
273 "GSTOREP_FLD",
274 "GSTOREP_S",
275 "GSTOREP_FNC",
276 "GSTOREP_V",
277 "GADDRESS",
278 "GLOAD_I",
279 "GLOAD_F",
280 "GLOAD_FLD",
281 "GLOAD_ENT",
282 "GLOAD_S",
283 "GLOAD_FNC",
284 "BOUNDCHECK",
285 NULL,
286 NULL,
287 NULL,
288 NULL,
289 "GLOAD_V",
290 };
291
292
293
294 //=============================================================================
295
296 /*
297 =================
298 PRVM_PrintStatement
299 =================
300 */
301 extern cvar_t prvm_coverage;
302 extern cvar_t prvm_statementprofiling;
303 extern cvar_t prvm_timeprofiling;
304 static void PRVM_PrintStatement(prvm_prog_t *prog, mstatement_t *s)
305 {
306         size_t i;
307         int opnum = (int)(s - prog->statements);
308         char valuebuf[MAX_INPUTLINE];
309         const char *opname;
310
311         Con_Printf("s%i: ", opnum);
312         if( prog->statement_linenums )
313         {
314                 if ( prog->statement_columnnums )
315                         Con_Printf( "%s:%i:%i: ", PRVM_GetString( prog, prog->xfunction->s_file ), prog->statement_linenums[ opnum ], prog->statement_columnnums[ opnum ] );
316                 else
317                         Con_Printf( "%s:%i: ", PRVM_GetString( prog, prog->xfunction->s_file ), prog->statement_linenums[ opnum ] );
318         }
319
320         if (prvm_statementprofiling.integer)
321                 Con_Printf("%7.0f ", prog->statement_profile[s - prog->statements]);
322
323         if ( (unsigned)s->op < sizeof(prvm_opnames)/sizeof(prvm_opnames[0]) && prvm_opnames[s->op])
324                 opname = prvm_opnames[s->op];
325         else
326                 opname = valuebuf, dpsnprintf(valuebuf, sizeof(valuebuf), "OPCODE_%u", (unsigned)s->op);
327         Con_Printf("%s ",  opname);
328         i = strlen(opname);
329         // don't count a preceding color tag when padding the name
330         if (opname[0] == STRING_COLOR_TAG)
331                 i -= 2;
332         for ( ; i<10 ; i++)
333                 Con_Print(" ");
334
335         if (s->operand[0] >= 0) Con_Printf(  "%s", PRVM_GlobalString(prog, s->operand[0], valuebuf, sizeof(valuebuf)));
336         if (s->operand[1] >= 0) Con_Printf(", %s", PRVM_GlobalString(prog, s->operand[1], valuebuf, sizeof(valuebuf)));
337         if (s->operand[2] >= 0) Con_Printf(", %s", PRVM_GlobalString(prog, s->operand[2], valuebuf, sizeof(valuebuf)));
338         if (s->jumpabsolute >= 0) Con_Printf(", statement %i", s->jumpabsolute);
339         Con_Print("\n");
340 }
341
342 void PRVM_PrintFunctionStatements (prvm_prog_t *prog, const char *name)
343 {
344         int i, firststatement, endstatement;
345         mfunction_t *func;
346         func = PRVM_ED_FindFunction (prog, name);
347         if (!func)
348         {
349                 Con_Printf("%s progs: no function named %s\n", prog->name, name);
350                 return;
351         }
352         firststatement = func->first_statement;
353         if (firststatement < 0)
354         {
355                 Con_Printf("%s progs: function %s is builtin #%i\n", prog->name, name, -firststatement);
356                 return;
357         }
358
359         // find the end statement
360         endstatement = prog->numstatements;
361         for (i = 0;i < prog->numfunctions;i++)
362                 if (endstatement > prog->functions[i].first_statement && firststatement < prog->functions[i].first_statement)
363                         endstatement = prog->functions[i].first_statement;
364
365         // now print the range of statements
366         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);
367         prog->xfunction = func;
368         for (i = firststatement;i < endstatement;i++)
369         {
370                 PRVM_PrintStatement(prog, prog->statements + i);
371                 if (!(prvm_coverage.integer & 4))
372                         prog->statement_profile[i] = 0;
373         }
374         if (prvm_coverage.integer & 4)
375                 Con_Printf("Collecting statement coverage, not flushing statement profile.\n");
376 }
377
378 /*
379 ============
380 PRVM_PrintFunction_f
381
382 ============
383 */
384 void PRVM_PrintFunction_f(cmd_state_t *cmd)
385 {
386         prvm_prog_t *prog;
387         if (Cmd_Argc(cmd) != 3)
388         {
389                 Con_Printf("usage: prvm_printfunction <program name> <function name>\n");
390                 return;
391         }
392
393         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
394                 return;
395
396         PRVM_PrintFunctionStatements(prog, Cmd_Argv(cmd, 2));
397 }
398
399 /*
400 ============
401 PRVM_StackTrace
402 ============
403 */
404 void PRVM_StackTrace (prvm_prog_t *prog)
405 {
406         mfunction_t     *f;
407         int                     i;
408
409         prog->stack[prog->depth].s = prog->xstatement;
410         prog->stack[prog->depth].f = prog->xfunction;
411         for (i = prog->depth;i > 0;i--)
412         {
413                 f = prog->stack[i].f;
414
415                 if (!f)
416                         Con_Print("<NULL FUNCTION>\n");
417                 else
418                 {
419                         if (prog->statement_linenums)
420                         {
421                                 if (prog->statement_columnnums)
422                                         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);
423                                 else
424                                         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);
425                         }
426                         else
427                                 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);
428                 }
429         }
430 }
431
432 void PRVM_ShortStackTrace(prvm_prog_t *prog, char *buf, size_t bufsize)
433 {
434         mfunction_t *f;
435         int i;
436         char vabuf[1024];
437         char *p;
438
439         if(prog)
440         {
441                 i = dpsnprintf(buf, bufsize, "(%s) ", prog->name);
442                 p = buf + max(0, i);
443         }
444         else
445         {
446                 dp_strlcpy(buf, "<NO PROG>", bufsize);
447                 return;
448         }
449
450         prog->stack[prog->depth].s = prog->xstatement;
451         prog->stack[prog->depth].f = prog->xfunction;
452         for (i = prog->depth;i > 0;i--)
453         {
454                 f = prog->stack[i].f;
455                 p = dp_stpecpy(
456                         p,
457                         buf + bufsize,
458                         f
459                                 ? 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)
460                                 : "<NULL> "
461                         );
462                 if (p == buf + bufsize)
463                         break;
464         }
465 }
466
467
468 static void PRVM_CallProfile (prvm_prog_t *prog)
469 {
470         mfunction_t *f, *best;
471         int i;
472         double max;
473         double sum;
474         double newprofiletime;
475
476         Con_Printf( "%s Call Profile:\n", prog->name );
477
478         sum = 0;
479         do
480         {
481                 max = 0;
482                 best = NULL;
483                 for (i=0 ; i<prog->numfunctions ; i++)
484                 {
485                         f = &prog->functions[i];
486                         if (max < f->totaltime)
487                         {
488                                 max = f->totaltime;
489                                 best = f;
490                         }
491                 }
492                 if (best)
493                 {
494                         sum += best->totaltime;
495                         Con_Printf("%9.4f %s\n", best->totaltime, PRVM_GetString(prog, best->s_name));
496                         best->totaltime = 0;
497                 }
498         } while (best);
499
500         newprofiletime = Sys_DirtyTime();
501         Con_Printf("Total time since last profile reset: %9.4f\n", newprofiletime - prog->profiletime);
502         Con_Printf("       - used by QC code of this VM: %9.4f\n", sum);
503
504         prog->profiletime = newprofiletime;
505 }
506
507 void PRVM_Profile (prvm_prog_t *prog, int maxfunctions, double mintime, int sortby)
508 {
509         mfunction_t *f, *best;
510         int i, num;
511         double max;
512
513         if(!prvm_timeprofiling.integer)
514                 mintime *= 10000000; // count each statement as about 0.1µs
515
516         if(prvm_timeprofiling.integer)
517                 Con_Printf( "%s Profile:\n[CallCount]      [Time] [BuiltinTm] [Statement] [BuiltinCt] [TimeTotal] [StmtTotal] [BltnTotal] [self]\n", prog->name );
518                 //                        12345678901 12345678901 12345678901 12345678901 12345678901 12345678901 12345678901 123.45%
519         else
520                 Con_Printf( "%s Profile:\n[CallCount] [Statement] [BuiltinCt] [StmtTotal] [BltnTotal] [self]\n", prog->name );
521                 //                        12345678901 12345678901 12345678901 12345678901 12345678901 123.45%
522
523         num = 0;
524         do
525         {
526                 max = 0;
527                 best = NULL;
528                 for (i=0 ; i<prog->numfunctions ; i++)
529                 {
530                         f = &prog->functions[i];
531                         if(prvm_timeprofiling.integer)
532                         {
533                                 if(sortby)
534                                 {
535                                         if(f->first_statement < 0)
536                                         {
537                                                 if (max < f->tprofile)
538                                                 {
539                                                         max = f->tprofile;
540                                                         best = f;
541                                                 }
542                                         }
543                                         else
544                                         {
545                                                 if (max < f->tprofile_total)
546                                                 {
547                                                         max = f->tprofile_total;
548                                                         best = f;
549                                                 }
550                                         }
551                                 }
552                                 else
553                                 {
554                                         if (max < f->tprofile + f->tbprofile)
555                                         {
556                                                 max = f->tprofile + f->tbprofile;
557                                                 best = f;
558                                         }
559                                 }
560                         }
561                         else
562                         {
563                                 if(sortby)
564                                 {
565                                         if (max < f->profile_total + f->builtinsprofile_total + f->callcount)
566                                         {
567                                                 max = f->profile_total + f->builtinsprofile_total + f->callcount;
568                                                 best = f;
569                                         }
570                                 }
571                                 else
572                                 {
573                                         if (max < f->profile + f->builtinsprofile + f->callcount)
574                                         {
575                                                 max = f->profile + f->builtinsprofile + f->callcount;
576                                                 best = f;
577                                         }
578                                 }
579                         }
580                 }
581                 if (best)
582                 {
583                         if (num < maxfunctions && max > mintime)
584                         {
585                                 if(prvm_timeprofiling.integer)
586                                 {
587                                         if (best->first_statement < 0)
588                                                 Con_Printf("%11.0f %11.6f ------------- builtin ------------- %11.6f ----------- builtin ----------- %s\n", best->callcount, best->tprofile, best->tprofile, PRVM_GetString(prog, best->s_name));
589                                         //                 %11.6f 12345678901 12345678901 12345678901 %11.6f 12345678901 12345678901 123.45%
590                                         else
591                                                 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));
592                                 }
593                                 else
594                                 {
595                                         if (best->first_statement < 0)
596                                                 Con_Printf("%11.0f ----------------------- builtin ----------------------- %s\n", best->callcount, PRVM_GetString(prog, best->s_name));
597                                         //                 12345678901 12345678901 12345678901 12345678901 123.45%
598                                         else
599                                                 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));
600                                 }
601                         }
602                         num++;
603                         best->profile = 0;
604                         best->tprofile = 0;
605                         best->tbprofile = 0;
606                         best->builtinsprofile = 0;
607                         best->profile_total = 0;
608                         best->tprofile_total = 0;
609                         best->builtinsprofile_total = 0;
610                         best->callcount = 0;
611                 }
612         } while (best);
613 }
614
615 /*
616 ============
617 PRVM_CallProfile_f
618
619 ============
620 */
621 void PRVM_CallProfile_f(cmd_state_t *cmd)
622 {
623         prvm_prog_t *prog;
624         if (Cmd_Argc(cmd) != 2)
625         {
626                 Con_Print("prvm_callprofile <program name>\n");
627                 return;
628         }
629
630         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
631                 return;
632
633         PRVM_CallProfile(prog);
634 }
635
636 /*
637 ============
638 PRVM_Profile_f
639
640 ============
641 */
642 void PRVM_Profile_f(cmd_state_t *cmd)
643 {
644         prvm_prog_t *prog;
645         int howmany;
646
647         if (prvm_coverage.integer & 1)
648         {
649                 Con_Printf("Collecting function coverage, cannot profile - sorry!\n");
650                 return;
651         }
652
653         howmany = 1<<30;
654         if (Cmd_Argc(cmd) == 3)
655                 howmany = atoi(Cmd_Argv(cmd, 2));
656         else if (Cmd_Argc(cmd) != 2)
657         {
658                 Con_Print("prvm_profile <program name>\n");
659                 return;
660         }
661
662         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
663                 return;
664
665         PRVM_Profile(prog, howmany, 0, 0);
666 }
667
668 void PRVM_ChildProfile_f(cmd_state_t *cmd)
669 {
670         prvm_prog_t *prog;
671         int howmany;
672
673         if (prvm_coverage.integer & 1)
674         {
675                 Con_Printf("Collecting function coverage, cannot profile - sorry!\n");
676                 return;
677         }
678
679         howmany = 1<<30;
680         if (Cmd_Argc(cmd) == 3)
681                 howmany = atoi(Cmd_Argv(cmd, 2));
682         else if (Cmd_Argc(cmd) != 2)
683         {
684                 Con_Print("prvm_childprofile <program name>\n");
685                 return;
686         }
687
688         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
689                 return;
690
691         PRVM_Profile(prog, howmany, 0, 1);
692 }
693
694 void PRVM_PrintState(prvm_prog_t *prog, int stack_index)
695 {
696         int i;
697         mfunction_t *func = prog->xfunction;
698         int st = prog->xstatement;
699         if (stack_index > 0 && stack_index <= prog->depth)
700         {
701                 func = prog->stack[prog->depth - stack_index].f;
702                 st = prog->stack[prog->depth - stack_index].s;
703         }
704         if (prog->statestring)
705         {
706                 Con_Printf("Caller-provided information: %s\n", prog->statestring);
707         }
708         if (func)
709         {
710                 for (i = -7; i <= 0;i++)
711                         if (st + i >= func->first_statement)
712                                 PRVM_PrintStatement(prog, prog->statements + st + i);
713         }
714         PRVM_StackTrace(prog);
715 }
716
717 extern cvar_t prvm_errordump;
718 void PRVM_Crash(void)
719 {
720         prvm_prog_t *prog;
721         char vabuf[1024];
722         int i;
723
724         // determine which program crashed
725         for (i = 0; i < PRVM_PROG_MAX; ++i)
726                 if (PRVM_GetProg(i)->loaded && PRVM_GetProg(i)->depth > 0)
727                         break;
728         if (i >= PRVM_PROG_MAX)
729                 return; // none of them crashed
730         prog = PRVM_GetProg(i);
731
732         Con_Printf("QuakeC crash report for %s:\n", prog->name);
733         PRVM_PrintState(prog, 0);
734
735         // don't call graceful shutdown on crash
736         if (prog == SVVM_prog)
737                 PRVM_serverfunction(SV_Shutdown) = 0;
738         else if (prog == CLVM_prog)
739                 PRVM_clientfunction(CSQC_Shutdown) = 0;
740
741         if(prvm_errordump.integer && (prog == SVVM_prog || prog == CLVM_prog))
742         {
743                 // make a savegame
744                 SV_Savegame_to(prog, va(vabuf, sizeof(vabuf), "crash-%s.dmp", prog->name));
745         }
746
747         // dump the stack so host_error can shutdown functions
748         prog->depth = 0;
749         prog->localstack_used = 0;
750 }
751
752 /*
753 ============================================================================
754 PRVM_ExecuteProgram
755
756 The interpretation main loop
757 ============================================================================
758 */
759
760 /*
761 ====================
762 PRVM_EnterFunction
763
764 Returns the new program statement counter
765 ====================
766 */
767 static int PRVM_EnterFunction (prvm_prog_t *prog, mfunction_t *f)
768 {
769         int             i, j, c, o;
770
771         if (!f)
772                 prog->error_cmd("PRVM_EnterFunction: NULL function in %s", prog->name);
773
774         prog->stack[prog->depth].s = prog->xstatement;
775         prog->stack[prog->depth].f = prog->xfunction;
776         prog->stack[prog->depth].profile_acc = -f->profile;
777         prog->stack[prog->depth].tprofile_acc = -f->tprofile + -f->tbprofile;
778         prog->stack[prog->depth].builtinsprofile_acc = -f->builtinsprofile;
779         prog->depth++;
780         if (prog->depth >=PRVM_MAX_STACK_DEPTH)
781                 prog->error_cmd("stack overflow");
782
783 // save off any locals that the new function steps on
784         c = f->locals;
785         if (prog->localstack_used + c > PRVM_LOCALSTACK_SIZE)
786                 prog->error_cmd("PRVM_ExecuteProgram: locals stack overflow in %s", prog->name);
787
788         for (i=0 ; i < c ; i++)
789                 prog->localstack[prog->localstack_used+i] = prog->globals.ip[f->parm_start + i];
790         prog->localstack_used += c;
791
792 // copy parameters
793         o = f->parm_start;
794         for (i=0 ; i<f->numparms ; i++)
795         {
796                 for (j=0 ; j<f->parm_size[i] ; j++)
797                 {
798                         prog->globals.ip[o] = prog->globals.ip[OFS_PARM0+i*3+j];
799                         o++;
800                 }
801         }
802
803         ++f->recursion;
804         prog->xfunction = f;
805         return f->first_statement - 1;  // offset the s++
806 }
807
808 /*
809 ====================
810 PRVM_LeaveFunction
811 ====================
812 */
813 static int PRVM_LeaveFunction (prvm_prog_t *prog)
814 {
815         int             i, c;
816         mfunction_t *f;
817
818         if (prog->depth <= 0)
819                 prog->error_cmd("prog stack underflow in %s", prog->name);
820
821         if (!prog->xfunction)
822                 prog->error_cmd("PR_LeaveFunction: NULL function in %s", prog->name);
823 // restore locals from the stack
824         c = prog->xfunction->locals;
825         prog->localstack_used -= c;
826         if (prog->localstack_used < 0)
827                 prog->error_cmd("PRVM_ExecuteProgram: locals stack underflow in %s", prog->name);
828
829         for (i=0 ; i < c ; i++)
830                 prog->globals.ip[prog->xfunction->parm_start + i] = prog->localstack[prog->localstack_used+i];
831
832 // up stack
833         prog->depth--;
834         f = prog->xfunction;
835         --f->recursion;
836         prog->xfunction = prog->stack[prog->depth].f;
837         prog->stack[prog->depth].profile_acc += f->profile;
838         prog->stack[prog->depth].tprofile_acc += f->tprofile + f->tbprofile;
839         prog->stack[prog->depth].builtinsprofile_acc += f->builtinsprofile;
840         if(prog->depth > 0)
841         {
842                 prog->stack[prog->depth-1].profile_acc += prog->stack[prog->depth].profile_acc;
843                 prog->stack[prog->depth-1].tprofile_acc += prog->stack[prog->depth].tprofile_acc;
844                 prog->stack[prog->depth-1].builtinsprofile_acc += prog->stack[prog->depth].builtinsprofile_acc;
845         }
846         if(!f->recursion)
847         {
848                 // if f is already on the call stack...
849                 // we cannot add this profile data to it now
850                 // or we would add it more than once
851                 // so, let's only add to the function's profile if it is the outermost call
852                 f->profile_total += prog->stack[prog->depth].profile_acc;
853                 f->tprofile_total += prog->stack[prog->depth].tprofile_acc;
854                 f->builtinsprofile_total += prog->stack[prog->depth].builtinsprofile_acc;
855         }
856         
857         return prog->stack[prog->depth].s;
858 }
859
860 void PRVM_Init_Exec(prvm_prog_t *prog)
861 {
862         // dump the stack
863         prog->depth = 0;
864         prog->localstack_used = 0;
865         // reset the string table
866         // nothing here yet
867 }
868
869 /*
870 ==================
871 Coverage
872 ==================
873 */
874 // Note: in these two calls, prog->xfunction is assumed to be sane.
875 static const char *PRVM_WhereAmI(char *buf, size_t bufsize, prvm_prog_t *prog, mfunction_t *func, int statement)
876 {
877         if (prog->statement_linenums)
878         {
879                 if (prog->statement_columnnums)
880                         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);
881                 else
882                         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);
883         }
884         else
885                 return va(buf, bufsize, "%s(%s, %i)", PRVM_GetString(prog, func->s_file), PRVM_GetString(prog, func->s_name), statement - func->first_statement);
886 }
887 static void PRVM_FunctionCoverageEvent(prvm_prog_t *prog, mfunction_t *func)
888 {
889         ++prog->functions_covered;
890         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);
891 }
892 void PRVM_ExplicitCoverageEvent(prvm_prog_t *prog, mfunction_t *func, int statement)
893 {
894         char vabuf[128];
895         ++prog->explicit_covered;
896         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);
897 }
898 static void PRVM_StatementCoverageEvent(prvm_prog_t *prog, mfunction_t *func, int statement)
899 {
900         char vabuf[128];
901         ++prog->statements_covered;
902         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);
903 }
904
905 #if defined (__GNUC__) || (__clang__) || (__TINYC__)
906 #  ifndef CONFIG_PEDANTIC
907 #  define HAVE_COMPUTED_GOTOS 1
908 #  endif
909 #endif
910
911 #define OPA ((prvm_eval_t *)&globals[st->operand[0]])
912 #define OPB ((prvm_eval_t *)&globals[st->operand[1]])
913 #define OPC ((prvm_eval_t *)&globals[st->operand[2]])
914 extern cvar_t prvm_traceqc;
915 extern cvar_t prvm_statementprofiling;
916 extern qbool prvm_runawaycheck;
917
918 #ifdef PROFILING
919 #ifdef CONFIG_MENU
920 /*
921 ====================
922 MVM_ExecuteProgram
923 ====================
924 */
925 void MVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
926 {
927         mstatement_t    *st, *startst;
928         mfunction_t             *func, *enterfunc;
929         prvm_edict_t    *ed;
930         prvm_eval_t     *ptr;
931         int             jumpcount, cachedpr_trace, exitdepth;
932         int             restorevm_tempstringsbuf_cursize;
933         double  calltime;
934         double tm, starttm;
935         prvm_vec_t tempfloat;
936         // these may become out of date when a builtin is called, and are updated accordingly
937         prvm_vec_t *cached_edictsfields = prog->edictsfields.fp;
938         unsigned int cached_entityfields = prog->entityfields;
939         unsigned int cached_entityfields_3 = prog->entityfields - 3;
940         unsigned int cached_entityfieldsarea = prog->entityfieldsarea;
941         unsigned int cached_entityfieldsarea_entityfields = prog->entityfieldsarea - prog->entityfields;
942         unsigned int cached_entityfieldsarea_3 = prog->entityfieldsarea - 3;
943         unsigned int cached_entityfieldsarea_entityfields_3 = prog->entityfieldsarea - prog->entityfields - 3;
944         unsigned int cached_max_edicts = prog->max_edicts;
945         // these do not change
946         mstatement_t *cached_statements = prog->statements;
947         qbool cached_allowworldwrites = prog->allowworldwrites;
948         unsigned int cached_flag = prog->flag;
949
950         prvm_vec_t *globals = prog->globals.fp;
951
952         calltime = Sys_DirtyTime();
953
954         if (!fnum || fnum >= (unsigned int)prog->numfunctions)
955         {
956                 if (PRVM_allglobaledict(self))
957                         PRVM_ED_Print(prog, PRVM_PROG_TO_EDICT(PRVM_allglobaledict(self)), NULL);
958                 prog->error_cmd("MVM_ExecuteProgram: %s", errormessage);
959         }
960
961         func = &prog->functions[fnum];
962
963         // after executing this function, delete all tempstrings it created
964         restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
965
966         prog->trace = prvm_traceqc.integer;
967
968         // we know we're done when pr_depth drops to this
969         exitdepth = prog->depth;
970
971 // make a stack frame
972         st = &prog->statements[PRVM_EnterFunction(prog, func)];
973         // save the starting statement pointer for profiling
974         // (when the function exits or jumps, the (st - startst) integer value is
975         // added to the function's profile counter)
976         startst = st;
977         starttm = calltime;
978         // instead of counting instructions, we count jumps
979         jumpcount = 0;
980         // add one to the callcount of this function because otherwise engine-called functions aren't counted
981         if (prog->xfunction->callcount++ == 0 && (prvm_coverage.integer & 1))
982                 PRVM_FunctionCoverageEvent(prog, prog->xfunction);
983
984 chooseexecprogram:
985         cachedpr_trace = prog->trace;
986         if (prog->trace || prog->watch_global_type != ev_void || prog->watch_field_type != ev_void || prog->break_statement >= 0)
987         {
988 #define PRVMSLOWINTERPRETER 1
989                 if (prvm_timeprofiling.integer)
990                 {
991 #define PRVMTIMEPROFILING 1
992 #include "prvm_execprogram.h"
993 #undef PRVMTIMEPROFILING
994                 }
995                 else
996                 {
997 #include "prvm_execprogram.h"
998                 }
999 #undef PRVMSLOWINTERPRETER
1000         }
1001         else
1002         {
1003                 if (prvm_timeprofiling.integer)
1004                 {
1005 #define PRVMTIMEPROFILING 1
1006 #include "prvm_execprogram.h"
1007 #undef PRVMTIMEPROFILING
1008                 }
1009                 else
1010                 {
1011 #include "prvm_execprogram.h"
1012                 }
1013         }
1014
1015 cleanup:
1016         if (developer_insane.integer && prog->tempstringsbuf.cursize > restorevm_tempstringsbuf_cursize)
1017                 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);
1018         // delete tempstrings created by this function
1019         prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1020
1021         tm = Sys_DirtyTime() - calltime;if (tm < 0 || tm >= 1800) tm = 0;
1022         func->totaltime += tm;
1023
1024         if (prog == SVVM_prog)
1025                 SV_FlushBroadcastMessages();
1026 }
1027 #endif // CONFIG_MENU
1028
1029 /*
1030 ====================
1031 CLVM_ExecuteProgram
1032 ====================
1033 */
1034 void CLVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
1035 {
1036         mstatement_t    *st, *startst;
1037         mfunction_t             *func, *enterfunc;
1038         prvm_edict_t    *ed;
1039         prvm_eval_t     *ptr;
1040         int             jumpcount, cachedpr_trace, exitdepth;
1041         int             restorevm_tempstringsbuf_cursize;
1042         double  calltime;
1043         double tm, starttm;
1044         prvm_vec_t tempfloat;
1045         // these may become out of date when a builtin is called, and are updated accordingly
1046         prvm_vec_t *cached_edictsfields = prog->edictsfields.fp;
1047         unsigned int cached_entityfields = prog->entityfields;
1048         unsigned int cached_entityfields_3 = prog->entityfields - 3;
1049         unsigned int cached_entityfieldsarea = prog->entityfieldsarea;
1050         unsigned int cached_entityfieldsarea_entityfields = prog->entityfieldsarea - prog->entityfields;
1051         unsigned int cached_entityfieldsarea_3 = prog->entityfieldsarea - 3;
1052         unsigned int cached_entityfieldsarea_entityfields_3 = prog->entityfieldsarea - prog->entityfields - 3;
1053         unsigned int cached_max_edicts = prog->max_edicts;
1054         // these do not change
1055         mstatement_t *cached_statements = prog->statements;
1056         qbool cached_allowworldwrites = prog->allowworldwrites;
1057         unsigned int cached_flag = prog->flag;
1058
1059         prvm_vec_t *globals = prog->globals.fp;
1060
1061         calltime = Sys_DirtyTime();
1062
1063         if (!fnum || fnum >= (unsigned int)prog->numfunctions)
1064         {
1065                 if (PRVM_allglobaledict(self))
1066                         PRVM_ED_Print(prog, PRVM_PROG_TO_EDICT(PRVM_allglobaledict(self)), NULL);
1067                 prog->error_cmd("CLVM_ExecuteProgram: %s", errormessage);
1068         }
1069
1070         func = &prog->functions[fnum];
1071
1072         // after executing this function, delete all tempstrings it created
1073         restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1074
1075         prog->trace = prvm_traceqc.integer;
1076
1077         // we know we're done when pr_depth drops to this
1078         exitdepth = prog->depth;
1079
1080 // make a stack frame
1081         st = &prog->statements[PRVM_EnterFunction(prog, func)];
1082         // save the starting statement pointer for profiling
1083         // (when the function exits or jumps, the (st - startst) integer value is
1084         // added to the function's profile counter)
1085         startst = st;
1086         starttm = calltime;
1087         // instead of counting instructions, we count jumps
1088         jumpcount = 0;
1089         // add one to the callcount of this function because otherwise engine-called functions aren't counted
1090         if (prog->xfunction->callcount++ == 0 && (prvm_coverage.integer & 1))
1091                 PRVM_FunctionCoverageEvent(prog, prog->xfunction);
1092
1093 chooseexecprogram:
1094         cachedpr_trace = prog->trace;
1095         if (prog->trace || prog->watch_global_type != ev_void || prog->watch_field_type != ev_void || prog->break_statement >= 0)
1096         {
1097 #define PRVMSLOWINTERPRETER 1
1098                 if (prvm_timeprofiling.integer)
1099                 {
1100 #define PRVMTIMEPROFILING 1
1101 #include "prvm_execprogram.h"
1102 #undef PRVMTIMEPROFILING
1103                 }
1104                 else
1105                 {
1106 #include "prvm_execprogram.h"
1107                 }
1108 #undef PRVMSLOWINTERPRETER
1109         }
1110         else
1111         {
1112                 if (prvm_timeprofiling.integer)
1113                 {
1114 #define PRVMTIMEPROFILING 1
1115 #include "prvm_execprogram.h"
1116 #undef PRVMTIMEPROFILING
1117                 }
1118                 else
1119                 {
1120 #include "prvm_execprogram.h"
1121                 }
1122         }
1123
1124 cleanup:
1125         if (developer_insane.integer && prog->tempstringsbuf.cursize > restorevm_tempstringsbuf_cursize)
1126                 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);
1127         // delete tempstrings created by this function
1128         prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1129
1130         tm = Sys_DirtyTime() - calltime;if (tm < 0 || tm >= 1800) tm = 0;
1131         func->totaltime += tm;
1132
1133         if (prog == SVVM_prog)
1134                 SV_FlushBroadcastMessages();
1135 }
1136 #endif // PROFILING
1137
1138 /*
1139 ====================
1140 SVVM_ExecuteProgram
1141 ====================
1142 */
1143 #ifdef PROFILING
1144 void SVVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
1145 #else
1146 void PRVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
1147 #endif
1148 {
1149         mstatement_t    *st, *startst;
1150         mfunction_t             *func, *enterfunc;
1151         prvm_edict_t    *ed;
1152         prvm_eval_t     *ptr;
1153         int             jumpcount, cachedpr_trace, exitdepth;
1154         int             restorevm_tempstringsbuf_cursize;
1155         double  calltime;
1156         double tm, starttm;
1157         prvm_vec_t tempfloat;
1158         // these may become out of date when a builtin is called, and are updated accordingly
1159         prvm_vec_t *cached_edictsfields = prog->edictsfields.fp;
1160         unsigned int cached_entityfields = prog->entityfields;
1161         unsigned int cached_entityfields_3 = prog->entityfields - 3;
1162         unsigned int cached_entityfieldsarea = prog->entityfieldsarea;
1163         unsigned int cached_entityfieldsarea_entityfields = prog->entityfieldsarea - prog->entityfields;
1164         unsigned int cached_entityfieldsarea_3 = prog->entityfieldsarea - 3;
1165         unsigned int cached_entityfieldsarea_entityfields_3 = prog->entityfieldsarea - prog->entityfields - 3;
1166         unsigned int cached_max_edicts = prog->max_edicts;
1167         // these do not change
1168         mstatement_t *cached_statements = prog->statements;
1169         qbool cached_allowworldwrites = prog->allowworldwrites;
1170         unsigned int cached_flag = prog->flag;
1171
1172         prvm_vec_t *globals = prog->globals.fp;
1173
1174         calltime = Sys_DirtyTime();
1175
1176         if (!fnum || fnum >= (unsigned int)prog->numfunctions)
1177         {
1178                 if (PRVM_allglobaledict(self))
1179                         PRVM_ED_Print(prog, PRVM_PROG_TO_EDICT(PRVM_allglobaledict(self)), NULL);
1180                 prog->error_cmd("SVVM_ExecuteProgram: %s", errormessage);
1181         }
1182
1183         func = &prog->functions[fnum];
1184
1185         // after executing this function, delete all tempstrings it created
1186         restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1187
1188         prog->trace = prvm_traceqc.integer;
1189
1190         // we know we're done when pr_depth drops to this
1191         exitdepth = prog->depth;
1192
1193 // make a stack frame
1194         st = &prog->statements[PRVM_EnterFunction(prog, func)];
1195         // save the starting statement pointer for profiling
1196         // (when the function exits or jumps, the (st - startst) integer value is
1197         // added to the function's profile counter)
1198         startst = st;
1199         starttm = calltime;
1200         // instead of counting instructions, we count jumps
1201         jumpcount = 0;
1202         // add one to the callcount of this function because otherwise engine-called functions aren't counted
1203         if (prog->xfunction->callcount++ == 0 && (prvm_coverage.integer & 1))
1204                 PRVM_FunctionCoverageEvent(prog, prog->xfunction);
1205
1206 chooseexecprogram:
1207         cachedpr_trace = prog->trace;
1208         if (prog->trace || prog->watch_global_type != ev_void || prog->watch_field_type != ev_void || prog->break_statement >= 0)
1209         {
1210 #define PRVMSLOWINTERPRETER 1
1211                 if (prvm_timeprofiling.integer)
1212                 {
1213 #define PRVMTIMEPROFILING 1
1214 #include "prvm_execprogram.h"
1215 #undef PRVMTIMEPROFILING
1216                 }
1217                 else
1218                 {
1219 #include "prvm_execprogram.h"
1220                 }
1221 #undef PRVMSLOWINTERPRETER
1222         }
1223         else
1224         {
1225                 if (prvm_timeprofiling.integer)
1226                 {
1227 #define PRVMTIMEPROFILING 1
1228 #include "prvm_execprogram.h"
1229 #undef PRVMTIMEPROFILING
1230                 }
1231                 else
1232                 {
1233 #include "prvm_execprogram.h"
1234                 }
1235         }
1236
1237 cleanup:
1238         if (developer_insane.integer && prog->tempstringsbuf.cursize > restorevm_tempstringsbuf_cursize)
1239                 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);
1240         // delete tempstrings created by this function
1241         prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1242
1243         tm = Sys_DirtyTime() - calltime;if (tm < 0 || tm >= 1800) tm = 0;
1244         func->totaltime += tm;
1245
1246         if (prog == SVVM_prog)
1247                 SV_FlushBroadcastMessages();
1248 }