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