This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 21/27] Documentation: the "examples" subdirectory


This patch adds examples.  These examples are used included
by the documentation when it is built, and are run as code by the
testsuite, ensuring that the examples shown in the docs build and
run.

gcc/jit/
	* docs/examples/tut01-hello-world.c: New.
	* docs/examples/tut02-square.c: New.
	* docs/examples/tut03-sum-of-squares.c: New.
	* docs/examples/tut04-toyvm/Makefile: New.
	* docs/examples/tut04-toyvm/factorial.toy: New.
	* docs/examples/tut04-toyvm/fibonacci.toy: New.
	* docs/examples/tut04-toyvm/toyvm.c: New.
---
 gcc/jit/docs/examples/tut01-hello-world.c       | 123 ++++
 gcc/jit/docs/examples/tut02-square.c            | 107 +++
 gcc/jit/docs/examples/tut03-sum-of-squares.c    | 172 +++++
 gcc/jit/docs/examples/tut04-toyvm/Makefile      |  11 +
 gcc/jit/docs/examples/tut04-toyvm/factorial.toy |  50 ++
 gcc/jit/docs/examples/tut04-toyvm/fibonacci.toy |  66 ++
 gcc/jit/docs/examples/tut04-toyvm/toyvm.c       | 861 ++++++++++++++++++++++++
 7 files changed, 1390 insertions(+)
 create mode 100644 gcc/jit/docs/examples/tut01-hello-world.c
 create mode 100644 gcc/jit/docs/examples/tut02-square.c
 create mode 100644 gcc/jit/docs/examples/tut03-sum-of-squares.c
 create mode 100644 gcc/jit/docs/examples/tut04-toyvm/Makefile
 create mode 100644 gcc/jit/docs/examples/tut04-toyvm/factorial.toy
 create mode 100644 gcc/jit/docs/examples/tut04-toyvm/fibonacci.toy
 create mode 100644 gcc/jit/docs/examples/tut04-toyvm/toyvm.c

diff --git a/gcc/jit/docs/examples/tut01-hello-world.c b/gcc/jit/docs/examples/tut01-hello-world.c
new file mode 100644
index 0000000..49c9651
--- /dev/null
+++ b/gcc/jit/docs/examples/tut01-hello-world.c
@@ -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, &param_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, &param_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;
+}
diff --git a/gcc/jit/docs/examples/tut02-square.c b/gcc/jit/docs/examples/tut02-square.c
new file mode 100644
index 0000000..5eae179
--- /dev/null
+++ b/gcc/jit/docs/examples/tut02-square.c
@@ -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, &param_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;
+}
diff --git a/gcc/jit/docs/examples/tut03-sum-of-squares.c b/gcc/jit/docs/examples/tut03-sum-of-squares.c
new file mode 100644
index 0000000..594230b
--- /dev/null
+++ b/gcc/jit/docs/examples/tut03-sum-of-squares.c
@@ -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;
+}
diff --git a/gcc/jit/docs/examples/tut04-toyvm/Makefile b/gcc/jit/docs/examples/tut04-toyvm/Makefile
new file mode 100644
index 0000000..1b45c8d
--- /dev/null
+++ b/gcc/jit/docs/examples/tut04-toyvm/Makefile
@@ -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
diff --git a/gcc/jit/docs/examples/tut04-toyvm/factorial.toy b/gcc/jit/docs/examples/tut04-toyvm/factorial.toy
new file mode 100644
index 0000000..48e4034
--- /dev/null
+++ b/gcc/jit/docs/examples/tut04-toyvm/factorial.toy
@@ -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
diff --git a/gcc/jit/docs/examples/tut04-toyvm/fibonacci.toy b/gcc/jit/docs/examples/tut04-toyvm/fibonacci.toy
new file mode 100644
index 0000000..5ae0a40
--- /dev/null
+++ b/gcc/jit/docs/examples/tut04-toyvm/fibonacci.toy
@@ -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
diff --git a/gcc/jit/docs/examples/tut04-toyvm/toyvm.c b/gcc/jit/docs/examples/tut04-toyvm/toyvm.c
new file mode 100644
index 0000000..666bf2e
--- /dev/null
+++ b/gcc/jit/docs/examples/tut04-toyvm/toyvm.c
@@ -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;
+}
-- 
1.8.5.3


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]