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