]> git.xonotic.org Git - xonotic/gmqcc.git/blob - exec.c
Executor now executes and traces
[xonotic/gmqcc.git] / exec.c
1 #include "gmqcc.h"
2
3 #define QCVM_EXECUTOR
4
5 #define _MEM_VEC_FUN_APPEND(Tself, Twhat, mem)                       \
6 bool GMQCC_WARN Tself##_##mem##_append(Tself *s, Twhat *p, size_t c) \
7 {                                                                    \
8     Twhat *reall;                                                    \
9     if (s->mem##_count+c >= s->mem##_alloc) {                        \
10         if (!s->mem##_alloc) {                                       \
11             s->mem##_alloc = c < 16 ? 16 : c;                        \
12         } else {                                                     \
13             s->mem##_alloc *= 2;                                     \
14             if (s->mem##_count+c >= s->mem##_alloc) {                \
15                 s->mem##_alloc = s->mem##_count+c;                   \
16             }                                                        \
17         }                                                            \
18         reall = (Twhat*)mem_a(sizeof(Twhat) * s->mem##_alloc);       \
19         if (!reall) {                                                \
20             return false;                                            \
21         }                                                            \
22         memcpy(reall, s->mem, sizeof(Twhat) * s->mem##_count);       \
23         mem_d(s->mem);                                               \
24         s->mem = reall;                                              \
25     }                                                                \
26     memcpy(&s->mem[s->mem##_count], p, c*sizeof(*p));                \
27     s->mem##_count += c;                                             \
28     return true;                                                     \
29 }
30
31 #define _MEM_VEC_FUN_RESIZE(Tself, Twhat, mem)                   \
32 bool GMQCC_WARN Tself##_##mem##_resize(Tself *s, size_t c)       \
33 {                                                                \
34     Twhat *reall;                                                \
35     reall = (Twhat*)mem_a(sizeof(Twhat) * c);                    \
36     if (c > s->mem##_count) {                                    \
37         memcpy(reall, s->mem, sizeof(Twhat) * s->mem##_count);   \
38     } else {                                                     \
39         memcpy(reall, s->mem, sizeof(Twhat) * c);                \
40     }                                                            \
41     s->mem##_count = c;                                          \
42     s->mem##_alloc = c;                                          \
43     return true;                                                 \
44 }
45
46 /* darkplaces has (or will have) a 64 bit prog loader
47  * where the 32 bit qc program is autoconverted on load.
48  * Since we may want to support that as well, let's redefine
49  * float and int here.
50  */
51 typedef float   qcfloat;
52 typedef int32_t qcint;
53
54 typedef union {
55     qcint   _int;
56     qcint    string;
57     qcint    function;
58     qcint    edict;
59     qcfloat _float;
60     qcfloat vector[3];
61     qcint   ivector[3];
62 } qcany;
63
64 typedef char qcfloat_size_is_correct [sizeof(qcfloat) == 4 ?1:-1];
65 typedef char qcint_size_is_correct   [sizeof(int)     == 4 ?1:-1];
66
67 typedef struct {
68     uint32_t offset;
69     uint32_t length;
70 } prog_section;
71
72 typedef struct {
73     uint32_t     version;
74     uint16_t     crc16;
75     uint16_t     skip;
76
77     prog_section statements;
78     prog_section defs;
79     prog_section fields;
80     prog_section functions;
81     prog_section strings;
82     prog_section globals;
83     uint32_t     entfield;
84 } prog_header;
85
86 typedef prog_section_both      prog_def;
87 typedef prog_section_function  prog_function;
88 typedef prog_section_statement prog_statement;
89
90 enum {
91     VMERR_OK,
92     VMERR_TEMPSTRING_ALLOC,
93
94     VMERR_END
95 };
96
97 #define JUMPS_DEFAULT 1000000
98
99 #define VMXF_DEFAULT 0x0000
100 #define VMXF_TRACE   0x0001
101 #define VMXF_PROFILE 0x0002
102
103 struct qc_program_s;
104
105 typedef int (*prog_builtin)(struct qc_program_s *prog);
106
107 typedef struct {
108     qcint          stmt;
109     size_t         localsp;
110     prog_function *function;
111 } qc_exec_stack;
112
113 typedef struct qc_program_s {
114     char           *filename;
115
116     MEM_VECTOR_MAKE(prog_statement, code);
117     MEM_VECTOR_MAKE(prog_def,       defs);
118     MEM_VECTOR_MAKE(prog_def,       fields);
119     MEM_VECTOR_MAKE(prog_function,  functions);
120     MEM_VECTOR_MAKE(char,           strings);
121     MEM_VECTOR_MAKE(qcint,          globals);
122     MEM_VECTOR_MAKE(qcint,          entitydata);
123
124     size_t tempstring_start;
125     size_t tempstring_at;
126
127     qcint  vmerror;
128
129     MEM_VECTOR_MAKE(size_t, profile);
130
131     MEM_VECTOR_MAKE(prog_builtin, builtins);
132
133     /* size_t ip; */
134     qcint  entities;
135     size_t entityfields;
136     bool   allowworldwrites;
137
138     MEM_VECTOR_MAKE(qcint,         localstack);
139     MEM_VECTOR_MAKE(qc_exec_stack, stack);
140     size_t statement;
141
142     int    argc; /* current arg count for debugging */
143 } qc_program;
144 MEM_VEC_FUNCTIONS(qc_program, prog_statement, code)
145 MEM_VEC_FUNCTIONS(qc_program, prog_def,       defs)
146 MEM_VEC_FUNCTIONS(qc_program, prog_def,       fields)
147 MEM_VEC_FUNCTIONS(qc_program, prog_function,  functions)
148 MEM_VEC_FUNCTIONS(qc_program, char,           strings)
149 _MEM_VEC_FUN_APPEND(qc_program, char, strings)
150 _MEM_VEC_FUN_RESIZE(qc_program, char, strings)
151 MEM_VEC_FUNCTIONS(qc_program, qcint,          globals)
152 MEM_VEC_FUNCTIONS(qc_program, qcint,          entitydata)
153
154 MEM_VEC_FUNCTIONS(qc_program,   qcint, localstack)
155 _MEM_VEC_FUN_APPEND(qc_program, qcint, localstack)
156 _MEM_VEC_FUN_RESIZE(qc_program, qcint, localstack)
157 MEM_VEC_FUNCTIONS(qc_program,   qc_exec_stack, stack)
158
159 MEM_VEC_FUNCTIONS(qc_program,   size_t, profile)
160 _MEM_VEC_FUN_RESIZE(qc_program, size_t, profile)
161
162 qc_program* prog_load(const char *filename)
163 {
164     qc_program *prog;
165     prog_header header;
166     FILE *file;
167
168     file = fopen(filename, "rb");
169     if (!file)
170         return NULL;
171
172     if (fread(&header, sizeof(header), 1, file) != 1) {
173         perror("read");
174         fclose(file);
175         return NULL;
176     }
177
178     if (header.version != 6) {
179         printf("header says this is a version %i progs, we need version 6\n",
180                header.version);
181         fclose(file);
182         return NULL;
183     }
184
185     prog = (qc_program*)mem_a(sizeof(qc_program));
186     if (!prog) {
187         fclose(file);
188         printf("failed to allocate program data\n");
189         return NULL;
190     }
191     memset(prog, 0, sizeof(*prog));
192
193     prog->entityfields = header.entfield;
194
195     prog->filename = util_strdup(filename);
196     if (!prog->filename)
197         goto error;
198 #define read_data(hdrvar, progvar, type)                                         \
199     if (fseek(file, header.hdrvar.offset, SEEK_SET) != 0) {                      \
200         perror("fseek");                                                         \
201         goto error;                                                              \
202     }                                                                            \
203     prog->progvar##_alloc = header.hdrvar.length;                                \
204     prog->progvar##_count = header.hdrvar.length;                                \
205     prog->progvar = (type*)mem_a(header.hdrvar.length * sizeof(*prog->progvar)); \
206     if (!prog->progvar)                                                          \
207         goto error;                                                              \
208     if (fread(prog->progvar, sizeof(*prog->progvar), header.hdrvar.length, file) \
209         != header.hdrvar.length) {                                               \
210         perror("read");                                                          \
211         goto error;                                                              \
212     }
213 #define read_data1(x, y) read_data(x, x, y)
214
215     read_data (statements, code, prog_statement);
216     read_data1(defs,             prog_def);
217     read_data1(fields,           prog_def);
218     read_data1(functions,        prog_function);
219     read_data1(strings,          char);
220     read_data1(globals,          qcint);
221
222     fclose(file);
223
224     /* profile counters */
225     if (!qc_program_profile_resize(prog, prog->code_count))
226         goto error;
227
228     /* Add tempstring area */
229     prog->tempstring_start = prog->strings_count;
230     prog->tempstring_at    = prog->strings_count;
231     if (!qc_program_strings_resize(prog, prog->strings_count + 16*1024))
232         goto error;
233
234     return prog;
235
236 error:
237     if (prog->filename)   mem_d(prog->filename);
238     if (prog->code)       mem_d(prog->code);
239     if (prog->defs)       mem_d(prog->defs);
240     if (prog->fields)     mem_d(prog->fields);
241     if (prog->functions)  mem_d(prog->functions);
242     if (prog->strings)    mem_d(prog->strings);
243     if (prog->globals)    mem_d(prog->globals);
244     if (prog->entitydata) mem_d(prog->entitydata);
245     mem_d(prog);
246     return NULL;
247 }
248
249 void prog_delete(qc_program *prog)
250 {
251     if (prog->filename) mem_d(prog->filename);
252     MEM_VECTOR_CLEAR(prog, code);
253     MEM_VECTOR_CLEAR(prog, defs);
254     MEM_VECTOR_CLEAR(prog, fields);
255     MEM_VECTOR_CLEAR(prog, functions);
256     MEM_VECTOR_CLEAR(prog, strings);
257     MEM_VECTOR_CLEAR(prog, globals);
258     MEM_VECTOR_CLEAR(prog, entitydata);
259     MEM_VECTOR_CLEAR(prog, localstack);
260     MEM_VECTOR_CLEAR(prog, stack);
261     MEM_VECTOR_CLEAR(prog, profile);
262     mem_d(prog);
263 }
264
265 /***********************************************************************
266  * VM code
267  */
268
269 char* prog_getstring(qc_program *prog, qcint str)
270 {
271     if (str < 0 || str >= prog->strings_count)
272         return "<<<invalid string>>>";
273     return prog->strings + str;
274 }
275
276 prog_def* prog_entfield(qc_program *prog, qcint off)
277 {
278     size_t i;
279     for (i = 0; i < prog->fields_count; ++i) {
280         if (prog->fields[i].offset == off)
281             return (prog->fields + i);
282     }
283     return NULL;
284 }
285
286 prog_def* prog_getdef(qc_program *prog, qcint off)
287 {
288     size_t i;
289     for (i = 0; i < prog->defs_count; ++i) {
290         if (prog->defs[i].offset == off)
291             return (prog->defs + i);
292     }
293     return NULL;
294 }
295
296 qcany* prog_getedict(qc_program *prog, qcint e)
297 {
298     return (qcany*)(prog->entitydata + (prog->entityfields + e));
299 }
300
301 qcint prog_tempstring(qc_program *prog, const char *_str)
302 {
303     /* we don't access it, but the macro-generated functions don't use
304      * const
305      */
306     char *str = (char*)_str;
307
308     size_t len = strlen(str);
309     size_t at = prog->tempstring_at;
310
311     /* when we reach the end we start over */
312     if (at + len >= prog->strings_count)
313         at = prog->tempstring_start;
314
315     /* when it doesn't fit, reallocate */
316     if (at + len >= prog->strings_count)
317     {
318         prog->strings_count = at;
319         if (!qc_program_strings_append(prog, str, len+1)) {
320             prog->vmerror = VMERR_TEMPSTRING_ALLOC;
321             return 0;
322         }
323         return at;
324     }
325
326     /* when it fits, just copy */
327     memcpy(prog->strings + at, str, len+1);
328     prog->tempstring_at += len+1;
329     return at;
330 }
331
332 static void trace_print_global(qc_program *prog, unsigned int glob, int vtype)
333 {
334     static char spaces[16+1] = "            ";
335     prog_def *def;
336     qcany    *value;
337     int       len;
338
339     if (!glob)
340         return;
341
342     def = prog_getdef(prog, glob);
343     value = (qcany*)(&prog->globals[glob]);
344
345     if (def) {
346         len = printf("[%s] ", prog_getstring(prog, def->name));
347         vtype = def->type;
348     }
349     else
350         len = printf("[#%u] ", glob);
351
352     switch (vtype) {
353         case TYPE_VOID:
354         case TYPE_ENTITY:
355         case TYPE_FIELD:
356         case TYPE_FUNCTION:
357         case TYPE_POINTER:
358             len += printf("%i,", value->_int);
359             break;
360         case TYPE_VECTOR:
361             len += printf("'%g %g %g',", value->vector[0],
362                                          value->vector[1],
363                                          value->vector[2]);
364             break;
365         case TYPE_STRING:
366             len += printf("\"%s\",", prog_getstring(prog, value->string));
367             break;
368         case TYPE_FLOAT:
369         default:
370             len += printf("%g,", value->_float);
371             break;
372     }
373     if (len < 16) {
374         spaces[16-len] = 0;
375         printf(spaces);
376         spaces[16-len] = ' ';
377     }
378 }
379
380 static void prog_print_statement(qc_program *prog, prog_statement *st)
381 {
382     if (st->opcode >= (sizeof(asm_instr)/sizeof(asm_instr[0]))) {
383         printf("<illegal instruction %d>\n", st->opcode);
384         return;
385     }
386     printf("%-12s", asm_instr[st->opcode].m);
387     if (st->opcode >= INSTR_IF &&
388         st->opcode <= INSTR_IFNOT)
389     {
390         trace_print_global(prog, st->o1.u1, TYPE_FLOAT);
391         printf("%d\n", st->o2.s1);
392     }
393     else if (st->opcode >= INSTR_CALL0 &&
394              st->opcode <= INSTR_CALL8)
395     {
396         printf("\n");
397     }
398     else if (st->opcode == INSTR_GOTO)
399     {
400         printf("%i\n", st->o1.s1);
401     }
402     else
403     {
404         int t[3] = { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT };
405         switch (st->opcode)
406         {
407             case INSTR_MUL_FV:
408                 t[1] = t[2] = TYPE_VECTOR;
409                 break;
410             case INSTR_MUL_VF:
411                 t[0] = t[2] = TYPE_VECTOR;
412                 break;
413             case INSTR_MUL_V:
414                 t[0] = t[1] = TYPE_VECTOR;
415                 break;
416             case INSTR_ADD_V:
417             case INSTR_SUB_V:
418             case INSTR_EQ_V:
419             case INSTR_NE_V:
420                 t[0] = t[1] = t[2] = TYPE_VECTOR;
421                 break;
422             case INSTR_EQ_S:
423             case INSTR_NE_S:
424                 t[0] = t[1] = TYPE_STRING;
425                 break;
426             case INSTR_STORE_V:
427                 t[0] = t[1] = TYPE_VECTOR; t[2] = -1;
428                 break;
429             case INSTR_STORE_S:
430                 t[0] = t[1] = TYPE_STRING; t[2] = -1;
431                 break;
432         }
433         if (t[0] >= 0) trace_print_global(prog, st->o1.u1, t[0]);
434         if (t[1] >= 0) trace_print_global(prog, st->o2.u1, t[1]);
435         if (t[2] >= 0) trace_print_global(prog, st->o3.u1, t[2]);
436         printf("\n");
437     }
438 }
439
440 qcint prog_enterfunction(qc_program *prog, prog_function *func)
441 {
442     qc_exec_stack st;
443     prog_function *cur = NULL;
444
445     if (prog->stack_count)
446         cur = prog->stack[prog->stack_count-1].function;
447
448     /* back up locals */
449     st.localsp  = prog->localstack_count;
450     st.stmt     = prog->statement;
451     st.function = func;
452
453     if (cur)
454     {
455         qcint *globals = prog->globals + cur->firstlocal;
456         if (!qc_program_localstack_append(prog, globals, cur->locals))
457         {
458             printf("out of memory\n");
459             exit(1);
460         }
461     }
462
463     if (!qc_program_stack_add(prog, st)) {
464         printf("out of memory\n");
465         exit(1);
466     }
467
468     return func->entry;
469 }
470
471 qcint prog_leavefunction(qc_program *prog)
472 {
473     qc_exec_stack st = prog->stack[prog->stack_count-1];
474     if (!qc_program_stack_remove(prog, prog->stack_count-1)) {
475         printf("out of memory\n");
476         exit(1);
477     }
478
479     if (st.localsp != prog->localstack_count) {
480         if (!qc_program_localstack_resize(prog, st.localsp)) {
481             printf("out of memory\n");
482             exit(1);
483         }
484     }
485
486     return st.stmt;
487 }
488
489 bool prog_exec(qc_program *prog, prog_function *func, size_t flags, long maxjumps)
490 {
491     long jumpcount = 0;
492     prog_statement *st;
493
494     st = prog->code + prog_enterfunction(prog, func);
495     --st;
496     switch (flags)
497     {
498         default:
499         case 0:
500         {
501 #define QCVM_PROFILE 0
502 #define QCVM_TRACE   0
503 #           include "qcvm_execprogram.h"
504             break;
505         }
506         case (VMXF_TRACE):
507         {
508 #define QCVM_PROFILE 0
509 #define QCVM_TRACE   1
510 #           include "qcvm_execprogram.h"
511             break;
512         }
513         case (VMXF_PROFILE):
514         {
515 #define QCVM_PROFILE 1
516 #define QCVM_TRACE   0
517 #           include "qcvm_execprogram.h"
518             break;
519         }
520         case (VMXF_TRACE|VMXF_PROFILE):
521         {
522 #define QCVM_PROFILE 1
523 #define QCVM_TRACE   1
524 #           include "qcvm_execprogram.h"
525             break;
526         }
527     };
528
529 cleanup:
530     prog->localstack_count = 0;
531     prog->stack_count = 0;
532     return true;
533 }
534
535 /***********************************************************************
536  * main for when building the standalone executor
537  */
538
539 #if defined(QCVM_EXECUTOR)
540 int main(int argc, char **argv)
541 {
542     size_t      i;
543     qcint       fnmain = -1;
544     qc_program *prog;
545
546     if (argc != 2) {
547         printf("usage: %s file\n", argv[0]);
548         exit(1);
549     }
550
551     prog = prog_load(argv[1]);
552     if (!prog) {
553         printf("failed to load program '%s'\n", argv[1]);
554         exit(1);
555     }
556
557     for (i = 1; i < prog->functions_count; ++i) {
558         const char *name = prog_getstring(prog, prog->functions[i].name);
559         printf("Found function: %s\n", name);
560         if (!strcmp(name, "main"))
561             fnmain = (qcint)i;
562     }
563     if (fnmain > 0)
564     {
565         prog_exec(prog, &prog->functions[fnmain], VMXF_TRACE, JUMPS_DEFAULT);
566     }
567     else
568         printf("No main function found\n");
569
570     prog_delete(prog);
571     return 0;
572 }
573 #endif