]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/jit/docs/examples/tut04-toyvm/toyvm.cc
Update copyright years.
[thirdparty/gcc.git] / gcc / jit / docs / examples / tut04-toyvm / toyvm.cc
1 /* A simple stack-based virtual machine to demonstrate
2 JIT-compilation.
3 Copyright (C) 2014-2016 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 }