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