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