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