111
|
1 /* A simple stack-based virtual machine to demonstrate
|
|
2 JIT-compilation.
|
145
|
3 Copyright (C) 2014-2020 Free Software Foundation, Inc.
|
111
|
4
|
|
5 This file is part of GCC.
|
|
6
|
|
7 GCC is free software; you can redistribute it and/or modify it
|
|
8 under the terms of the GNU General Public License as published by
|
|
9 the Free Software Foundation; either version 3, or (at your option)
|
|
10 any later version.
|
|
11
|
|
12 GCC is distributed in the hope that it will be useful, but
|
|
13 WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
15 General Public License for more details.
|
|
16
|
|
17 You should have received a copy of the GNU General Public License
|
|
18 along with GCC; see the file COPYING3. If not see
|
|
19 <http://www.gnu.org/licenses/>. */
|
|
20
|
|
21 #include <assert.h>
|
|
22 #include <errno.h>
|
|
23 #include <stdio.h>
|
|
24 #include <stdlib.h>
|
|
25 #include <string.h>
|
|
26
|
|
27 #include <dejagnu.h>
|
|
28
|
|
29 #include <libgccjit.h>
|
|
30
|
|
31 /* Typedefs. */
|
|
32 typedef struct toyvm_op toyvm_op;
|
|
33 typedef struct toyvm_function toyvm_function;
|
|
34 typedef struct toyvm_frame toyvm_frame;
|
|
35 typedef struct compilation_state compilation_state;
|
|
36 typedef struct toyvm_compiled_function toyvm_compiled_function;
|
|
37
|
|
38 /* Functions are compiled to this function ptr type. */
|
|
39 typedef int (*toyvm_compiled_code) (int);
|
|
40
|
|
41 enum opcode {
|
|
42 /* Ops taking no operand. */
|
|
43 DUP,
|
|
44 ROT,
|
|
45 BINARY_ADD,
|
|
46 BINARY_SUBTRACT,
|
|
47 BINARY_MULT,
|
|
48 BINARY_COMPARE_LT,
|
|
49 RECURSE,
|
|
50 RETURN,
|
|
51
|
|
52 /* Ops taking an operand. */
|
|
53 PUSH_CONST,
|
|
54 JUMP_ABS_IF_TRUE
|
|
55 };
|
|
56
|
|
57 #define FIRST_UNARY_OPCODE (PUSH_CONST)
|
|
58
|
|
59 const char * const opcode_names[] = {
|
|
60 "DUP",
|
|
61 "ROT",
|
|
62 "BINARY_ADD",
|
|
63 "BINARY_SUBTRACT",
|
|
64 "BINARY_MULT",
|
|
65 "BINARY_COMPARE_LT",
|
|
66 "RECURSE",
|
|
67 "RETURN",
|
|
68
|
|
69 "PUSH_CONST",
|
|
70 "JUMP_ABS_IF_TRUE",
|
|
71 };
|
|
72
|
|
73 struct toyvm_op
|
|
74 {
|
|
75 /* Which operation. */
|
|
76 enum opcode op_opcode;
|
|
77
|
|
78 /* Some opcodes take an argument. */
|
|
79 int op_operand;
|
|
80
|
|
81 /* The line number of the operation within the source file. */
|
|
82 int op_linenum;
|
|
83 };
|
|
84
|
|
85 #define MAX_OPS (64)
|
|
86
|
|
87 struct toyvm_function
|
|
88 {
|
|
89 const char *fn_filename;
|
|
90 int fn_num_ops;
|
|
91 toyvm_op fn_ops[MAX_OPS];
|
|
92 };
|
|
93
|
|
94 #define MAX_STACK_DEPTH (8)
|
|
95
|
|
96 struct toyvm_frame
|
|
97 {
|
|
98 toyvm_function *frm_function;
|
|
99 int frm_pc;
|
|
100 int frm_stack[MAX_STACK_DEPTH];
|
|
101 int frm_cur_depth;
|
|
102 };
|
|
103
|
|
104 static void
|
|
105 add_op (toyvm_function *fn, enum opcode opcode,
|
|
106 int operand, int linenum)
|
|
107 {
|
|
108 toyvm_op *op;
|
|
109 assert (fn->fn_num_ops < MAX_OPS);
|
|
110 op = &fn->fn_ops[fn->fn_num_ops++];
|
|
111 op->op_opcode = opcode;
|
|
112 op->op_operand = operand;
|
|
113 op->op_linenum = linenum;
|
|
114 }
|
|
115
|
|
116 static void
|
|
117 add_unary_op (toyvm_function *fn, enum opcode opcode,
|
|
118 const char *rest_of_line, int linenum)
|
|
119 {
|
|
120 int operand = atoi (rest_of_line);
|
|
121 add_op (fn, opcode, operand, linenum);
|
|
122 }
|
|
123
|
|
124 static char *
|
|
125 get_function_name (const char *filename)
|
|
126 {
|
|
127 /* Skip any path separators. */
|
|
128 const char *pathsep = strrchr (filename, '/');
|
|
129 if (pathsep)
|
|
130 filename = pathsep + 1;
|
|
131
|
|
132 /* Copy filename to funcname. */
|
|
133 char *funcname = (char *)malloc (strlen (filename) + 1);
|
|
134
|
|
135 strcpy (funcname, filename);
|
|
136
|
|
137 /* Convert "." to NIL terminator. */
|
|
138 *(strchr (funcname, '.')) = '\0';
|
|
139
|
|
140 return funcname;
|
|
141 }
|
|
142
|
|
143 static toyvm_function *
|
|
144 toyvm_function_parse (const char *filename, const char *name)
|
|
145 {
|
|
146 FILE *f = NULL;
|
|
147 toyvm_function *fn = NULL;
|
|
148 char *line = NULL;
|
|
149 ssize_t linelen;
|
|
150 size_t bufsize;
|
|
151 int linenum = 0;
|
|
152
|
|
153 assert (filename);
|
|
154 assert (name);
|
|
155
|
|
156 f = fopen (filename, "r");
|
|
157 if (!f)
|
|
158 {
|
|
159 fprintf (stderr,
|
|
160 "cannot open file %s: %s\n",
|
|
161 filename, strerror (errno));
|
|
162 goto error;
|
|
163 }
|
|
164
|
|
165 fn = (toyvm_function *)calloc (1, sizeof (toyvm_function));
|
|
166 if (!fn)
|
|
167 {
|
|
168 fprintf (stderr, "out of memory allocating toyvm_function\n");
|
|
169 goto error;
|
|
170 }
|
|
171 fn->fn_filename = filename;
|
|
172
|
|
173 /* Read the lines of the file. */
|
|
174 while ((linelen = getline (&line, &bufsize, f)) != -1)
|
|
175 {
|
|
176 /* Note that this is a terrible parser, but it avoids the need to
|
|
177 bring in lex/yacc as a dependency. */
|
|
178 linenum++;
|
|
179
|
|
180 if (0)
|
|
181 fprintf (stdout, "%3d: %s", linenum, line);
|
|
182
|
|
183 /* Lines beginning with # are comments. */
|
|
184 if (line[0] == '#')
|
|
185 continue;
|
|
186
|
|
187 /* Skip blank lines. */
|
|
188 if (line[0] == '\n')
|
|
189 continue;
|
|
190
|
|
191 #define LINE_MATCHES(OPCODE) (0 == strncmp ((OPCODE), line, strlen (OPCODE)))
|
|
192 if (LINE_MATCHES ("DUP\n"))
|
|
193 add_op (fn, DUP, 0, linenum);
|
|
194 else if (LINE_MATCHES ("ROT\n"))
|
|
195 add_op (fn, ROT, 0, linenum);
|
|
196 else if (LINE_MATCHES ("BINARY_ADD\n"))
|
|
197 add_op (fn, BINARY_ADD, 0, linenum);
|
|
198 else if (LINE_MATCHES ("BINARY_SUBTRACT\n"))
|
|
199 add_op (fn, BINARY_SUBTRACT, 0, linenum);
|
|
200 else if (LINE_MATCHES ("BINARY_MULT\n"))
|
|
201 add_op (fn, BINARY_MULT, 0, linenum);
|
|
202 else if (LINE_MATCHES ("BINARY_COMPARE_LT\n"))
|
|
203 add_op (fn, BINARY_COMPARE_LT, 0, linenum);
|
|
204 else if (LINE_MATCHES ("RECURSE\n"))
|
|
205 add_op (fn, RECURSE, 0, linenum);
|
|
206 else if (LINE_MATCHES ("RETURN\n"))
|
|
207 add_op (fn, RETURN, 0, linenum);
|
|
208 else if (LINE_MATCHES ("PUSH_CONST "))
|
|
209 add_unary_op (fn, PUSH_CONST,
|
|
210 line + strlen ("PUSH_CONST "), linenum);
|
|
211 else if (LINE_MATCHES ("JUMP_ABS_IF_TRUE "))
|
|
212 add_unary_op (fn, JUMP_ABS_IF_TRUE,
|
|
213 line + strlen("JUMP_ABS_IF_TRUE "), linenum);
|
|
214 else
|
|
215 {
|
|
216 fprintf (stderr, "%s:%d: parse error\n", filename, linenum);
|
|
217 free (fn);
|
|
218 fn = NULL;
|
|
219 goto error;
|
|
220 }
|
|
221 #undef LINE_MATCHES
|
|
222 }
|
|
223 free (line);
|
|
224 fclose (f);
|
|
225
|
|
226 return fn;
|
|
227
|
|
228 error:
|
|
229 free (line);
|
|
230 if (f)
|
|
231 fclose (f);
|
|
232 free (fn);
|
|
233 return NULL;
|
|
234 }
|
|
235
|
|
236 static void
|
|
237 toyvm_function_disassemble_op (toyvm_function *fn, toyvm_op *op, int index, FILE *out)
|
|
238 {
|
|
239 fprintf (out, "%s:%d: index %d: %s",
|
|
240 fn->fn_filename, op->op_linenum, index,
|
|
241 opcode_names[op->op_opcode]);
|
|
242 if (op->op_opcode >= FIRST_UNARY_OPCODE)
|
|
243 fprintf (out, " %d", op->op_operand);
|
|
244 fprintf (out, "\n");
|
|
245 }
|
|
246
|
|
247 static void
|
|
248 toyvm_function_disassemble (toyvm_function *fn, FILE *out)
|
|
249 {
|
|
250 int i;
|
|
251 for (i = 0; i < fn->fn_num_ops; i++)
|
|
252 {
|
|
253 toyvm_op *op = &fn->fn_ops[i];
|
|
254 toyvm_function_disassemble_op (fn, op, i, out);
|
|
255 }
|
|
256 }
|
|
257
|
|
258 static void
|
|
259 toyvm_frame_push (toyvm_frame *frame, int arg)
|
|
260 {
|
|
261 assert (frame->frm_cur_depth < MAX_STACK_DEPTH);
|
|
262 frame->frm_stack[frame->frm_cur_depth++] = arg;
|
|
263 }
|
|
264
|
|
265 static int
|
|
266 toyvm_frame_pop (toyvm_frame *frame)
|
|
267 {
|
|
268 assert (frame->frm_cur_depth > 0);
|
|
269 return frame->frm_stack[--frame->frm_cur_depth];
|
|
270 }
|
|
271
|
|
272 static void
|
|
273 toyvm_frame_dump_stack (toyvm_frame *frame, FILE *out)
|
|
274 {
|
|
275 int i;
|
|
276 fprintf (out, "stack:");
|
|
277 for (i = 0; i < frame->frm_cur_depth; i++)
|
|
278 {
|
|
279 fprintf (out, " %d", frame->frm_stack[i]);
|
|
280 }
|
|
281 fprintf (out, "\n");
|
|
282 }
|
|
283
|
|
284 /* Execute the given function. */
|
|
285
|
|
286 static int
|
|
287 toyvm_function_interpret (toyvm_function *fn, int arg, FILE *trace)
|
|
288 {
|
|
289 toyvm_frame frame;
|
|
290 #define PUSH(ARG) (toyvm_frame_push (&frame, (ARG)))
|
|
291 #define POP(ARG) (toyvm_frame_pop (&frame))
|
|
292
|
|
293 frame.frm_function = fn;
|
|
294 frame.frm_pc = 0;
|
|
295 frame.frm_cur_depth = 0;
|
|
296
|
|
297 PUSH (arg);
|
|
298
|
|
299 while (1)
|
|
300 {
|
|
301 toyvm_op *op;
|
|
302 int x, y;
|
|
303 assert (frame.frm_pc < fn->fn_num_ops);
|
|
304 op = &fn->fn_ops[frame.frm_pc++];
|
|
305
|
|
306 if (trace)
|
|
307 {
|
|
308 toyvm_frame_dump_stack (&frame, trace);
|
|
309 toyvm_function_disassemble_op (fn, op, frame.frm_pc, trace);
|
|
310 }
|
|
311
|
|
312 switch (op->op_opcode)
|
|
313 {
|
|
314 /* Ops taking no operand. */
|
|
315 case DUP:
|
|
316 x = POP ();
|
|
317 PUSH (x);
|
|
318 PUSH (x);
|
|
319 break;
|
|
320
|
|
321 case ROT:
|
|
322 y = POP ();
|
|
323 x = POP ();
|
|
324 PUSH (y);
|
|
325 PUSH (x);
|
|
326 break;
|
|
327
|
|
328 case BINARY_ADD:
|
|
329 y = POP ();
|
|
330 x = POP ();
|
|
331 PUSH (x + y);
|
|
332 break;
|
|
333
|
|
334 case BINARY_SUBTRACT:
|
|
335 y = POP ();
|
|
336 x = POP ();
|
|
337 PUSH (x - y);
|
|
338 break;
|
|
339
|
|
340 case BINARY_MULT:
|
|
341 y = POP ();
|
|
342 x = POP ();
|
|
343 PUSH (x * y);
|
|
344 break;
|
|
345
|
|
346 case BINARY_COMPARE_LT:
|
|
347 y = POP ();
|
|
348 x = POP ();
|
|
349 PUSH (x < y);
|
|
350 break;
|
|
351
|
|
352 case RECURSE:
|
|
353 x = POP ();
|
|
354 x = toyvm_function_interpret (fn, x, trace);
|
|
355 PUSH (x);
|
|
356 break;
|
|
357
|
|
358 case RETURN:
|
|
359 return POP ();
|
|
360
|
|
361 /* Ops taking an operand. */
|
|
362 case PUSH_CONST:
|
|
363 PUSH (op->op_operand);
|
|
364 break;
|
|
365
|
|
366 case JUMP_ABS_IF_TRUE:
|
|
367 x = POP ();
|
|
368 if (x)
|
|
369 frame.frm_pc = op->op_operand;
|
|
370 break;
|
|
371
|
|
372 default:
|
|
373 assert (0); /* unknown opcode */
|
|
374
|
|
375 } /* end of switch on opcode */
|
|
376 } /* end of while loop */
|
|
377
|
|
378 #undef PUSH
|
|
379 #undef POP
|
|
380 }
|
|
381
|
|
382 /* JIT compilation. */
|
|
383
|
|
384 struct compilation_state
|
|
385 {
|
|
386 gcc_jit_context *ctxt;
|
|
387
|
|
388 gcc_jit_type *int_type;
|
|
389 gcc_jit_type *bool_type;
|
|
390 gcc_jit_type *stack_type; /* int[MAX_STACK_DEPTH] */
|
|
391
|
|
392 gcc_jit_rvalue *const_one;
|
|
393
|
|
394 gcc_jit_function *fn;
|
|
395 gcc_jit_param *param_arg;
|
|
396 gcc_jit_lvalue *stack;
|
|
397 gcc_jit_lvalue *stack_depth;
|
|
398 gcc_jit_lvalue *x;
|
|
399 gcc_jit_lvalue *y;
|
|
400
|
|
401 gcc_jit_location *op_locs[MAX_OPS];
|
|
402 gcc_jit_block *initial_block;
|
|
403 gcc_jit_block *op_blocks[MAX_OPS];
|
|
404
|
|
405 };
|
|
406
|
|
407 /* Stack manipulation. */
|
|
408
|
|
409 static void
|
|
410 add_push (compilation_state *state,
|
|
411 gcc_jit_block *block,
|
|
412 gcc_jit_rvalue *rvalue,
|
|
413 gcc_jit_location *loc)
|
|
414 {
|
|
415 /* stack[stack_depth] = RVALUE */
|
|
416 gcc_jit_block_add_assignment (
|
|
417 block,
|
|
418 loc,
|
|
419 /* stack[stack_depth] */
|
|
420 gcc_jit_context_new_array_access (
|
|
421 state->ctxt,
|
|
422 loc,
|
|
423 gcc_jit_lvalue_as_rvalue (state->stack),
|
|
424 gcc_jit_lvalue_as_rvalue (state->stack_depth)),
|
|
425 rvalue);
|
|
426
|
|
427 /* "stack_depth++;". */
|
|
428 gcc_jit_block_add_assignment_op (
|
|
429 block,
|
|
430 loc,
|
|
431 state->stack_depth,
|
|
432 GCC_JIT_BINARY_OP_PLUS,
|
|
433 state->const_one);
|
|
434 }
|
|
435
|
|
436 static void
|
|
437 add_pop (compilation_state *state,
|
|
438 gcc_jit_block *block,
|
|
439 gcc_jit_lvalue *lvalue,
|
|
440 gcc_jit_location *loc)
|
|
441 {
|
|
442 /* "--stack_depth;". */
|
|
443 gcc_jit_block_add_assignment_op (
|
|
444 block,
|
|
445 loc,
|
|
446 state->stack_depth,
|
|
447 GCC_JIT_BINARY_OP_MINUS,
|
|
448 state->const_one);
|
|
449
|
|
450 /* "LVALUE = stack[stack_depth];". */
|
|
451 gcc_jit_block_add_assignment (
|
|
452 block,
|
|
453 loc,
|
|
454 lvalue,
|
|
455 /* stack[stack_depth] */
|
|
456 gcc_jit_lvalue_as_rvalue (
|
|
457 gcc_jit_context_new_array_access (
|
|
458 state->ctxt,
|
|
459 loc,
|
|
460 gcc_jit_lvalue_as_rvalue (state->stack),
|
|
461 gcc_jit_lvalue_as_rvalue (state->stack_depth))));
|
|
462 }
|
|
463
|
|
464 /* A struct to hold the compilation results. */
|
|
465
|
|
466 struct toyvm_compiled_function
|
|
467 {
|
|
468 gcc_jit_result *cf_jit_result;
|
|
469 toyvm_compiled_code cf_code;
|
|
470 };
|
|
471
|
|
472 /* The main compilation hook. */
|
|
473
|
|
474 static toyvm_compiled_function *
|
|
475 toyvm_function_compile (toyvm_function *fn)
|
|
476 {
|
|
477 compilation_state state;
|
|
478 int pc;
|
|
479 char *funcname;
|
|
480
|
|
481 memset (&state, 0, sizeof (state));
|
|
482
|
|
483 funcname = get_function_name (fn->fn_filename);
|
|
484
|
|
485 state.ctxt = gcc_jit_context_acquire ();
|
|
486
|
|
487 gcc_jit_context_set_bool_option (state.ctxt,
|
|
488 GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE,
|
|
489 0);
|
|
490 gcc_jit_context_set_bool_option (state.ctxt,
|
|
491 GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE,
|
|
492 0);
|
|
493 gcc_jit_context_set_int_option (state.ctxt,
|
|
494 GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL,
|
|
495 3);
|
|
496 gcc_jit_context_set_bool_option (state.ctxt,
|
|
497 GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES,
|
|
498 0);
|
|
499 gcc_jit_context_set_bool_option (state.ctxt,
|
|
500 GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING,
|
|
501 0);
|
|
502 gcc_jit_context_set_bool_option (state.ctxt,
|
|
503 GCC_JIT_BOOL_OPTION_DEBUGINFO,
|
|
504 1);
|
|
505
|
|
506 /* Create types. */
|
|
507 state.int_type =
|
|
508 gcc_jit_context_get_type (state.ctxt, GCC_JIT_TYPE_INT);
|
|
509 state.bool_type =
|
|
510 gcc_jit_context_get_type (state.ctxt, GCC_JIT_TYPE_BOOL);
|
|
511 state.stack_type =
|
|
512 gcc_jit_context_new_array_type (state.ctxt, NULL,
|
|
513 state.int_type, MAX_STACK_DEPTH);
|
|
514
|
|
515 /* The constant value 1. */
|
|
516 state.const_one = gcc_jit_context_one (state.ctxt, state.int_type);
|
|
517
|
|
518 /* Create locations. */
|
|
519 for (pc = 0; pc < fn->fn_num_ops; pc++)
|
|
520 {
|
|
521 toyvm_op *op = &fn->fn_ops[pc];
|
|
522
|
|
523 state.op_locs[pc] = gcc_jit_context_new_location (state.ctxt,
|
|
524 fn->fn_filename,
|
|
525 op->op_linenum,
|
|
526 0); /* column */
|
|
527 }
|
|
528
|
|
529 /* Creating the function. */
|
|
530 state.param_arg =
|
|
531 gcc_jit_context_new_param (state.ctxt, state.op_locs[0],
|
|
532 state.int_type, "arg");
|
|
533 state.fn =
|
|
534 gcc_jit_context_new_function (state.ctxt,
|
|
535 state.op_locs[0],
|
|
536 GCC_JIT_FUNCTION_EXPORTED,
|
|
537 state.int_type,
|
|
538 funcname,
|
|
539 1, &state.param_arg, 0);
|
|
540
|
|
541 /* Create stack lvalues. */
|
|
542 state.stack =
|
|
543 gcc_jit_function_new_local (state.fn, NULL,
|
|
544 state.stack_type, "stack");
|
|
545 state.stack_depth =
|
|
546 gcc_jit_function_new_local (state.fn, NULL,
|
|
547 state.int_type, "stack_depth");
|
|
548 state.x =
|
|
549 gcc_jit_function_new_local (state.fn, NULL,
|
|
550 state.int_type, "x");
|
|
551 state.y =
|
|
552 gcc_jit_function_new_local (state.fn, NULL,
|
|
553 state.int_type, "y");
|
|
554
|
|
555 /* 1st pass: create blocks, one per opcode. */
|
|
556
|
|
557 /* We need an entry block to do one-time initialization, so create that
|
|
558 first. */
|
|
559 state.initial_block = gcc_jit_function_new_block (state.fn, "initial");
|
|
560
|
|
561 /* Create a block per operation. */
|
|
562 for (pc = 0; pc < fn->fn_num_ops; pc++)
|
|
563 {
|
|
564 char buf[16];
|
|
565 sprintf (buf, "instr%i", pc);
|
|
566 state.op_blocks[pc] = gcc_jit_function_new_block (state.fn, buf);
|
|
567 }
|
|
568
|
|
569 /* Populate the initial block. */
|
|
570
|
|
571 /* "stack_depth = 0;". */
|
|
572 gcc_jit_block_add_assignment (
|
|
573 state.initial_block,
|
|
574 state.op_locs[0],
|
|
575 state.stack_depth,
|
|
576 gcc_jit_context_zero (state.ctxt, state.int_type));
|
|
577
|
|
578 /* "PUSH (arg);". */
|
|
579 add_push (&state,
|
|
580 state.initial_block,
|
|
581 gcc_jit_param_as_rvalue (state.param_arg),
|
|
582 state.op_locs[0]);
|
|
583
|
|
584 /* ...and jump to insn 0. */
|
|
585 gcc_jit_block_end_with_jump (state.initial_block,
|
|
586 state.op_locs[0],
|
|
587 state.op_blocks[0]);
|
|
588
|
|
589 /* 2nd pass: fill in instructions. */
|
|
590 for (pc = 0; pc < fn->fn_num_ops; pc++)
|
|
591 {
|
|
592 gcc_jit_location *loc = state.op_locs[pc];
|
|
593
|
|
594 gcc_jit_block *block = state.op_blocks[pc];
|
|
595 gcc_jit_block *next_block = (pc < fn->fn_num_ops
|
|
596 ? state.op_blocks[pc + 1]
|
|
597 : NULL);
|
|
598
|
|
599 toyvm_op *op;
|
|
600 op = &fn->fn_ops[pc];
|
|
601
|
|
602 /* Helper macros. */
|
|
603
|
|
604 #define X_EQUALS_POP()\
|
|
605 add_pop (&state, block, state.x, loc)
|
|
606 #define Y_EQUALS_POP()\
|
|
607 add_pop (&state, block, state.y, loc)
|
|
608 #define PUSH_RVALUE(RVALUE)\
|
|
609 add_push (&state, block, (RVALUE), loc)
|
|
610 #define PUSH_X()\
|
|
611 PUSH_RVALUE (gcc_jit_lvalue_as_rvalue (state.x))
|
|
612 #define PUSH_Y() \
|
|
613 PUSH_RVALUE (gcc_jit_lvalue_as_rvalue (state.y))
|
|
614
|
|
615 gcc_jit_block_add_comment (block, loc, opcode_names[op->op_opcode]);
|
|
616
|
|
617 /* Handle the individual opcodes. */
|
|
618
|
|
619 switch (op->op_opcode)
|
|
620 {
|
|
621 case DUP:
|
|
622 X_EQUALS_POP ();
|
|
623 PUSH_X ();
|
|
624 PUSH_X ();
|
|
625 break;
|
|
626
|
|
627 case ROT:
|
|
628 Y_EQUALS_POP ();
|
|
629 X_EQUALS_POP ();
|
|
630 PUSH_Y ();
|
|
631 PUSH_X ();
|
|
632 break;
|
|
633
|
|
634 case BINARY_ADD:
|
|
635 Y_EQUALS_POP ();
|
|
636 X_EQUALS_POP ();
|
|
637 PUSH_RVALUE (
|
|
638 gcc_jit_context_new_binary_op (
|
|
639 state.ctxt,
|
|
640 loc,
|
|
641 GCC_JIT_BINARY_OP_PLUS,
|
|
642 state.int_type,
|
|
643 gcc_jit_lvalue_as_rvalue (state.x),
|
|
644 gcc_jit_lvalue_as_rvalue (state.y)));
|
|
645 break;
|
|
646
|
|
647 case BINARY_SUBTRACT:
|
|
648 Y_EQUALS_POP ();
|
|
649 X_EQUALS_POP ();
|
|
650 PUSH_RVALUE (
|
|
651 gcc_jit_context_new_binary_op (
|
|
652 state.ctxt,
|
|
653 loc,
|
|
654 GCC_JIT_BINARY_OP_MINUS,
|
|
655 state.int_type,
|
|
656 gcc_jit_lvalue_as_rvalue (state.x),
|
|
657 gcc_jit_lvalue_as_rvalue (state.y)));
|
|
658 break;
|
|
659
|
|
660 case BINARY_MULT:
|
|
661 Y_EQUALS_POP ();
|
|
662 X_EQUALS_POP ();
|
|
663 PUSH_RVALUE (
|
|
664 gcc_jit_context_new_binary_op (
|
|
665 state.ctxt,
|
|
666 loc,
|
|
667 GCC_JIT_BINARY_OP_MULT,
|
|
668 state.int_type,
|
|
669 gcc_jit_lvalue_as_rvalue (state.x),
|
|
670 gcc_jit_lvalue_as_rvalue (state.y)));
|
|
671 break;
|
|
672
|
|
673 case BINARY_COMPARE_LT:
|
|
674 Y_EQUALS_POP ();
|
|
675 X_EQUALS_POP ();
|
|
676 PUSH_RVALUE (
|
|
677 /* cast of bool to int */
|
|
678 gcc_jit_context_new_cast (
|
|
679 state.ctxt,
|
|
680 loc,
|
|
681 /* (x < y) as a bool */
|
|
682 gcc_jit_context_new_comparison (
|
|
683 state.ctxt,
|
|
684 loc,
|
|
685 GCC_JIT_COMPARISON_LT,
|
|
686 gcc_jit_lvalue_as_rvalue (state.x),
|
|
687 gcc_jit_lvalue_as_rvalue (state.y)),
|
|
688 state.int_type));
|
|
689 break;
|
|
690
|
|
691 case RECURSE:
|
|
692 {
|
|
693 X_EQUALS_POP ();
|
|
694 gcc_jit_rvalue *arg = gcc_jit_lvalue_as_rvalue (state.x);
|
|
695 PUSH_RVALUE (
|
|
696 gcc_jit_context_new_call (
|
|
697 state.ctxt,
|
|
698 loc,
|
|
699 state.fn,
|
|
700 1, &arg));
|
|
701 break;
|
|
702 }
|
|
703
|
|
704 case RETURN:
|
|
705 X_EQUALS_POP ();
|
|
706 gcc_jit_block_end_with_return (
|
|
707 block,
|
|
708 loc,
|
|
709 gcc_jit_lvalue_as_rvalue (state.x));
|
|
710 break;
|
|
711
|
|
712 /* Ops taking an operand. */
|
|
713 case PUSH_CONST:
|
|
714 PUSH_RVALUE (
|
|
715 gcc_jit_context_new_rvalue_from_int (
|
|
716 state.ctxt,
|
|
717 state.int_type,
|
|
718 op->op_operand));
|
|
719 break;
|
|
720
|
|
721 case JUMP_ABS_IF_TRUE:
|
|
722 X_EQUALS_POP ();
|
|
723 gcc_jit_block_end_with_conditional (
|
|
724 block,
|
|
725 loc,
|
|
726 /* "(bool)x". */
|
|
727 gcc_jit_context_new_cast (
|
|
728 state.ctxt,
|
|
729 loc,
|
|
730 gcc_jit_lvalue_as_rvalue (state.x),
|
|
731 state.bool_type),
|
|
732 state.op_blocks[op->op_operand], /* on_true */
|
|
733 next_block); /* on_false */
|
|
734 break;
|
|
735
|
|
736 default:
|
|
737 assert(0);
|
|
738 } /* end of switch on opcode */
|
|
739
|
|
740 /* Go to the next block. */
|
|
741 if (op->op_opcode != JUMP_ABS_IF_TRUE
|
|
742 && op->op_opcode != RETURN)
|
|
743 gcc_jit_block_end_with_jump (
|
|
744 block,
|
|
745 loc,
|
|
746 next_block);
|
|
747
|
|
748 } /* end of loop on PC locations. */
|
|
749
|
|
750 /* We've now finished populating the context. Compile it. */
|
|
751 gcc_jit_result *jit_result = gcc_jit_context_compile (state.ctxt);
|
|
752 gcc_jit_context_release (state.ctxt);
|
|
753
|
|
754 toyvm_compiled_function *toyvm_result =
|
|
755 (toyvm_compiled_function *)calloc (1, sizeof (toyvm_compiled_function));
|
|
756 if (!toyvm_result)
|
|
757 {
|
|
758 fprintf (stderr, "out of memory allocating toyvm_compiled_function\n");
|
|
759 gcc_jit_result_release (jit_result);
|
|
760 return NULL;
|
|
761 }
|
|
762
|
|
763 toyvm_result->cf_jit_result = jit_result;
|
|
764 toyvm_result->cf_code =
|
|
765 (toyvm_compiled_code)gcc_jit_result_get_code (jit_result,
|
|
766 funcname);
|
|
767
|
|
768 free (funcname);
|
|
769
|
|
770 return toyvm_result;
|
|
771 }
|
|
772
|
|
773 char test[1024];
|
|
774
|
|
775 #define CHECK_NON_NULL(PTR) \
|
|
776 do { \
|
|
777 if ((PTR) != NULL) \
|
|
778 { \
|
|
779 pass ("%s: %s is non-null", test, #PTR); \
|
|
780 } \
|
|
781 else \
|
|
782 { \
|
|
783 fail ("%s: %s is NULL", test, #PTR); \
|
|
784 abort (); \
|
|
785 } \
|
|
786 } while (0)
|
|
787
|
|
788 #define CHECK_VALUE(ACTUAL, EXPECTED) \
|
|
789 do { \
|
|
790 if ((ACTUAL) == (EXPECTED)) \
|
|
791 { \
|
|
792 pass ("%s: actual: %s == expected: %s", test, #ACTUAL, #EXPECTED); \
|
|
793 } \
|
|
794 else \
|
|
795 { \
|
|
796 fail ("%s: actual: %s != expected: %s", test, #ACTUAL, #EXPECTED); \
|
|
797 fprintf (stderr, "incorrect value\n"); \
|
|
798 abort (); \
|
|
799 } \
|
|
800 } while (0)
|
|
801
|
|
802 static void
|
|
803 test_script (const char *scripts_dir, const char *script_name, int input,
|
|
804 int expected_result)
|
|
805 {
|
|
806 char *script_path;
|
|
807 toyvm_function *fn;
|
|
808 int interpreted_result;
|
|
809 toyvm_compiled_function *compiled_fn;
|
|
810 toyvm_compiled_code code;
|
|
811 int compiled_result;
|
|
812
|
|
813 snprintf (test, sizeof (test), "toyvm.c: %s", script_name);
|
|
814
|
|
815 script_path = (char *)malloc (strlen (scripts_dir)
|
|
816 + strlen (script_name) + 1);
|
|
817 CHECK_NON_NULL (script_path);
|
|
818 sprintf (script_path, "%s%s", scripts_dir, script_name);
|
|
819
|
|
820 fn = toyvm_function_parse (script_path, script_name);
|
|
821 CHECK_NON_NULL (fn);
|
|
822
|
|
823 interpreted_result = toyvm_function_interpret (fn, input, NULL);
|
|
824 CHECK_VALUE (interpreted_result, expected_result);
|
|
825
|
|
826 compiled_fn = toyvm_function_compile (fn);
|
|
827 CHECK_NON_NULL (compiled_fn);
|
|
828
|
|
829 code = (toyvm_compiled_code)compiled_fn->cf_code;
|
|
830 CHECK_NON_NULL (code);
|
|
831
|
|
832 compiled_result = code (input);
|
|
833 CHECK_VALUE (compiled_result, expected_result);
|
|
834
|
|
835 gcc_jit_result_release (compiled_fn->cf_jit_result);
|
|
836 free (compiled_fn);
|
|
837 free (fn);
|
|
838 free (script_path);
|
|
839 }
|
|
840
|
|
841 #define PATH_TO_SCRIPTS ("/jit/docs/examples/tut04-toyvm/")
|
|
842
|
|
843 static void
|
|
844 test_suite (void)
|
|
845 {
|
|
846 const char *srcdir;
|
|
847 char *scripts_dir;
|
|
848
|
|
849 snprintf (test, sizeof (test), "toyvm.c");
|
|
850
|
|
851 /* We need to locate the test scripts.
|
|
852 Rely on "srcdir" being set in the environment. */
|
|
853
|
|
854 srcdir = getenv ("srcdir");
|
|
855 CHECK_NON_NULL (srcdir);
|
|
856
|
|
857 scripts_dir = (char *)malloc (strlen (srcdir) + strlen(PATH_TO_SCRIPTS)
|
|
858 + 1);
|
|
859 CHECK_NON_NULL (scripts_dir);
|
|
860 sprintf (scripts_dir, "%s%s", srcdir, PATH_TO_SCRIPTS);
|
|
861
|
|
862 test_script (scripts_dir, "factorial.toy", 10, 3628800);
|
|
863 test_script (scripts_dir, "fibonacci.toy", 10, 55);
|
|
864
|
|
865 free (scripts_dir);
|
|
866 }
|
|
867
|
|
868 int
|
|
869 main (int argc, char **argv)
|
|
870 {
|
|
871 const char *filename = NULL;
|
|
872 toyvm_function *fn = NULL;
|
|
873
|
|
874 /* If called with no args, assume we're being run by the test suite. */
|
|
875 if (argc < 3)
|
|
876 {
|
|
877 test_suite ();
|
|
878 return 0;
|
|
879 }
|
|
880
|
|
881 if (argc != 3)
|
|
882 {
|
|
883 fprintf (stdout,
|
|
884 "%s FILENAME INPUT: Parse and run a .toy file\n",
|
|
885 argv[0]);
|
|
886 exit (1);
|
|
887 }
|
|
888
|
|
889 filename = argv[1];
|
|
890 fn = toyvm_function_parse (filename, filename);
|
|
891 if (!fn)
|
|
892 exit (1);
|
|
893
|
|
894 if (0)
|
|
895 toyvm_function_disassemble (fn, stdout);
|
|
896
|
|
897 printf ("interpreter result: %d\n",
|
|
898 toyvm_function_interpret (fn, atoi (argv[2]), NULL));
|
|
899
|
|
900 /* JIT-compilation. */
|
|
901 toyvm_compiled_function *compiled_fn
|
|
902 = toyvm_function_compile (fn);
|
|
903
|
|
904 toyvm_compiled_code code = compiled_fn->cf_code;
|
|
905 printf ("compiler result: %d\n",
|
|
906 code (atoi (argv[2])));
|
|
907
|
|
908 gcc_jit_result_release (compiled_fn->cf_jit_result);
|
|
909 free (compiled_fn);
|
|
910
|
|
911 return 0;
|
|
912 }
|