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