1 /* A simple stack-based virtual machine to demonstrate
2    JIT-compilation.
3    Copyright (C) 2014-2019 Free Software Foundation, Inc.
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
add_op(toyvm_function * fn,enum opcode opcode,int operand,int linenum)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
add_unary_op(toyvm_function * fn,enum opcode opcode,const char * rest_of_line,int linenum)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 *
get_function_name(const char * filename)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 *
toyvm_function_parse(const char * filename,const char * name)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
toyvm_function_disassemble_op(toyvm_function * fn,toyvm_op * op,int index,FILE * out)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
toyvm_function_disassemble(toyvm_function * fn,FILE * out)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
toyvm_frame_push(toyvm_frame * frame,int arg)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
toyvm_frame_pop(toyvm_frame * frame)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
toyvm_frame_dump_stack(toyvm_frame * frame,FILE * out)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
toyvm_function_interpret(toyvm_function * fn,int arg,FILE * trace)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
add_push(compilation_state * state,gcc_jit_block * block,gcc_jit_rvalue * rvalue,gcc_jit_location * loc)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
add_pop(compilation_state * state,gcc_jit_block * block,gcc_jit_lvalue * lvalue,gcc_jit_location * loc)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 *
toyvm_function_compile(toyvm_function * fn)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
test_script(const char * scripts_dir,const char * script_name,int input,int expected_result)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
test_suite(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
main(int argc,char ** argv)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 }
913