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