]> git.xonotic.org Git - xonotic/darkplaces.git/blob - prvm_exec.c
PRVM: don't break csqc when menuqc has a problem
[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(prvm_prog_t *prog)
719 {
720         char vabuf[1024];
721         int outfd = sys.outfd;
722
723         if (prog == NULL)
724                 return;
725         if (!prog->loaded)
726                 return;
727
728         // set output to stderr
729         sys.outfd = fileno(stderr);
730
731         PRVM_serverfunction(SV_Shutdown) = 0; // don't call SV_Shutdown on crash
732
733         if( prog->depth > 0 )
734         {
735                 Con_Printf("QuakeC crash report for %s:\n", prog->name);
736                 PRVM_PrintState(prog, 0);
737         }
738
739         if(prvm_errordump.integer)
740         {
741                 // make a savegame
742                 SV_Savegame_to(prog, va(vabuf, sizeof(vabuf), "crash-%s.dmp", prog->name));
743         }
744
745         // dump the stack so host_error can shutdown functions
746         prog->depth = 0;
747         prog->localstack_used = 0;
748
749         // delete all tempstrings (FIXME: is this safe in VM->engine->VM recursion?)
750         prog->tempstringsbuf.cursize = 0;
751
752         // reset the prog pointer
753         prog = NULL;
754
755         // restore configured outfd
756         sys.outfd = outfd;
757 }
758
759 /*
760 ============================================================================
761 PRVM_ExecuteProgram
762
763 The interpretation main loop
764 ============================================================================
765 */
766
767 /*
768 ====================
769 PRVM_EnterFunction
770
771 Returns the new program statement counter
772 ====================
773 */
774 static int PRVM_EnterFunction (prvm_prog_t *prog, mfunction_t *f)
775 {
776         int             i, j, c, o;
777
778         if (!f)
779                 prog->error_cmd("PRVM_EnterFunction: NULL function in %s", prog->name);
780
781         prog->stack[prog->depth].s = prog->xstatement;
782         prog->stack[prog->depth].f = prog->xfunction;
783         prog->stack[prog->depth].profile_acc = -f->profile;
784         prog->stack[prog->depth].tprofile_acc = -f->tprofile + -f->tbprofile;
785         prog->stack[prog->depth].builtinsprofile_acc = -f->builtinsprofile;
786         prog->depth++;
787         if (prog->depth >=PRVM_MAX_STACK_DEPTH)
788                 prog->error_cmd("stack overflow");
789
790 // save off any locals that the new function steps on
791         c = f->locals;
792         if (prog->localstack_used + c > PRVM_LOCALSTACK_SIZE)
793                 prog->error_cmd("PRVM_ExecuteProgram: locals stack overflow in %s", prog->name);
794
795         for (i=0 ; i < c ; i++)
796                 prog->localstack[prog->localstack_used+i] = prog->globals.ip[f->parm_start + i];
797         prog->localstack_used += c;
798
799 // copy parameters
800         o = f->parm_start;
801         for (i=0 ; i<f->numparms ; i++)
802         {
803                 for (j=0 ; j<f->parm_size[i] ; j++)
804                 {
805                         prog->globals.ip[o] = prog->globals.ip[OFS_PARM0+i*3+j];
806                         o++;
807                 }
808         }
809
810         ++f->recursion;
811         prog->xfunction = f;
812         return f->first_statement - 1;  // offset the s++
813 }
814
815 /*
816 ====================
817 PRVM_LeaveFunction
818 ====================
819 */
820 static int PRVM_LeaveFunction (prvm_prog_t *prog)
821 {
822         int             i, c;
823         mfunction_t *f;
824
825         if (prog->depth <= 0)
826                 prog->error_cmd("prog stack underflow in %s", prog->name);
827
828         if (!prog->xfunction)
829                 prog->error_cmd("PR_LeaveFunction: NULL function in %s", prog->name);
830 // restore locals from the stack
831         c = prog->xfunction->locals;
832         prog->localstack_used -= c;
833         if (prog->localstack_used < 0)
834                 prog->error_cmd("PRVM_ExecuteProgram: locals stack underflow in %s", prog->name);
835
836         for (i=0 ; i < c ; i++)
837                 prog->globals.ip[prog->xfunction->parm_start + i] = prog->localstack[prog->localstack_used+i];
838
839 // up stack
840         prog->depth--;
841         f = prog->xfunction;
842         --f->recursion;
843         prog->xfunction = prog->stack[prog->depth].f;
844         prog->stack[prog->depth].profile_acc += f->profile;
845         prog->stack[prog->depth].tprofile_acc += f->tprofile + f->tbprofile;
846         prog->stack[prog->depth].builtinsprofile_acc += f->builtinsprofile;
847         if(prog->depth > 0)
848         {
849                 prog->stack[prog->depth-1].profile_acc += prog->stack[prog->depth].profile_acc;
850                 prog->stack[prog->depth-1].tprofile_acc += prog->stack[prog->depth].tprofile_acc;
851                 prog->stack[prog->depth-1].builtinsprofile_acc += prog->stack[prog->depth].builtinsprofile_acc;
852         }
853         if(!f->recursion)
854         {
855                 // if f is already on the call stack...
856                 // we cannot add this profile data to it now
857                 // or we would add it more than once
858                 // so, let's only add to the function's profile if it is the outermost call
859                 f->profile_total += prog->stack[prog->depth].profile_acc;
860                 f->tprofile_total += prog->stack[prog->depth].tprofile_acc;
861                 f->builtinsprofile_total += prog->stack[prog->depth].builtinsprofile_acc;
862         }
863         
864         return prog->stack[prog->depth].s;
865 }
866
867 void PRVM_Init_Exec(prvm_prog_t *prog)
868 {
869         // dump the stack
870         prog->depth = 0;
871         prog->localstack_used = 0;
872         // reset the string table
873         // nothing here yet
874 }
875
876 /*
877 ==================
878 Coverage
879 ==================
880 */
881 // Note: in these two calls, prog->xfunction is assumed to be sane.
882 static const char *PRVM_WhereAmI(char *buf, size_t bufsize, prvm_prog_t *prog, mfunction_t *func, int statement)
883 {
884         if (prog->statement_linenums)
885         {
886                 if (prog->statement_columnnums)
887                         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);
888                 else
889                         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);
890         }
891         else
892                 return va(buf, bufsize, "%s(%s, %i)", PRVM_GetString(prog, func->s_file), PRVM_GetString(prog, func->s_name), statement - func->first_statement);
893 }
894 static void PRVM_FunctionCoverageEvent(prvm_prog_t *prog, mfunction_t *func)
895 {
896         ++prog->functions_covered;
897         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);
898 }
899 void PRVM_ExplicitCoverageEvent(prvm_prog_t *prog, mfunction_t *func, int statement)
900 {
901         char vabuf[128];
902         ++prog->explicit_covered;
903         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);
904 }
905 static void PRVM_StatementCoverageEvent(prvm_prog_t *prog, mfunction_t *func, int statement)
906 {
907         char vabuf[128];
908         ++prog->statements_covered;
909         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);
910 }
911
912 #if defined (__GNUC__) || (__clang__) || (__TINYC__)
913 #  ifndef CONFIG_PEDANTIC
914 #  define HAVE_COMPUTED_GOTOS 1
915 #  endif
916 #endif
917
918 #define OPA ((prvm_eval_t *)&globals[st->operand[0]])
919 #define OPB ((prvm_eval_t *)&globals[st->operand[1]])
920 #define OPC ((prvm_eval_t *)&globals[st->operand[2]])
921 extern cvar_t prvm_traceqc;
922 extern cvar_t prvm_statementprofiling;
923 extern qbool prvm_runawaycheck;
924
925 #ifdef PROFILING
926 #ifdef CONFIG_MENU
927 /*
928 ====================
929 MVM_ExecuteProgram
930 ====================
931 */
932 void MVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
933 {
934         mstatement_t    *st, *startst;
935         mfunction_t             *func, *enterfunc;
936         prvm_edict_t    *ed;
937         prvm_eval_t     *ptr;
938         int             jumpcount, cachedpr_trace, exitdepth;
939         int             restorevm_tempstringsbuf_cursize;
940         double  calltime;
941         double tm, starttm;
942         prvm_vec_t tempfloat;
943         // these may become out of date when a builtin is called, and are updated accordingly
944         prvm_vec_t *cached_edictsfields = prog->edictsfields.fp;
945         unsigned int cached_entityfields = prog->entityfields;
946         unsigned int cached_entityfields_3 = prog->entityfields - 3;
947         unsigned int cached_entityfieldsarea = prog->entityfieldsarea;
948         unsigned int cached_entityfieldsarea_entityfields = prog->entityfieldsarea - prog->entityfields;
949         unsigned int cached_entityfieldsarea_3 = prog->entityfieldsarea - 3;
950         unsigned int cached_entityfieldsarea_entityfields_3 = prog->entityfieldsarea - prog->entityfields - 3;
951         unsigned int cached_max_edicts = prog->max_edicts;
952         // these do not change
953         mstatement_t *cached_statements = prog->statements;
954         qbool cached_allowworldwrites = prog->allowworldwrites;
955         unsigned int cached_flag = prog->flag;
956
957         prvm_vec_t *globals = prog->globals.fp;
958
959         calltime = Sys_DirtyTime();
960
961         if (!fnum || fnum >= (unsigned int)prog->numfunctions)
962         {
963                 if (PRVM_allglobaledict(self))
964                         PRVM_ED_Print(prog, PRVM_PROG_TO_EDICT(PRVM_allglobaledict(self)), NULL);
965                 prog->error_cmd("MVM_ExecuteProgram: %s", errormessage);
966         }
967
968         func = &prog->functions[fnum];
969
970         // after executing this function, delete all tempstrings it created
971         restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
972
973         prog->trace = prvm_traceqc.integer;
974
975         // we know we're done when pr_depth drops to this
976         exitdepth = prog->depth;
977
978 // make a stack frame
979         st = &prog->statements[PRVM_EnterFunction(prog, func)];
980         // save the starting statement pointer for profiling
981         // (when the function exits or jumps, the (st - startst) integer value is
982         // added to the function's profile counter)
983         startst = st;
984         starttm = calltime;
985         // instead of counting instructions, we count jumps
986         jumpcount = 0;
987         // add one to the callcount of this function because otherwise engine-called functions aren't counted
988         if (prog->xfunction->callcount++ == 0 && (prvm_coverage.integer & 1))
989                 PRVM_FunctionCoverageEvent(prog, prog->xfunction);
990
991 chooseexecprogram:
992         cachedpr_trace = prog->trace;
993         if (prog->trace || prog->watch_global_type != ev_void || prog->watch_field_type != ev_void || prog->break_statement >= 0)
994         {
995 #define PRVMSLOWINTERPRETER 1
996                 if (prvm_timeprofiling.integer)
997                 {
998 #define PRVMTIMEPROFILING 1
999 #include "prvm_execprogram.h"
1000 #undef PRVMTIMEPROFILING
1001                 }
1002                 else
1003                 {
1004 #include "prvm_execprogram.h"
1005                 }
1006 #undef PRVMSLOWINTERPRETER
1007         }
1008         else
1009         {
1010                 if (prvm_timeprofiling.integer)
1011                 {
1012 #define PRVMTIMEPROFILING 1
1013 #include "prvm_execprogram.h"
1014 #undef PRVMTIMEPROFILING
1015                 }
1016                 else
1017                 {
1018 #include "prvm_execprogram.h"
1019                 }
1020         }
1021
1022 cleanup:
1023         if (developer_insane.integer && prog->tempstringsbuf.cursize > restorevm_tempstringsbuf_cursize)
1024                 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);
1025         // delete tempstrings created by this function
1026         prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1027
1028         tm = Sys_DirtyTime() - calltime;if (tm < 0 || tm >= 1800) tm = 0;
1029         func->totaltime += tm;
1030
1031         if (prog == SVVM_prog)
1032                 SV_FlushBroadcastMessages();
1033 }
1034 #endif
1035
1036 /*
1037 ====================
1038 CLVM_ExecuteProgram
1039 ====================
1040 */
1041 void CLVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
1042 {
1043         mstatement_t    *st, *startst;
1044         mfunction_t             *func, *enterfunc;
1045         prvm_edict_t    *ed;
1046         prvm_eval_t     *ptr;
1047         int             jumpcount, cachedpr_trace, exitdepth;
1048         int             restorevm_tempstringsbuf_cursize;
1049         double  calltime;
1050         double tm, starttm;
1051         prvm_vec_t tempfloat;
1052         // these may become out of date when a builtin is called, and are updated accordingly
1053         prvm_vec_t *cached_edictsfields = prog->edictsfields.fp;
1054         unsigned int cached_entityfields = prog->entityfields;
1055         unsigned int cached_entityfields_3 = prog->entityfields - 3;
1056         unsigned int cached_entityfieldsarea = prog->entityfieldsarea;
1057         unsigned int cached_entityfieldsarea_entityfields = prog->entityfieldsarea - prog->entityfields;
1058         unsigned int cached_entityfieldsarea_3 = prog->entityfieldsarea - 3;
1059         unsigned int cached_entityfieldsarea_entityfields_3 = prog->entityfieldsarea - prog->entityfields - 3;
1060         unsigned int cached_max_edicts = prog->max_edicts;
1061         // these do not change
1062         mstatement_t *cached_statements = prog->statements;
1063         qbool cached_allowworldwrites = prog->allowworldwrites;
1064         unsigned int cached_flag = prog->flag;
1065
1066         prvm_vec_t *globals = prog->globals.fp;
1067
1068         calltime = Sys_DirtyTime();
1069
1070         if (!fnum || fnum >= (unsigned int)prog->numfunctions)
1071         {
1072                 if (PRVM_allglobaledict(self))
1073                         PRVM_ED_Print(prog, PRVM_PROG_TO_EDICT(PRVM_allglobaledict(self)), NULL);
1074                 prog->error_cmd("CLVM_ExecuteProgram: %s", errormessage);
1075         }
1076
1077         func = &prog->functions[fnum];
1078
1079         // after executing this function, delete all tempstrings it created
1080         restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1081
1082         prog->trace = prvm_traceqc.integer;
1083
1084         // we know we're done when pr_depth drops to this
1085         exitdepth = prog->depth;
1086
1087 // make a stack frame
1088         st = &prog->statements[PRVM_EnterFunction(prog, func)];
1089         // save the starting statement pointer for profiling
1090         // (when the function exits or jumps, the (st - startst) integer value is
1091         // added to the function's profile counter)
1092         startst = st;
1093         starttm = calltime;
1094         // instead of counting instructions, we count jumps
1095         jumpcount = 0;
1096         // add one to the callcount of this function because otherwise engine-called functions aren't counted
1097         if (prog->xfunction->callcount++ == 0 && (prvm_coverage.integer & 1))
1098                 PRVM_FunctionCoverageEvent(prog, prog->xfunction);
1099
1100 chooseexecprogram:
1101         cachedpr_trace = prog->trace;
1102         if (prog->trace || prog->watch_global_type != ev_void || prog->watch_field_type != ev_void || prog->break_statement >= 0)
1103         {
1104 #define PRVMSLOWINTERPRETER 1
1105                 if (prvm_timeprofiling.integer)
1106                 {
1107 #define PRVMTIMEPROFILING 1
1108 #include "prvm_execprogram.h"
1109 #undef PRVMTIMEPROFILING
1110                 }
1111                 else
1112                 {
1113 #include "prvm_execprogram.h"
1114                 }
1115 #undef PRVMSLOWINTERPRETER
1116         }
1117         else
1118         {
1119                 if (prvm_timeprofiling.integer)
1120                 {
1121 #define PRVMTIMEPROFILING 1
1122 #include "prvm_execprogram.h"
1123 #undef PRVMTIMEPROFILING
1124                 }
1125                 else
1126                 {
1127 #include "prvm_execprogram.h"
1128                 }
1129         }
1130
1131 cleanup:
1132         if (developer_insane.integer && prog->tempstringsbuf.cursize > restorevm_tempstringsbuf_cursize)
1133                 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);
1134         // delete tempstrings created by this function
1135         prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1136
1137         tm = Sys_DirtyTime() - calltime;if (tm < 0 || tm >= 1800) tm = 0;
1138         func->totaltime += tm;
1139
1140         if (prog == SVVM_prog)
1141                 SV_FlushBroadcastMessages();
1142 }
1143 #endif
1144
1145 /*
1146 ====================
1147 SVVM_ExecuteProgram
1148 ====================
1149 */
1150 #ifdef PROFILING
1151 void SVVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
1152 #else
1153 void PRVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
1154 #endif
1155 {
1156         mstatement_t    *st, *startst;
1157         mfunction_t             *func, *enterfunc;
1158         prvm_edict_t    *ed;
1159         prvm_eval_t     *ptr;
1160         int             jumpcount, cachedpr_trace, exitdepth;
1161         int             restorevm_tempstringsbuf_cursize;
1162         double  calltime;
1163         double tm, starttm;
1164         prvm_vec_t tempfloat;
1165         // these may become out of date when a builtin is called, and are updated accordingly
1166         prvm_vec_t *cached_edictsfields = prog->edictsfields.fp;
1167         unsigned int cached_entityfields = prog->entityfields;
1168         unsigned int cached_entityfields_3 = prog->entityfields - 3;
1169         unsigned int cached_entityfieldsarea = prog->entityfieldsarea;
1170         unsigned int cached_entityfieldsarea_entityfields = prog->entityfieldsarea - prog->entityfields;
1171         unsigned int cached_entityfieldsarea_3 = prog->entityfieldsarea - 3;
1172         unsigned int cached_entityfieldsarea_entityfields_3 = prog->entityfieldsarea - prog->entityfields - 3;
1173         unsigned int cached_max_edicts = prog->max_edicts;
1174         // these do not change
1175         mstatement_t *cached_statements = prog->statements;
1176         qbool cached_allowworldwrites = prog->allowworldwrites;
1177         unsigned int cached_flag = prog->flag;
1178
1179         prvm_vec_t *globals = prog->globals.fp;
1180
1181         calltime = Sys_DirtyTime();
1182
1183         if (!fnum || fnum >= (unsigned int)prog->numfunctions)
1184         {
1185                 if (PRVM_allglobaledict(self))
1186                         PRVM_ED_Print(prog, PRVM_PROG_TO_EDICT(PRVM_allglobaledict(self)), NULL);
1187                 prog->error_cmd("SVVM_ExecuteProgram: %s", errormessage);
1188         }
1189
1190         func = &prog->functions[fnum];
1191
1192         // after executing this function, delete all tempstrings it created
1193         restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1194
1195         prog->trace = prvm_traceqc.integer;
1196
1197         // we know we're done when pr_depth drops to this
1198         exitdepth = prog->depth;
1199
1200 // make a stack frame
1201         st = &prog->statements[PRVM_EnterFunction(prog, func)];
1202         // save the starting statement pointer for profiling
1203         // (when the function exits or jumps, the (st - startst) integer value is
1204         // added to the function's profile counter)
1205         startst = st;
1206         starttm = calltime;
1207         // instead of counting instructions, we count jumps
1208         jumpcount = 0;
1209         // add one to the callcount of this function because otherwise engine-called functions aren't counted
1210         if (prog->xfunction->callcount++ == 0 && (prvm_coverage.integer & 1))
1211                 PRVM_FunctionCoverageEvent(prog, prog->xfunction);
1212
1213 chooseexecprogram:
1214         cachedpr_trace = prog->trace;
1215         if (prog->trace || prog->watch_global_type != ev_void || prog->watch_field_type != ev_void || prog->break_statement >= 0)
1216         {
1217 #define PRVMSLOWINTERPRETER 1
1218                 if (prvm_timeprofiling.integer)
1219                 {
1220 #define PRVMTIMEPROFILING 1
1221 #include "prvm_execprogram.h"
1222 #undef PRVMTIMEPROFILING
1223                 }
1224                 else
1225                 {
1226 #include "prvm_execprogram.h"
1227                 }
1228 #undef PRVMSLOWINTERPRETER
1229         }
1230         else
1231         {
1232                 if (prvm_timeprofiling.integer)
1233                 {
1234 #define PRVMTIMEPROFILING 1
1235 #include "prvm_execprogram.h"
1236 #undef PRVMTIMEPROFILING
1237                 }
1238                 else
1239                 {
1240 #include "prvm_execprogram.h"
1241                 }
1242         }
1243
1244 cleanup:
1245         if (developer_insane.integer && prog->tempstringsbuf.cursize > restorevm_tempstringsbuf_cursize)
1246                 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);
1247         // delete tempstrings created by this function
1248         prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1249
1250         tm = Sys_DirtyTime() - calltime;if (tm < 0 || tm >= 1800) tm = 0;
1251         func->totaltime += tm;
1252
1253         if (prog == SVVM_prog)
1254                 SV_FlushBroadcastMessages();
1255 }