new file mode 100644
@@ -0,0 +1,123 @@
+/* Smoketest example for libgccjit.so
+ 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 <libgccjit.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+static void
+create_code (gcc_jit_context *ctxt)
+{
+ /* Let's try to inject the equivalent of:
+ void
+ greet (const char *name)
+ {
+ printf ("hello %s\n", name);
+ }
+ */
+ gcc_jit_type *void_type =
+ gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+ gcc_jit_type *const_char_ptr_type =
+ gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_CONST_CHAR_PTR);
+ gcc_jit_param *param_name =
+ gcc_jit_context_new_param (ctxt, NULL, const_char_ptr_type, "name");
+ gcc_jit_function *func =
+ gcc_jit_context_new_function (ctxt, NULL,
+ GCC_JIT_FUNCTION_EXPORTED,
+ void_type,
+ "greet",
+ 1, ¶m_name,
+ 0);
+
+ gcc_jit_param *param_format =
+ gcc_jit_context_new_param (ctxt, NULL, const_char_ptr_type, "format");
+ gcc_jit_function *printf_func =
+ gcc_jit_context_new_function (ctxt, NULL,
+ GCC_JIT_FUNCTION_IMPORTED,
+ gcc_jit_context_get_type (
+ ctxt, GCC_JIT_TYPE_INT),
+ "printf",
+ 1, ¶m_format,
+ 1);
+ gcc_jit_rvalue *args[2];
+ args[0] = gcc_jit_context_new_string_literal (ctxt, "hello %s\n");
+ args[1] = gcc_jit_param_as_rvalue (param_name);
+
+ gcc_jit_block *block = gcc_jit_function_new_block (func, NULL);
+
+ gcc_jit_block_add_eval (
+ block, NULL,
+ gcc_jit_context_new_call (ctxt,
+ NULL,
+ printf_func,
+ 2, args));
+ gcc_jit_block_end_with_void_return (block, NULL);
+}
+
+int
+main (int argc, char **argv)
+{
+ gcc_jit_context *ctxt;
+ gcc_jit_result *result;
+
+ /* Get a "context" object for working with the library. */
+ ctxt = gcc_jit_context_acquire ();
+ if (!ctxt)
+ {
+ fprintf (stderr, "NULL ctxt");
+ exit (1);
+ }
+
+ /* Set some options on the context.
+ Let's see the code being generated, in assembler form. */
+ gcc_jit_context_set_bool_option (
+ ctxt,
+ GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE,
+ 0);
+
+ /* Populate the context. */
+ create_code (ctxt);
+
+ /* Compile the code. */
+ result = gcc_jit_context_compile (ctxt);
+ if (!result)
+ {
+ fprintf (stderr, "NULL result");
+ exit (1);
+ }
+
+ /* Extract the generated code from "result". */
+ typedef void (*fn_type) (const char *);
+ fn_type greet =
+ (fn_type)gcc_jit_result_get_code (result, "greet");
+ if (!greet)
+ {
+ fprintf (stderr, "NULL greet");
+ exit (1);
+ }
+
+ /* Now call the generated function: */
+ greet ("world");
+ fflush (stdout);
+
+ gcc_jit_context_release (ctxt);
+ gcc_jit_result_release (result);
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,107 @@
+/* Usage example for libgccjit.so
+ 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 <libgccjit.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+void
+create_code (gcc_jit_context *ctxt)
+{
+ /* Let's try to inject the equivalent of:
+
+ int square (int i)
+ {
+ return i * i;
+ }
+ */
+ gcc_jit_type *int_type =
+ gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+ gcc_jit_param *param_i =
+ gcc_jit_context_new_param (ctxt, NULL, int_type, "i");
+ gcc_jit_function *func =
+ gcc_jit_context_new_function (ctxt, NULL,
+ GCC_JIT_FUNCTION_EXPORTED,
+ int_type,
+ "square",
+ 1, ¶m_i,
+ 0);
+
+ gcc_jit_block *block = gcc_jit_function_new_block (func, NULL);
+
+ gcc_jit_rvalue *expr =
+ gcc_jit_context_new_binary_op (
+ ctxt, NULL,
+ GCC_JIT_BINARY_OP_MULT, int_type,
+ gcc_jit_param_as_rvalue (param_i),
+ gcc_jit_param_as_rvalue (param_i));
+
+ gcc_jit_block_end_with_return (block, NULL, expr);
+}
+
+int
+main (int argc, char **argv)
+{
+ gcc_jit_context *ctxt = NULL;
+ gcc_jit_result *result = NULL;
+
+ /* Get a "context" object for working with the library. */
+ ctxt = gcc_jit_context_acquire ();
+ if (!ctxt)
+ {
+ fprintf (stderr, "NULL ctxt");
+ goto error;
+ }
+
+ /* Set some options on the context.
+ Let's see the code being generated, in assembler form. */
+ gcc_jit_context_set_bool_option (
+ ctxt,
+ GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE,
+ 0);
+
+ /* Populate the context. */
+ create_code (ctxt);
+
+ /* Compile the code. */
+ result = gcc_jit_context_compile (ctxt);
+ if (!result)
+ {
+ fprintf (stderr, "NULL result");
+ goto error;
+ }
+
+ /* Extract the generated code from "result". */
+ void *fn_ptr = gcc_jit_result_get_code (result, "square");
+ if (!fn_ptr)
+ {
+ fprintf (stderr, "NULL fn_ptr");
+ goto error;
+ }
+
+ typedef int (*fn_type) (int);
+ fn_type square = (fn_type)fn_ptr;
+ printf ("result: %d", square (5));
+
+ error:
+ gcc_jit_context_release (ctxt);
+ gcc_jit_result_release (result);
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,172 @@
+/* Usage example for libgccjit.so
+ 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 <libgccjit.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+void
+create_code (gcc_jit_context *ctxt)
+{
+ /*
+ Simple sum-of-squares, to test conditionals and looping
+
+ int loop_test (int n)
+ {
+ int i;
+ int sum = 0;
+ for (i = 0; i < n ; i ++)
+ {
+ sum += i * i;
+ }
+ return sum;
+ */
+ gcc_jit_type *the_type =
+ gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+ gcc_jit_type *return_type = the_type;
+
+ gcc_jit_param *n =
+ gcc_jit_context_new_param (ctxt, NULL, the_type, "n");
+ gcc_jit_param *params[1] = {n};
+ gcc_jit_function *func =
+ gcc_jit_context_new_function (ctxt, NULL,
+ GCC_JIT_FUNCTION_EXPORTED,
+ return_type,
+ "loop_test",
+ 1, params, 0);
+
+ /* Build locals: */
+ gcc_jit_lvalue *i =
+ gcc_jit_function_new_local (func, NULL, the_type, "i");
+ gcc_jit_lvalue *sum =
+ gcc_jit_function_new_local (func, NULL, the_type, "sum");
+
+ gcc_jit_block *b_initial =
+ gcc_jit_function_new_block (func, "initial");
+ gcc_jit_block *b_loop_cond =
+ gcc_jit_function_new_block (func, "loop_cond");
+ gcc_jit_block *b_loop_body =
+ gcc_jit_function_new_block (func, "loop_body");
+ gcc_jit_block *b_after_loop =
+ gcc_jit_function_new_block (func, "after_loop");
+
+ /* sum = 0; */
+ gcc_jit_block_add_assignment (
+ b_initial, NULL,
+ sum,
+ gcc_jit_context_zero (ctxt, the_type));
+
+ /* i = 0; */
+ gcc_jit_block_add_assignment (
+ b_initial, NULL,
+ i,
+ gcc_jit_context_zero (ctxt, the_type));
+
+ gcc_jit_block_end_with_jump (b_initial, NULL, b_loop_cond);
+
+ /* if (i >= n) */
+ gcc_jit_block_end_with_conditional (
+ b_loop_cond, NULL,
+ gcc_jit_context_new_comparison (
+ ctxt, NULL,
+ GCC_JIT_COMPARISON_GE,
+ gcc_jit_lvalue_as_rvalue (i),
+ gcc_jit_param_as_rvalue (n)),
+ b_after_loop,
+ b_loop_body);
+
+ /* sum += i * i */
+ gcc_jit_block_add_assignment_op (
+ b_loop_body, NULL,
+ sum,
+ GCC_JIT_BINARY_OP_PLUS,
+ gcc_jit_context_new_binary_op (
+ ctxt, NULL,
+ GCC_JIT_BINARY_OP_MULT, the_type,
+ gcc_jit_lvalue_as_rvalue (i),
+ gcc_jit_lvalue_as_rvalue (i)));
+
+ /* i++ */
+ gcc_jit_block_add_assignment_op (
+ b_loop_body, NULL,
+ i,
+ GCC_JIT_BINARY_OP_PLUS,
+ gcc_jit_context_one (ctxt, the_type));
+
+ gcc_jit_block_end_with_jump (b_loop_body, NULL, b_loop_cond);
+
+ /* return sum */
+ gcc_jit_block_end_with_return (
+ b_after_loop,
+ NULL,
+ gcc_jit_lvalue_as_rvalue (sum));
+}
+
+int
+main (int argc, char **argv)
+{
+ gcc_jit_context *ctxt = NULL;
+ gcc_jit_result *result = NULL;
+
+ /* Get a "context" object for working with the library. */
+ ctxt = gcc_jit_context_acquire ();
+ if (!ctxt)
+ {
+ fprintf (stderr, "NULL ctxt");
+ goto error;
+ }
+
+ /* Set some options on the context.
+ Let's see the code being generated, in assembler form. */
+ gcc_jit_context_set_bool_option (
+ ctxt,
+ GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE,
+ 0);
+
+ /* Populate the context. */
+ create_code (ctxt);
+
+ /* Compile the code. */
+ result = gcc_jit_context_compile (ctxt);
+ if (!result)
+ {
+ fprintf (stderr, "NULL result");
+ goto error;
+ }
+
+ /* Extract the generated code from "result". */
+ typedef int (*loop_test_fn_type) (int);
+ loop_test_fn_type loop_test =
+ (loop_test_fn_type)gcc_jit_result_get_code (result, "loop_test");
+ if (!loop_test)
+ {
+ fprintf (stderr, "NULL loop_test");
+ goto error;
+ }
+
+ /* Run the generated code. */
+ int val = loop_test (10);
+ printf("loop_test returned: %d\n", val);
+
+ error:
+ gcc_jit_context_release (ctxt);
+ gcc_jit_result_release (result);
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,11 @@
+factorial: toyvm
+ ./toyvm factorial.toy 10
+
+fibonacci: toyvm
+ ./toyvm fibonacci.toy 8
+
+toyvm: toyvm.c Makefile
+ g++ -Wall -g -o $@ $< $(shell pkg-config --cflags --libs libgccjit)
+
+clean:
+ rm -f *.o toyvm
new file mode 100644
@@ -0,0 +1,50 @@
+# Simple recursive factorial implementation, roughly equivalent to:
+#
+# int factorial (int arg)
+# {
+# if (arg < 2)
+# return arg
+# return arg * factorial (arg - 1)
+# }
+
+# Initial state:
+# stack: [arg]
+
+# 0:
+DUP
+# stack: [arg, arg]
+
+# 1:
+PUSH_CONST 2
+# stack: [arg, arg, 2]
+
+# 2:
+BINARY_COMPARE_LT
+# stack: [arg, (arg < 2)]
+
+# 3:
+JUMP_ABS_IF_TRUE 9
+# stack: [arg]
+
+# 4:
+DUP
+# stack: [arg, arg]
+
+# 5:
+PUSH_CONST 1
+# stack: [arg, arg, 1]
+
+# 6:
+BINARY_SUBTRACT
+# stack: [arg, (arg - 1)
+
+# 7:
+RECURSE
+# stack: [arg, factorial(arg - 1)]
+
+# 8:
+BINARY_MULT
+# stack: [arg * factorial(arg - 1)]
+
+# 9:
+RETURN
new file mode 100644
@@ -0,0 +1,66 @@
+# Simple recursive fibonacci implementation, roughly equivalent to:
+#
+# int fibonacci (int arg)
+# {
+# if (arg < 2)
+# return arg
+# return fibonacci (arg-1) + fibonacci (arg-2)
+# }
+
+# Initial state:
+# stack: [arg]
+
+# 0:
+DUP
+# stack: [arg, arg]
+
+# 1:
+PUSH_CONST 2
+# stack: [arg, arg, 2]
+
+# 2:
+BINARY_COMPARE_LT
+# stack: [arg, (arg < 2)]
+
+# 3:
+JUMP_ABS_IF_TRUE 13
+# stack: [arg]
+
+# 4:
+DUP
+# stack: [arg, arg]
+
+# 5:
+PUSH_CONST 1
+# stack: [arg, arg, 1]
+
+# 6:
+BINARY_SUBTRACT
+# stack: [arg, (arg - 1)
+
+# 7:
+RECURSE
+# stack: [arg, fib(arg - 1)]
+
+# 8:
+ROT
+# stack: [fib(arg - 1), arg]
+
+# 9:
+PUSH_CONST 2
+# stack: [fib(arg - 1), arg, 2]
+
+# 10:
+BINARY_SUBTRACT
+# stack: [fib(arg - 1), arg, (arg - 2)
+
+# 11:
+RECURSE
+# stack: [fib(arg - 1), fib(arg - 1)]
+
+# 12:
+BINARY_ADD
+# stack: [fib(arg - 1) + fib(arg - 1)]
+
+# 13:
+RETURN
new file mode 100644
@@ -0,0 +1,861 @@
+/* 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>
+
+/* Typedefs. */
+typedef struct toyvm_op toyvm_op;
+typedef struct toyvm_function toyvm_function;
+typedef struct toyvm_frame toyvm_frame;
+typedef struct compilation_state compilation_state;
+
+/* 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)
+
+struct toyvm_function
+{
+ const char *fn_filename;
+ int fn_num_ops;
+ toyvm_op fn_ops[MAX_OPS];
+};
+
+#define MAX_STACK_DEPTH (8)
+
+struct toyvm_frame
+{
+ toyvm_function *frm_function;
+ int frm_pc;
+ int frm_stack[MAX_STACK_DEPTH];
+ int frm_cur_depth;
+};
+
+static void
+add_op (toyvm_function *fn, enum opcode opcode,
+ int operand, int linenum)
+{
+ toyvm_op *op;
+ assert (fn->fn_num_ops < MAX_OPS);
+ op = &fn->fn_ops[fn->fn_num_ops++];
+ op->op_opcode = opcode;
+ op->op_operand = operand;
+ op->op_linenum = linenum;
+}
+
+static void
+add_unary_op (toyvm_function *fn, enum opcode opcode,
+ const char *rest_of_line, int linenum)
+{
+ int operand = atoi (rest_of_line);
+ add_op (fn, opcode, operand, linenum);
+}
+
+static 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 = name;
+
+ /* 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"))
+ add_op (fn, DUP, 0, linenum);
+ else if (LINE_MATCHES ("ROT\n"))
+ add_op (fn, ROT, 0, linenum);
+ else if (LINE_MATCHES ("BINARY_ADD\n"))
+ add_op (fn, BINARY_ADD, 0, linenum);
+ else if (LINE_MATCHES ("BINARY_SUBTRACT\n"))
+ add_op (fn, BINARY_SUBTRACT, 0, linenum);
+ else if (LINE_MATCHES ("BINARY_MULT\n"))
+ add_op (fn, BINARY_MULT, 0, linenum);
+ else if (LINE_MATCHES ("BINARY_COMPARE_LT\n"))
+ add_op (fn, BINARY_COMPARE_LT, 0, linenum);
+ else if (LINE_MATCHES ("RECURSE\n"))
+ add_op (fn, RECURSE, 0, linenum);
+ else if (LINE_MATCHES ("RETURN\n"))
+ add_op (fn, RETURN, 0, linenum);
+ else if (LINE_MATCHES ("PUSH_CONST "))
+ add_unary_op (fn, PUSH_CONST,
+ line + strlen ("PUSH_CONST "), linenum);
+ else if (LINE_MATCHES ("JUMP_ABS_IF_TRUE "))
+ add_unary_op (fn, 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);
+ fclose (f);
+ free (fn);
+ return NULL;
+}
+
+static void
+toyvm_function_disassemble_op (toyvm_function *fn, toyvm_op *op, int index, FILE *out)
+{
+ fprintf (out, "%s:%d: index %d: %s",
+ fn->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");
+}
+
+static void
+toyvm_function_disassemble (toyvm_function *fn, FILE *out)
+{
+ int i;
+ for (i = 0; i < fn->fn_num_ops; i++)
+ {
+ toyvm_op *op = &fn->fn_ops[i];
+ toyvm_function_disassemble_op (fn, op, i, out);
+ }
+}
+
+static void
+toyvm_frame_push (toyvm_frame *frame, int arg)
+{
+ assert (frame->frm_cur_depth < MAX_STACK_DEPTH);
+ frame->frm_stack[frame->frm_cur_depth++] = arg;
+}
+
+static int
+toyvm_frame_pop (toyvm_frame *frame)
+{
+ assert (frame->frm_cur_depth > 0);
+ return frame->frm_stack[--frame->frm_cur_depth];
+}
+
+static void
+toyvm_frame_dump_stack (toyvm_frame *frame, FILE *out)
+{
+ int i;
+ fprintf (out, "stack:");
+ for (i = 0; i < frame->frm_cur_depth; i++)
+ {
+ fprintf (out, " %d", frame->frm_stack[i]);
+ }
+ fprintf (out, "\n");
+}
+
+/* Execute the given function. */
+
+static int
+toyvm_function_interpret (toyvm_function *fn, int arg, FILE *trace)
+{
+ toyvm_frame frame;
+#define PUSH(ARG) (toyvm_frame_push (&frame, (ARG)))
+#define POP(ARG) (toyvm_frame_pop (&frame))
+
+ frame.frm_function = fn;
+ frame.frm_pc = 0;
+ frame.frm_cur_depth = 0;
+
+ PUSH (arg);
+
+ while (1)
+ {
+ toyvm_op *op;
+ int x, y;
+ assert (frame.frm_pc < fn->fn_num_ops);
+ op = &fn->fn_ops[frame.frm_pc++];
+
+ if (trace)
+ {
+ toyvm_frame_dump_stack (&frame, trace);
+ toyvm_function_disassemble_op (fn, 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 = toyvm_function_interpret (fn, 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. */
+
+struct compilation_state
+{
+ gcc_jit_context *ctxt;
+
+ gcc_jit_type *int_type;
+ gcc_jit_type *bool_type;
+ gcc_jit_type *stack_type; /* int[MAX_STACK_DEPTH] */
+
+ gcc_jit_rvalue *const_one;
+
+ gcc_jit_function *fn;
+ gcc_jit_param *param_arg;
+ gcc_jit_lvalue *stack;
+ gcc_jit_lvalue *stack_depth;
+ gcc_jit_lvalue *x;
+ gcc_jit_lvalue *y;
+
+ gcc_jit_location *op_locs[MAX_OPS];
+ gcc_jit_block *initial_block;
+ gcc_jit_block *op_blocks[MAX_OPS];
+
+};
+
+/* Stack manipulation. */
+
+static void
+add_push (compilation_state *state,
+ gcc_jit_block *block,
+ gcc_jit_rvalue *rvalue,
+ gcc_jit_location *loc)
+{
+ /* stack[stack_depth] = RVALUE */
+ gcc_jit_block_add_assignment (
+ block,
+ loc,
+ /* stack[stack_depth] */
+ gcc_jit_context_new_array_access (
+ state->ctxt,
+ loc,
+ gcc_jit_lvalue_as_rvalue (state->stack),
+ gcc_jit_lvalue_as_rvalue (state->stack_depth)),
+ rvalue);
+
+ /* "stack_depth++;". */
+ gcc_jit_block_add_assignment_op (
+ block,
+ loc,
+ state->stack_depth,
+ GCC_JIT_BINARY_OP_PLUS,
+ state->const_one);
+}
+
+static void
+add_pop (compilation_state *state,
+ gcc_jit_block *block,
+ gcc_jit_lvalue *lvalue,
+ gcc_jit_location *loc)
+{
+ /* "--stack_depth;". */
+ gcc_jit_block_add_assignment_op (
+ block,
+ loc,
+ state->stack_depth,
+ GCC_JIT_BINARY_OP_MINUS,
+ state->const_one);
+
+ /* "LVALUE = stack[stack_depth];". */
+ gcc_jit_block_add_assignment (
+ block,
+ loc,
+ lvalue,
+ /* stack[stack_depth] */
+ gcc_jit_lvalue_as_rvalue (
+ gcc_jit_context_new_array_access (
+ state->ctxt,
+ loc,
+ gcc_jit_lvalue_as_rvalue (state->stack),
+ gcc_jit_lvalue_as_rvalue (state->stack_depth))));
+}
+
+/* The main compilation hook. */
+
+static toyvm_compiled_func
+toyvm_function_compile (toyvm_function *fn)
+{
+ compilation_state state;
+ int pc;
+ char *funcname;
+
+ memset (&state, 0, sizeof (state));
+
+ /* Copy filename to funcname. */
+ funcname = (char *)malloc (strlen (fn->fn_filename) + 1);
+ strcpy (funcname, fn->fn_filename);
+
+ /* Convert "." to NIL terminator. */
+ *(strchr (funcname, '.')) = '\0';
+
+ state.ctxt = gcc_jit_context_acquire ();
+
+ gcc_jit_context_set_bool_option (state.ctxt,
+ GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE,
+ 0);
+ gcc_jit_context_set_bool_option (state.ctxt,
+ GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE,
+ 0);
+ gcc_jit_context_set_int_option (state.ctxt,
+ GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL,
+ 3);
+ gcc_jit_context_set_bool_option (state.ctxt,
+ GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES,
+ 0);
+ gcc_jit_context_set_bool_option (state.ctxt,
+ GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING,
+ 0);
+ gcc_jit_context_set_bool_option (state.ctxt,
+ GCC_JIT_BOOL_OPTION_DEBUGINFO,
+ 1);
+
+ /* Create types. */
+ state.int_type =
+ gcc_jit_context_get_type (state.ctxt, GCC_JIT_TYPE_INT);
+ state.bool_type =
+ gcc_jit_context_get_type (state.ctxt, GCC_JIT_TYPE_BOOL);
+ state.stack_type =
+ gcc_jit_context_new_array_type (state.ctxt, NULL,
+ state.int_type, MAX_STACK_DEPTH);
+
+ /* The constant value 1. */
+ state.const_one = gcc_jit_context_one (state.ctxt, state.int_type);
+
+ /* Create locations. */
+ for (pc = 0; pc < fn->fn_num_ops; pc++)
+ {
+ toyvm_op *op = &fn->fn_ops[pc];
+
+ state.op_locs[pc] = gcc_jit_context_new_location (state.ctxt,
+ fn->fn_filename,
+ op->op_linenum,
+ 0); /* column */
+ }
+
+ /* Creating the function. */
+ state.param_arg =
+ gcc_jit_context_new_param (state.ctxt, state.op_locs[0],
+ state.int_type, "arg");
+ state.fn =
+ gcc_jit_context_new_function (state.ctxt,
+ state.op_locs[0],
+ GCC_JIT_FUNCTION_EXPORTED,
+ state.int_type,
+ funcname,
+ 1, &state.param_arg, 0);
+
+ /* Create stack lvalues. */
+ state.stack =
+ gcc_jit_function_new_local (state.fn, NULL,
+ state.stack_type, "stack");
+ state.stack_depth =
+ gcc_jit_function_new_local (state.fn, NULL,
+ state.int_type, "stack_depth");
+ state.x =
+ gcc_jit_function_new_local (state.fn, NULL,
+ state.int_type, "x");
+ state.y =
+ gcc_jit_function_new_local (state.fn, NULL,
+ state.int_type, "y");
+
+ /* 1st pass: create blocks, one per opcode. */
+
+ /* We need an entry block to do one-time initialization, so create that
+ first. */
+ state.initial_block = gcc_jit_function_new_block (state.fn, "initial");
+
+ /* Create a block per operation. */
+ for (pc = 0; pc < fn->fn_num_ops; pc++)
+ {
+ char buf[16];
+ sprintf (buf, "instr%i", pc);
+ state.op_blocks[pc] = gcc_jit_function_new_block (state.fn, buf);
+ }
+
+ /* Populate the initial block. */
+
+ /* "stack_depth = 0;". */
+ gcc_jit_block_add_assignment (
+ state.initial_block,
+ state.op_locs[0],
+ state.stack_depth,
+ gcc_jit_context_zero (state.ctxt, state.int_type));
+
+ /* "PUSH (arg);". */
+ add_push (&state,
+ state.initial_block,
+ gcc_jit_param_as_rvalue (state.param_arg),
+ state.op_locs[0]);
+
+ /* ...and jump to insn 0. */
+ gcc_jit_block_end_with_jump (state.initial_block,
+ state.op_locs[0],
+ state.op_blocks[0]);
+
+ /* 2nd pass: fill in instructions. */
+ for (pc = 0; pc < fn->fn_num_ops; pc++)
+ {
+ gcc_jit_location *loc = state.op_locs[pc];
+
+ gcc_jit_block *block = state.op_blocks[pc];
+ gcc_jit_block *next_block = (pc < fn->fn_num_ops
+ ? state.op_blocks[pc + 1]
+ : NULL);
+
+ toyvm_op *op;
+ op = &fn->fn_ops[pc];
+
+ /* Helper macros. */
+
+#define X_EQUALS_POP()\
+ add_pop (&state, block, state.x, loc)
+#define Y_EQUALS_POP()\
+ add_pop (&state, block, state.y, loc)
+#define PUSH_RVALUE(RVALUE)\
+ add_push (&state, block, (RVALUE), loc)
+#define PUSH_X()\
+ PUSH_RVALUE (gcc_jit_lvalue_as_rvalue (state.x))
+#define PUSH_Y() \
+ PUSH_RVALUE (gcc_jit_lvalue_as_rvalue (state.y))
+
+ gcc_jit_block_add_comment (block, loc, opcode_names[op->op_opcode]);
+
+ /* 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 (
+ gcc_jit_context_new_binary_op (
+ state.ctxt,
+ loc,
+ GCC_JIT_BINARY_OP_PLUS,
+ state.int_type,
+ gcc_jit_lvalue_as_rvalue (state.x),
+ gcc_jit_lvalue_as_rvalue (state.y)));
+ break;
+
+ case BINARY_SUBTRACT:
+ Y_EQUALS_POP ();
+ X_EQUALS_POP ();
+ PUSH_RVALUE (
+ gcc_jit_context_new_binary_op (
+ state.ctxt,
+ loc,
+ GCC_JIT_BINARY_OP_MINUS,
+ state.int_type,
+ gcc_jit_lvalue_as_rvalue (state.x),
+ gcc_jit_lvalue_as_rvalue (state.y)));
+ break;
+
+ case BINARY_MULT:
+ Y_EQUALS_POP ();
+ X_EQUALS_POP ();
+ PUSH_RVALUE (
+ gcc_jit_context_new_binary_op (
+ state.ctxt,
+ loc,
+ GCC_JIT_BINARY_OP_MULT,
+ state.int_type,
+ gcc_jit_lvalue_as_rvalue (state.x),
+ gcc_jit_lvalue_as_rvalue (state.y)));
+ break;
+
+ case BINARY_COMPARE_LT:
+ Y_EQUALS_POP ();
+ X_EQUALS_POP ();
+ PUSH_RVALUE (
+ /* cast of bool to int */
+ gcc_jit_context_new_cast (
+ state.ctxt,
+ loc,
+ /* (x < y) as a bool */
+ gcc_jit_context_new_comparison (
+ state.ctxt,
+ loc,
+ GCC_JIT_COMPARISON_LT,
+ gcc_jit_lvalue_as_rvalue (state.x),
+ gcc_jit_lvalue_as_rvalue (state.y)),
+ state.int_type));
+ break;
+
+ case RECURSE:
+ {
+ X_EQUALS_POP ();
+ gcc_jit_rvalue *arg = gcc_jit_lvalue_as_rvalue (state.x);
+ PUSH_RVALUE (
+ gcc_jit_context_new_call (
+ state.ctxt,
+ loc,
+ state.fn,
+ 1, &arg));
+ break;
+ }
+
+ case RETURN:
+ X_EQUALS_POP ();
+ gcc_jit_block_end_with_return (
+ block,
+ loc,
+ gcc_jit_lvalue_as_rvalue (state.x));
+ break;
+
+ /* Ops taking an operand. */
+ case PUSH_CONST:
+ PUSH_RVALUE (
+ gcc_jit_context_new_rvalue_from_int (
+ state.ctxt,
+ state.int_type,
+ op->op_operand));
+ break;
+
+ case JUMP_ABS_IF_TRUE:
+ X_EQUALS_POP ();
+ gcc_jit_block_end_with_conditional (
+ block,
+ loc,
+ /* "(bool)x". */
+ gcc_jit_context_new_cast (
+ state.ctxt,
+ loc,
+ gcc_jit_lvalue_as_rvalue (state.x),
+ state.bool_type),
+ state.op_blocks[op->op_operand], /* on_true */
+ next_block); /* on_false */
+ 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)
+ gcc_jit_block_end_with_jump (
+ block,
+ loc,
+ next_block);
+
+ } /* end of loop on PC locations. */
+
+ /* We've now finished populating the context. Compile it. */
+ gcc_jit_result *result = gcc_jit_context_compile (state.ctxt);
+ gcc_jit_context_release (state.ctxt);
+
+ return (toyvm_compiled_func)gcc_jit_result_get_code (result,
+ funcname);
+ /* (this leaks "result" and "funcname") */
+}
+
+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.c: %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 = toyvm_function_interpret (fn, input, NULL);
+ CHECK_VALUE (interpreted_result, expected_result);
+
+ code = toyvm_function_compile (fn);
+ 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.c");
+
+ /* 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)
+ toyvm_function_disassemble (fn, stdout);
+
+ printf ("interpreter result: %d\n",
+ toyvm_function_interpret (fn, atoi (argv[2]), NULL));
+
+ /* JIT-compilation. */
+ toyvm_compiled_func code = toyvm_function_compile (fn);
+ printf ("compiler result: %d\n",
+ code (atoi (argv[2])));
+
+ return 0;
+}