aboutsummaryrefslogtreecommitdiff
path: root/gcc/jit/docs/examples/tut04-toyvm/toyvm.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/jit/docs/examples/tut04-toyvm/toyvm.cc')
-rw-r--r--gcc/jit/docs/examples/tut04-toyvm/toyvm.cc902
1 files changed, 902 insertions, 0 deletions
diff --git a/gcc/jit/docs/examples/tut04-toyvm/toyvm.cc b/gcc/jit/docs/examples/tut04-toyvm/toyvm.cc
new file mode 100644
index 0000000..3a9bbde
--- /dev/null
+++ b/gcc/jit/docs/examples/tut04-toyvm/toyvm.cc
@@ -0,0 +1,902 @@
+/* A simple stack-based virtual machine to demonstrate
+ JIT-compilation.
+ Copyright (C) 2014 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 <dejagnu.h>
+
+#include <libgccjit++.h>
+
+/* 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);
+
+ toyvm_compiled_func
+ compile ();
+
+private:
+ const char *fn_filename;
+ 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);
+}
+
+static char *
+get_function_name (const char *filename)
+{
+ /* Skip any path separators. */
+ const char *pathsep = strrchr (filename, '/');
+ if (pathsep)
+ filename = pathsep + 1;
+
+ /* Copy filename to funcname. */
+ char *funcname = (char *)malloc (strlen (filename) + 1);
+
+ strcpy (funcname, filename);
+
+ /* Convert "." to NIL terminator. */
+ *(strchr (funcname, '.')) = '\0';
+
+ return funcname;
+}
+
+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;
+
+ /* 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);
+ gcc_jit_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. */
+
+toyvm_compiled_func
+toyvm_function::compile ()
+{
+ compilation_state state (*this);
+ char *funcname;
+
+ funcname = get_function_name (fn_filename);
+
+ state.create_context ();
+ state.create_types ();
+ state.create_locations ();
+ state.create_function (funcname);
+
+ /* We've now finished populating the context. Compile it. */
+ gcc_jit_result *result = state.compile ();
+
+ return (toyvm_compiled_func)gcc_jit_result_get_code (result, funcname);
+ /* (this leaks "result" and "funcname") */
+}
+
+/* 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. */
+}
+
+gcc_jit_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);
+
+ code = fn->compile ();
+ 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. */
+ toyvm_compiled_func code = fn->compile ();
+ printf ("compiler result: %d\n",
+ code (atoi (argv[2])));
+
+ return 0;
+}