| /* A simple stack-based virtual machine to demonstrate |
| JIT-compilation. |
| Copyright (C) 2014-2021 Free Software Foundation, Inc. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it |
| under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| GCC is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "jit-dejagnu.h" |
| |
| #include <libgccjit++.h> |
| |
| /* Wrapper around a gcc_jit_result *. */ |
| |
| class compilation_result |
| { |
| public: |
| compilation_result (gcc_jit_result *result) : |
| m_result (result) |
| { |
| } |
| ~compilation_result () |
| { |
| gcc_jit_result_release (m_result); |
| } |
| |
| void *get_code (const char *funcname) |
| { |
| return gcc_jit_result_get_code (m_result, funcname); |
| } |
| |
| private: |
| gcc_jit_result *m_result; |
| }; |
| |
| /* Functions are compiled to this function ptr type. */ |
| typedef int (*toyvm_compiled_func) (int); |
| |
| enum opcode { |
| /* Ops taking no operand. */ |
| DUP, |
| ROT, |
| BINARY_ADD, |
| BINARY_SUBTRACT, |
| BINARY_MULT, |
| BINARY_COMPARE_LT, |
| RECURSE, |
| RETURN, |
| |
| /* Ops taking an operand. */ |
| PUSH_CONST, |
| JUMP_ABS_IF_TRUE |
| }; |
| |
| #define FIRST_UNARY_OPCODE (PUSH_CONST) |
| |
| const char * const opcode_names[] = { |
| "DUP", |
| "ROT", |
| "BINARY_ADD", |
| "BINARY_SUBTRACT", |
| "BINARY_MULT", |
| "BINARY_COMPARE_LT", |
| "RECURSE", |
| "RETURN", |
| |
| "PUSH_CONST", |
| "JUMP_ABS_IF_TRUE", |
| }; |
| |
| struct toyvm_op |
| { |
| /* Which operation. */ |
| enum opcode op_opcode; |
| |
| /* Some opcodes take an argument. */ |
| int op_operand; |
| |
| /* The line number of the operation within the source file. */ |
| int op_linenum; |
| }; |
| |
| #define MAX_OPS (64) |
| |
| class toyvm_function |
| { |
| public: |
| void |
| add_op (enum opcode opcode, |
| int operand, int linenum); |
| |
| void |
| add_unary_op (enum opcode opcode, |
| const char *rest_of_line, int linenum); |
| |
| static toyvm_function * |
| parse (const char *filename, const char *name); |
| |
| void |
| disassemble_op (toyvm_op *op, int index, FILE *out); |
| |
| void |
| disassemble (FILE *out); |
| |
| int |
| interpret (int arg, FILE *trace); |
| |
| compilation_result |
| compile (); |
| |
| const char * |
| get_function_name () const { return m_funcname; } |
| |
| private: |
| void |
| make_function_name (const char *filename); |
| |
| private: |
| const char *fn_filename; |
| char *m_funcname; |
| int fn_num_ops; |
| toyvm_op fn_ops[MAX_OPS]; |
| friend struct compilation_state; |
| }; |
| |
| #define MAX_STACK_DEPTH (8) |
| |
| class toyvm_frame |
| { |
| public: |
| void push (int arg); |
| int pop (); |
| void dump_stack (FILE *out); |
| |
| private: |
| toyvm_function *frm_function; |
| int frm_pc; |
| int frm_stack[MAX_STACK_DEPTH]; |
| int frm_cur_depth; |
| |
| friend int toyvm_function::interpret (int arg, FILE *trace); |
| |
| }; |
| |
| void |
| toyvm_function::add_op (enum opcode opcode, |
| int operand, int linenum) |
| { |
| toyvm_op *op; |
| assert (fn_num_ops < MAX_OPS); |
| op = &fn_ops[fn_num_ops++]; |
| op->op_opcode = opcode; |
| op->op_operand = operand; |
| op->op_linenum = linenum; |
| } |
| |
| void |
| toyvm_function::add_unary_op (enum opcode opcode, |
| const char *rest_of_line, int linenum) |
| { |
| int operand = atoi (rest_of_line); |
| add_op (opcode, operand, linenum); |
| } |
| |
| void |
| toyvm_function::make_function_name (const char *filename) |
| { |
| /* Skip any path separators. */ |
| const char *pathsep = strrchr (filename, '/'); |
| if (pathsep) |
| filename = pathsep + 1; |
| |
| /* Copy filename to funcname. */ |
| m_funcname = (char *)malloc (strlen (filename) + 1); |
| |
| strcpy (m_funcname, filename); |
| |
| /* Convert "." to NIL terminator. */ |
| *(strchr (m_funcname, '.')) = '\0'; |
| } |
| |
| toyvm_function * |
| toyvm_function::parse (const char *filename, const char *name) |
| { |
| FILE *f = NULL; |
| toyvm_function *fn = NULL; |
| char *line = NULL; |
| ssize_t linelen; |
| size_t bufsize; |
| int linenum = 0; |
| |
| assert (filename); |
| assert (name); |
| |
| f = fopen (filename, "r"); |
| if (!f) |
| { |
| fprintf (stderr, |
| "cannot open file %s: %s\n", |
| filename, strerror (errno)); |
| goto error; |
| } |
| |
| fn = (toyvm_function *)calloc (1, sizeof (toyvm_function)); |
| if (!fn) |
| { |
| fprintf (stderr, "out of memory allocating toyvm_function\n"); |
| goto error; |
| } |
| fn->fn_filename = filename; |
| fn->make_function_name (filename); |
| |
| /* Read the lines of the file. */ |
| while ((linelen = getline (&line, &bufsize, f)) != -1) |
| { |
| /* Note that this is a terrible parser, but it avoids the need to |
| bring in lex/yacc as a dependency. */ |
| linenum++; |
| |
| if (0) |
| fprintf (stdout, "%3d: %s", linenum, line); |
| |
| /* Lines beginning with # are comments. */ |
| if (line[0] == '#') |
| continue; |
| |
| /* Skip blank lines. */ |
| if (line[0] == '\n') |
| continue; |
| |
| #define LINE_MATCHES(OPCODE) (0 == strncmp ((OPCODE), line, strlen (OPCODE))) |
| if (LINE_MATCHES ("DUP\n")) |
| fn->add_op (DUP, 0, linenum); |
| else if (LINE_MATCHES ("ROT\n")) |
| fn->add_op (ROT, 0, linenum); |
| else if (LINE_MATCHES ("BINARY_ADD\n")) |
| fn->add_op (BINARY_ADD, 0, linenum); |
| else if (LINE_MATCHES ("BINARY_SUBTRACT\n")) |
| fn->add_op (BINARY_SUBTRACT, 0, linenum); |
| else if (LINE_MATCHES ("BINARY_MULT\n")) |
| fn->add_op (BINARY_MULT, 0, linenum); |
| else if (LINE_MATCHES ("BINARY_COMPARE_LT\n")) |
| fn->add_op (BINARY_COMPARE_LT, 0, linenum); |
| else if (LINE_MATCHES ("RECURSE\n")) |
| fn->add_op (RECURSE, 0, linenum); |
| else if (LINE_MATCHES ("RETURN\n")) |
| fn->add_op (RETURN, 0, linenum); |
| else if (LINE_MATCHES ("PUSH_CONST ")) |
| fn->add_unary_op (PUSH_CONST, |
| line + strlen ("PUSH_CONST "), linenum); |
| else if (LINE_MATCHES ("JUMP_ABS_IF_TRUE ")) |
| fn->add_unary_op (JUMP_ABS_IF_TRUE, |
| line + strlen("JUMP_ABS_IF_TRUE "), linenum); |
| else |
| { |
| fprintf (stderr, "%s:%d: parse error\n", filename, linenum); |
| free (fn); |
| fn = NULL; |
| goto error; |
| } |
| #undef LINE_MATCHES |
| } |
| free (line); |
| fclose (f); |
| |
| return fn; |
| |
| error: |
| free (line); |
| if (f) |
| fclose (f); |
| free (fn); |
| return NULL; |
| } |
| |
| void |
| toyvm_function::disassemble_op (toyvm_op *op, int index, FILE *out) |
| { |
| fprintf (out, "%s:%d: index %d: %s", |
| fn_filename, op->op_linenum, index, |
| opcode_names[op->op_opcode]); |
| if (op->op_opcode >= FIRST_UNARY_OPCODE) |
| fprintf (out, " %d", op->op_operand); |
| fprintf (out, "\n"); |
| } |
| |
| void |
| toyvm_function::disassemble (FILE *out) |
| { |
| int i; |
| for (i = 0; i < fn_num_ops; i++) |
| { |
| toyvm_op *op = &fn_ops[i]; |
| disassemble_op (op, i, out); |
| } |
| } |
| |
| void |
| toyvm_frame::push (int arg) |
| { |
| assert (frm_cur_depth < MAX_STACK_DEPTH); |
| frm_stack[frm_cur_depth++] = arg; |
| } |
| |
| int |
| toyvm_frame::pop () |
| { |
| assert (frm_cur_depth > 0); |
| return frm_stack[--frm_cur_depth]; |
| } |
| |
| void |
| toyvm_frame::dump_stack (FILE *out) |
| { |
| int i; |
| fprintf (out, "stack:"); |
| for (i = 0; i < frm_cur_depth; i++) |
| { |
| fprintf (out, " %d", frm_stack[i]); |
| } |
| fprintf (out, "\n"); |
| } |
| |
| /* Execute the given function. */ |
| |
| int |
| toyvm_function::interpret (int arg, FILE *trace) |
| { |
| toyvm_frame frame; |
| #define PUSH(ARG) (frame.push (ARG)) |
| #define POP(ARG) (frame.pop ()) |
| |
| frame.frm_function = this; |
| frame.frm_pc = 0; |
| frame.frm_cur_depth = 0; |
| |
| PUSH (arg); |
| |
| while (1) |
| { |
| toyvm_op *op; |
| int x, y; |
| assert (frame.frm_pc < fn_num_ops); |
| op = &fn_ops[frame.frm_pc++]; |
| |
| if (trace) |
| { |
| frame.dump_stack (trace); |
| disassemble_op (op, frame.frm_pc, trace); |
| } |
| |
| switch (op->op_opcode) |
| { |
| /* Ops taking no operand. */ |
| case DUP: |
| x = POP (); |
| PUSH (x); |
| PUSH (x); |
| break; |
| |
| case ROT: |
| y = POP (); |
| x = POP (); |
| PUSH (y); |
| PUSH (x); |
| break; |
| |
| case BINARY_ADD: |
| y = POP (); |
| x = POP (); |
| PUSH (x + y); |
| break; |
| |
| case BINARY_SUBTRACT: |
| y = POP (); |
| x = POP (); |
| PUSH (x - y); |
| break; |
| |
| case BINARY_MULT: |
| y = POP (); |
| x = POP (); |
| PUSH (x * y); |
| break; |
| |
| case BINARY_COMPARE_LT: |
| y = POP (); |
| x = POP (); |
| PUSH (x < y); |
| break; |
| |
| case RECURSE: |
| x = POP (); |
| x = interpret (x, trace); |
| PUSH (x); |
| break; |
| |
| case RETURN: |
| return POP (); |
| |
| /* Ops taking an operand. */ |
| case PUSH_CONST: |
| PUSH (op->op_operand); |
| break; |
| |
| case JUMP_ABS_IF_TRUE: |
| x = POP (); |
| if (x) |
| frame.frm_pc = op->op_operand; |
| break; |
| |
| default: |
| assert (0); /* unknown opcode */ |
| |
| } /* end of switch on opcode */ |
| } /* end of while loop */ |
| |
| #undef PUSH |
| #undef POP |
| } |
| |
| /* JIT compilation. */ |
| |
| class compilation_state |
| { |
| public: |
| compilation_state (toyvm_function &toyvmfn) : |
| toyvmfn (toyvmfn) |
| {} |
| |
| void create_context (); |
| void create_types (); |
| void create_locations (); |
| void create_function (const char *funcname); |
| compilation_result compile (); |
| |
| private: |
| void |
| add_push (gccjit::block block, |
| gccjit::rvalue rvalue, |
| gccjit::location loc); |
| |
| void |
| add_pop (gccjit::block block, |
| gccjit::lvalue lvalue, |
| gccjit::location loc); |
| |
| private: |
| |
| /* State. */ |
| |
| toyvm_function &toyvmfn; |
| |
| gccjit::context ctxt; |
| |
| gccjit::type int_type; |
| gccjit::type bool_type; |
| gccjit::type stack_type; /* int[MAX_STACK_DEPTH] */ |
| |
| gccjit::rvalue const_one; |
| |
| gccjit::function fn; |
| gccjit::param param_arg; |
| gccjit::lvalue stack; |
| gccjit::lvalue stack_depth; |
| gccjit::lvalue x; |
| gccjit::lvalue y; |
| |
| gccjit::location op_locs[MAX_OPS]; |
| gccjit::block initial_block; |
| gccjit::block op_blocks[MAX_OPS]; |
| |
| }; |
| |
| /* The main compilation hook. */ |
| |
| compilation_result |
| toyvm_function::compile () |
| { |
| compilation_state state (*this); |
| |
| state.create_context (); |
| state.create_types (); |
| state.create_locations (); |
| state.create_function (get_function_name ()); |
| |
| /* We've now finished populating the context. Compile it. */ |
| return state.compile (); |
| } |
| |
| /* Stack manipulation. */ |
| |
| void |
| compilation_state::add_push (gccjit::block block, |
| gccjit::rvalue rvalue, |
| gccjit::location loc) |
| { |
| /* stack[stack_depth] = RVALUE */ |
| block.add_assignment ( |
| /* stack[stack_depth] */ |
| ctxt.new_array_access ( |
| stack, |
| stack_depth, |
| loc), |
| rvalue, |
| loc); |
| |
| /* "stack_depth++;". */ |
| block.add_assignment_op ( |
| stack_depth, |
| GCC_JIT_BINARY_OP_PLUS, |
| const_one, |
| loc); |
| } |
| |
| void |
| compilation_state::add_pop (gccjit::block block, |
| gccjit::lvalue lvalue, |
| gccjit::location loc) |
| { |
| /* "--stack_depth;". */ |
| block.add_assignment_op ( |
| stack_depth, |
| GCC_JIT_BINARY_OP_MINUS, |
| const_one, |
| loc); |
| |
| /* "LVALUE = stack[stack_depth];". */ |
| block.add_assignment ( |
| lvalue, |
| /* stack[stack_depth] */ |
| ctxt.new_array_access (stack, |
| stack_depth, |
| loc), |
| loc); |
| } |
| |
| /* Create the context. */ |
| |
| void |
| compilation_state::create_context () |
| { |
| ctxt = gccjit::context::acquire (); |
| |
| ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, |
| 0); |
| ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE, |
| 0); |
| ctxt.set_int_option (GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, |
| 3); |
| ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES, |
| 0); |
| ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING, |
| 0); |
| ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DEBUGINFO, |
| 1); |
| } |
| |
| /* Create types. */ |
| |
| void |
| compilation_state::create_types () |
| { |
| /* Create types. */ |
| int_type = ctxt.get_type (GCC_JIT_TYPE_INT); |
| bool_type = ctxt.get_type (GCC_JIT_TYPE_BOOL); |
| stack_type = ctxt.new_array_type (int_type, MAX_STACK_DEPTH); |
| |
| /* The constant value 1. */ |
| const_one = ctxt.one (int_type); |
| |
| } |
| |
| /* Create locations. */ |
| |
| void |
| compilation_state::create_locations () |
| { |
| for (int pc = 0; pc < toyvmfn.fn_num_ops; pc++) |
| { |
| toyvm_op *op = &toyvmfn.fn_ops[pc]; |
| |
| op_locs[pc] = ctxt.new_location (toyvmfn.fn_filename, |
| op->op_linenum, |
| 0); /* column */ |
| } |
| } |
| |
| /* Creating the function. */ |
| |
| void |
| compilation_state::create_function (const char *funcname) |
| { |
| std::vector <gccjit::param> params; |
| param_arg = ctxt.new_param (int_type, "arg", op_locs[0]); |
| params.push_back (param_arg); |
| fn = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED, |
| int_type, |
| funcname, |
| params, 0, |
| op_locs[0]); |
| |
| /* Create stack lvalues. */ |
| stack = fn.new_local (stack_type, "stack"); |
| stack_depth = fn.new_local (int_type, "stack_depth"); |
| x = fn.new_local (int_type, "x"); |
| y = fn.new_local (int_type, "y"); |
| |
| /* 1st pass: create blocks, one per opcode. */ |
| |
| /* We need an entry block to do one-time initialization, so create that |
| first. */ |
| initial_block = fn.new_block ("initial"); |
| |
| /* Create a block per operation. */ |
| for (int pc = 0; pc < toyvmfn.fn_num_ops; pc++) |
| { |
| char buf[16]; |
| sprintf (buf, "instr%i", pc); |
| op_blocks[pc] = fn.new_block (buf); |
| } |
| |
| /* Populate the initial block. */ |
| |
| /* "stack_depth = 0;". */ |
| initial_block.add_assignment (stack_depth, |
| ctxt.zero (int_type), |
| op_locs[0]); |
| |
| /* "PUSH (arg);". */ |
| add_push (initial_block, |
| param_arg, |
| op_locs[0]); |
| |
| /* ...and jump to insn 0. */ |
| initial_block.end_with_jump (op_blocks[0], |
| op_locs[0]); |
| |
| /* 2nd pass: fill in instructions. */ |
| for (int pc = 0; pc < toyvmfn.fn_num_ops; pc++) |
| { |
| gccjit::location loc = op_locs[pc]; |
| |
| gccjit::block block = op_blocks[pc]; |
| gccjit::block next_block = (pc < toyvmfn.fn_num_ops |
| ? op_blocks[pc + 1] |
| : NULL); |
| |
| toyvm_op *op; |
| op = &toyvmfn.fn_ops[pc]; |
| |
| /* Helper macros. */ |
| |
| #define X_EQUALS_POP()\ |
| add_pop (block, x, loc) |
| #define Y_EQUALS_POP()\ |
| add_pop (block, y, loc) |
| #define PUSH_RVALUE(RVALUE)\ |
| add_push (block, (RVALUE), loc) |
| #define PUSH_X()\ |
| PUSH_RVALUE (x) |
| #define PUSH_Y() \ |
| PUSH_RVALUE (y) |
| |
| block.add_comment (opcode_names[op->op_opcode], loc); |
| |
| /* Handle the individual opcodes. */ |
| |
| switch (op->op_opcode) |
| { |
| case DUP: |
| X_EQUALS_POP (); |
| PUSH_X (); |
| PUSH_X (); |
| break; |
| |
| case ROT: |
| Y_EQUALS_POP (); |
| X_EQUALS_POP (); |
| PUSH_Y (); |
| PUSH_X (); |
| break; |
| |
| case BINARY_ADD: |
| Y_EQUALS_POP (); |
| X_EQUALS_POP (); |
| PUSH_RVALUE ( |
| ctxt.new_binary_op ( |
| GCC_JIT_BINARY_OP_PLUS, |
| int_type, |
| x, y, |
| loc)); |
| break; |
| |
| case BINARY_SUBTRACT: |
| Y_EQUALS_POP (); |
| X_EQUALS_POP (); |
| PUSH_RVALUE ( |
| ctxt.new_binary_op ( |
| GCC_JIT_BINARY_OP_MINUS, |
| int_type, |
| x, y, |
| loc)); |
| break; |
| |
| case BINARY_MULT: |
| Y_EQUALS_POP (); |
| X_EQUALS_POP (); |
| PUSH_RVALUE ( |
| ctxt.new_binary_op ( |
| GCC_JIT_BINARY_OP_MULT, |
| int_type, |
| x, y, |
| loc)); |
| break; |
| |
| case BINARY_COMPARE_LT: |
| Y_EQUALS_POP (); |
| X_EQUALS_POP (); |
| PUSH_RVALUE ( |
| /* cast of bool to int */ |
| ctxt.new_cast ( |
| /* (x < y) as a bool */ |
| ctxt.new_comparison ( |
| GCC_JIT_COMPARISON_LT, |
| x, y, |
| loc), |
| int_type, |
| loc)); |
| break; |
| |
| case RECURSE: |
| { |
| X_EQUALS_POP (); |
| PUSH_RVALUE ( |
| ctxt.new_call ( |
| fn, |
| x, |
| loc)); |
| break; |
| } |
| |
| case RETURN: |
| X_EQUALS_POP (); |
| block.end_with_return (x, loc); |
| break; |
| |
| /* Ops taking an operand. */ |
| case PUSH_CONST: |
| PUSH_RVALUE ( |
| ctxt.new_rvalue (int_type, op->op_operand)); |
| break; |
| |
| case JUMP_ABS_IF_TRUE: |
| X_EQUALS_POP (); |
| block.end_with_conditional ( |
| /* "(bool)x". */ |
| ctxt.new_cast (x, bool_type, loc), |
| op_blocks[op->op_operand], /* on_true */ |
| next_block, /* on_false */ |
| loc); |
| break; |
| |
| default: |
| assert(0); |
| } /* end of switch on opcode */ |
| |
| /* Go to the next block. */ |
| if (op->op_opcode != JUMP_ABS_IF_TRUE |
| && op->op_opcode != RETURN) |
| block.end_with_jump (next_block, loc); |
| |
| } /* end of loop on PC locations. */ |
| } |
| |
| compilation_result |
| compilation_state::compile () |
| { |
| return ctxt.compile (); |
| } |
| |
| char test[1024]; |
| |
| #define CHECK_NON_NULL(PTR) \ |
| do { \ |
| if ((PTR) != NULL) \ |
| { \ |
| pass ("%s: %s is non-null", test, #PTR); \ |
| } \ |
| else \ |
| { \ |
| fail ("%s: %s is NULL", test, #PTR); \ |
| abort (); \ |
| } \ |
| } while (0) |
| |
| #define CHECK_VALUE(ACTUAL, EXPECTED) \ |
| do { \ |
| if ((ACTUAL) == (EXPECTED)) \ |
| { \ |
| pass ("%s: actual: %s == expected: %s", test, #ACTUAL, #EXPECTED); \ |
| } \ |
| else \ |
| { \ |
| fail ("%s: actual: %s != expected: %s", test, #ACTUAL, #EXPECTED); \ |
| fprintf (stderr, "incorrect value\n"); \ |
| abort (); \ |
| } \ |
| } while (0) |
| |
| static void |
| test_script (const char *scripts_dir, const char *script_name, int input, |
| int expected_result) |
| { |
| char *script_path; |
| toyvm_function *fn; |
| int interpreted_result; |
| toyvm_compiled_func code; |
| int compiled_result; |
| |
| snprintf (test, sizeof (test), "toyvm.cc: %s", script_name); |
| |
| script_path = (char *)malloc (strlen (scripts_dir) |
| + strlen (script_name) + 1); |
| CHECK_NON_NULL (script_path); |
| sprintf (script_path, "%s%s", scripts_dir, script_name); |
| |
| fn = toyvm_function::parse (script_path, script_name); |
| CHECK_NON_NULL (fn); |
| |
| interpreted_result = fn->interpret (input, NULL); |
| CHECK_VALUE (interpreted_result, expected_result); |
| |
| compilation_result compiler_result = fn->compile (); |
| |
| const char *funcname = fn->get_function_name (); |
| code = (toyvm_compiled_func)compiler_result.get_code (funcname); |
| CHECK_NON_NULL (code); |
| |
| compiled_result = code (input); |
| CHECK_VALUE (compiled_result, expected_result); |
| |
| free (script_path); |
| } |
| |
| #define PATH_TO_SCRIPTS ("/jit/docs/examples/tut04-toyvm/") |
| |
| static void |
| test_suite (void) |
| { |
| const char *srcdir; |
| char *scripts_dir; |
| |
| snprintf (test, sizeof (test), "toyvm.cc"); |
| |
| /* We need to locate the test scripts. |
| Rely on "srcdir" being set in the environment. */ |
| |
| srcdir = getenv ("srcdir"); |
| CHECK_NON_NULL (srcdir); |
| |
| scripts_dir = (char *)malloc (strlen (srcdir) + strlen(PATH_TO_SCRIPTS) |
| + 1); |
| CHECK_NON_NULL (scripts_dir); |
| sprintf (scripts_dir, "%s%s", srcdir, PATH_TO_SCRIPTS); |
| |
| test_script (scripts_dir, "factorial.toy", 10, 3628800); |
| test_script (scripts_dir, "fibonacci.toy", 10, 55); |
| |
| free (scripts_dir); |
| } |
| |
| int |
| main (int argc, char **argv) |
| { |
| const char *filename = NULL; |
| toyvm_function *fn = NULL; |
| |
| /* If called with no args, assume we're being run by the test suite. */ |
| if (argc < 3) |
| { |
| test_suite (); |
| return 0; |
| } |
| |
| if (argc != 3) |
| { |
| fprintf (stdout, |
| "%s FILENAME INPUT: Parse and run a .toy file\n", |
| argv[0]); |
| exit (1); |
| } |
| |
| filename = argv[1]; |
| fn = toyvm_function::parse (filename, filename); |
| if (!fn) |
| exit (1); |
| |
| if (0) |
| fn->disassemble (stdout); |
| |
| printf ("interpreter result: %d\n", |
| fn->interpret (atoi (argv[2]), NULL)); |
| |
| /* JIT-compilation. */ |
| compilation_result compiler_result = fn->compile (); |
| |
| const char *funcname = fn->get_function_name (); |
| toyvm_compiled_func code |
| = (toyvm_compiled_func)compiler_result.get_code (funcname); |
| |
| printf ("compiler result: %d\n", |
| code (atoi (argv[2]))); |
| |
| return 0; |
| } |