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