]> 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 int print_escaped_string(const char *str)
215 {
216     int len = 2;
217     putchar('"');
218     while (*str) {
219         switch (*str) {
220             case '\a': len += 2; putchar('\\'); putchar('a'); break;
221             case '\b': len += 2; putchar('\\'); putchar('b'); break;
222             case '\r': len += 2; putchar('\\'); putchar('r'); break;
223             case '\n': len += 2; putchar('\\'); putchar('n'); break;
224             case '\t': len += 2; putchar('\\'); putchar('t'); break;
225             case '\f': len += 2; putchar('\\'); putchar('f'); break;
226             case '\v': len += 2; putchar('\\'); putchar('v'); break;
227             case '\\': len += 2; putchar('\\'); putchar('\\'); break;
228             case '"':  len += 2; putchar('\\'); putchar('"'); break;
229             default:
230                 ++len;
231                 putchar(*str);
232                 break;
233         }
234         ++str;
235     }
236     putchar('"');
237     return len;
238 }
239
240 static void trace_print_global(qc_program *prog, unsigned int glob, int vtype)
241 {
242     static char spaces[16+1] = "            ";
243     prog_section_def *def;
244     qcany    *value;
245     int       len;
246
247     if (!glob)
248         return;
249
250     def = prog_getdef(prog, glob);
251     value = (qcany*)(&prog->globals[glob]);
252
253     if (def) {
254         len = printf("[%s] ", prog_getstring(prog, def->name));
255         vtype = def->type;
256     }
257     else
258         len = printf("[@%u] ", glob);
259
260     switch (vtype) {
261         case TYPE_VOID:
262         case TYPE_ENTITY:
263         case TYPE_FIELD:
264         case TYPE_FUNCTION:
265         case TYPE_POINTER:
266             len += printf("%i,", value->_int);
267             break;
268         case TYPE_VECTOR:
269             len += printf("'%g %g %g',", value->vector[0],
270                                          value->vector[1],
271                                          value->vector[2]);
272             break;
273         case TYPE_STRING:
274             len += print_escaped_string(prog_getstring(prog, value->string));
275             /* len += printf("\"%s\",", prog_getstring(prog, value->string)); */
276             break;
277         case TYPE_FLOAT:
278         default:
279             len += printf("%g,", value->_float);
280             break;
281     }
282     if (len < 16) {
283         spaces[16-len] = 0;
284         printf(spaces);
285         spaces[16-len] = ' ';
286     }
287 }
288
289 static void prog_print_statement(qc_program *prog, prog_section_statement *st)
290 {
291     if (st->opcode >= (sizeof(asm_instr)/sizeof(asm_instr[0]))) {
292         printf("<illegal instruction %d>\n", st->opcode);
293         return;
294     }
295     printf(" <> %-12s", asm_instr[st->opcode].m);
296     if (st->opcode >= INSTR_IF &&
297         st->opcode <= INSTR_IFNOT)
298     {
299         trace_print_global(prog, st->o1.u1, TYPE_FLOAT);
300         printf("%d\n", st->o2.s1);
301     }
302     else if (st->opcode >= INSTR_CALL0 &&
303              st->opcode <= INSTR_CALL8)
304     {
305         printf("\n");
306     }
307     else if (st->opcode == INSTR_GOTO)
308     {
309         printf("%i\n", st->o1.s1);
310     }
311     else
312     {
313         int t[3] = { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT };
314         switch (st->opcode)
315         {
316             case INSTR_MUL_FV:
317                 t[1] = t[2] = TYPE_VECTOR;
318                 break;
319             case INSTR_MUL_VF:
320                 t[0] = t[2] = TYPE_VECTOR;
321                 break;
322             case INSTR_MUL_V:
323                 t[0] = t[1] = TYPE_VECTOR;
324                 break;
325             case INSTR_ADD_V:
326             case INSTR_SUB_V:
327             case INSTR_EQ_V:
328             case INSTR_NE_V:
329                 t[0] = t[1] = t[2] = TYPE_VECTOR;
330                 break;
331             case INSTR_EQ_S:
332             case INSTR_NE_S:
333                 t[0] = t[1] = TYPE_STRING;
334                 break;
335             case INSTR_STORE_V:
336                 t[0] = t[1] = TYPE_VECTOR; t[2] = -1;
337                 break;
338             case INSTR_STORE_S:
339                 t[0] = t[1] = TYPE_STRING; t[2] = -1;
340                 break;
341         }
342         if (t[0] >= 0) trace_print_global(prog, st->o1.u1, t[0]);
343         if (t[1] >= 0) trace_print_global(prog, st->o2.u1, t[1]);
344         if (t[2] >= 0) trace_print_global(prog, st->o3.u1, t[2]);
345         printf("\n");
346     }
347 }
348
349 static qcint prog_enterfunction(qc_program *prog, prog_section_function *func)
350 {
351     qc_exec_stack st;
352     size_t p, parampos;
353
354     /* back up locals */
355     st.localsp  = prog->localstack_count;
356     st.stmt     = prog->statement;
357     st.function = func;
358
359 #ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS
360     if (prog->stack_count)
361     {
362         prog_section_function *cur;
363         cur = prog->stack[prog->stack_count-1].function;
364         if (cur)
365         {
366             qcint *globals = prog->globals + cur->firstlocal;
367             if (!qc_program_localstack_append(prog, globals, cur->locals))
368             {
369                 printf("out of memory\n");
370                 exit(1);
371             }
372         }
373     }
374 #else
375     {
376         qcint *globals = prog->globals + func->firstlocal;
377         if (!qc_program_localstack_append(prog, globals, func->locals))
378         {
379             printf("out of memory\n");
380             exit(1);
381         }
382     }
383 #endif
384
385     /* copy parameters */
386     parampos = func->firstlocal;
387     for (p = 0; p < func->nargs; ++p)
388     {
389         size_t s;
390         for (s = 0; s < func->argsize[p]; ++s) {
391             prog->globals[parampos] = prog->globals[OFS_PARM0 + 3*p + s];
392             ++parampos;
393         }
394     }
395
396     if (!qc_program_stack_add(prog, st)) {
397         printf("out of memory\n");
398         exit(1);
399     }
400
401     return func->entry;
402 }
403
404 static qcint prog_leavefunction(qc_program *prog)
405 {
406     prog_section_function *prev = NULL;
407     size_t oldsp;
408
409     qc_exec_stack st = prog->stack[prog->stack_count-1];
410
411 #ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS
412     if (prog->stack_count > 1) {
413         prev  = prog->stack[prog->stack_count-2].function;
414         oldsp = prog->stack[prog->stack_count-2].localsp;
415     }
416 #else
417     prev  = prog->stack[prog->stack_count-1].function;
418     oldsp = prog->stack[prog->stack_count-1].localsp;
419 #endif
420     if (prev) {
421         qcint *globals = prog->globals + prev->firstlocal;
422         memcpy(globals, prog->localstack + oldsp, prev->locals);
423         if (!qc_program_localstack_resize(prog, oldsp)) {
424             printf("out of memory\n");
425             exit(1);
426         }
427     }
428
429     if (!qc_program_stack_remove(prog, prog->stack_count-1)) {
430         printf("out of memory\n");
431         exit(1);
432     }
433
434     return st.stmt;
435 }
436
437 bool prog_exec(qc_program *prog, prog_section_function *func, size_t flags, long maxjumps)
438 {
439     long jumpcount = 0;
440     prog_section_statement *st;
441
442     st = prog->code + prog_enterfunction(prog, func);
443     --st;
444     switch (flags)
445     {
446         default:
447         case 0:
448         {
449 #define QCVM_PROFILE 0
450 #define QCVM_TRACE   0
451 #           include "execloop.h"
452             break;
453         }
454         case (VMXF_TRACE):
455         {
456 #define QCVM_PROFILE 0
457 #define QCVM_TRACE   1
458 #           include "execloop.h"
459             break;
460         }
461         case (VMXF_PROFILE):
462         {
463 #define QCVM_PROFILE 1
464 #define QCVM_TRACE   0
465 #           include "execloop.h"
466             break;
467         }
468         case (VMXF_TRACE|VMXF_PROFILE):
469         {
470 #define QCVM_PROFILE 1
471 #define QCVM_TRACE   1
472 #           include "execloop.h"
473             break;
474         }
475     };
476
477 cleanup:
478     prog->localstack_count = 0;
479     prog->stack_count = 0;
480     return true;
481 }
482
483 /***********************************************************************
484  * main for when building the standalone executor
485  */
486
487 #if defined(QCVM_EXECUTOR)
488 bool        opts_debug    = false;
489 bool        opts_memchk   = false;
490
491 static int qc_print(qc_program *prog)
492 {
493     qcany *str = (qcany*)(prog->globals + OFS_PARM0);
494     printf("%s", prog_getstring(prog, str->string));
495     return 0;
496 }
497
498 static prog_builtin qc_builtins[] = {
499     NULL,
500     &qc_print
501 };
502 static size_t qc_builtins_count = sizeof(qc_builtins) / sizeof(qc_builtins[0]);
503
504 int main(int argc, char **argv)
505 {
506     size_t      i;
507     qcint       fnmain = -1;
508     qc_program *prog;
509
510     if (argc != 2) {
511         printf("usage: %s file\n", argv[0]);
512         exit(1);
513     }
514
515     prog = prog_load(argv[1]);
516     if (!prog) {
517         printf("failed to load program '%s'\n", argv[1]);
518         exit(1);
519     }
520
521     prog->builtins       = qc_builtins;
522     prog->builtins_count = qc_builtins_count;
523     prog->builtins_alloc = 0;
524
525     for (i = 1; i < prog->functions_count; ++i) {
526         const char *name = prog_getstring(prog, prog->functions[i].name);
527         printf("Found function: %s\n", name);
528         if (!strcmp(name, "main"))
529             fnmain = (qcint)i;
530     }
531     if (fnmain > 0)
532     {
533         prog_exec(prog, &prog->functions[fnmain], VMXF_TRACE, VM_JUMPS_DEFAULT);
534     }
535     else
536         printf("No main function found\n");
537
538     prog_delete(prog);
539     return 0;
540 }
541 #endif