1/* A simple stack-based virtual machine to demonstrate 2 JIT-compilation. 3 Copyright (C) 2014-2015 Free Software Foundation, Inc. 4 5This file is part of GCC. 6 7GCC is free software; you can redistribute it and/or modify it 8under the terms of the GNU General Public License as published by 9the Free Software Foundation; either version 3, or (at your option) 10any later version. 11 12GCC is distributed in the hope that it will be useful, but 13WITHOUT ANY WARRANTY; without even the implied warranty of 14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15General Public License for more details. 16 17You should have received a copy of the GNU General Public License 18along 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. */ 32typedef struct toyvm_op toyvm_op; 33typedef struct toyvm_function toyvm_function; 34typedef struct toyvm_frame toyvm_frame; 35typedef struct compilation_state compilation_state; 36typedef struct toyvm_compiled_function toyvm_compiled_function; 37 38/* Functions are compiled to this function ptr type. */ 39typedef int (*toyvm_compiled_code) (int); 40 41enum 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 59const 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 73struct 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 87struct 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 96struct 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 104static void 105add_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 116static void 117add_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 124static char * 125get_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 143static toyvm_function * 144toyvm_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 236static void 237toyvm_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 247static void 248toyvm_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 258static void 259toyvm_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 265static int 266toyvm_frame_pop (toyvm_frame *frame) 267{ 268 assert (frame->frm_cur_depth > 0); 269 return frame->frm_stack[--frame->frm_cur_depth]; 270} 271 272static void 273toyvm_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 286static int 287toyvm_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 384struct 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 409static void 410add_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 436static void 437add_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 466struct 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 474static toyvm_compiled_function * 475toyvm_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 773char 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 802static void 803test_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 843static void 844test_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 868int 869main (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