[jit] Implement nested jit-compilation contexts

David Malcolm dmalcolm@redhat.com
Tue Jan 28 22:01:00 GMT 2014


Committed to dmalcolm/jit:

As discussed in http://gcc.gnu.org/ml/jit/2014-q1/msg00001.html,
experiments with adding libgccjit to GNU Octave revealed the absence of
a way for a user of libgccjit to separate one-time startup from
per-invocation activities.

For example, the existing GNU Octave JIT has a singleton class
"jit_typeinfo" which is created once per process, containing the JIT
representation of types, helper functions and other such global
declarations.   When a function or loop becomes "hot", the JIT uses the
objects referenced by this singleton - for example, a type-inferencer
uses the instances of types owned by the jit_typeinfo singleton, giving
back an IR for the function in terms of these type object instances.

As of 96b218c9a1d5f39fb649e02c0e77586b180e8516, libgccjit's entities now
have lifetimes bounded by gcc_jit_context objects, rather than within
the activation frame of a callback.

This next commit adds a gcc_jit_context_new_child_context API entrypoint,
allowing client code to create nested contexts: one-time initialization
can be done in a parent context, and per-method JIT-compilation can be
done in a child context.  Child contexts can use entities from their
parent context (or, indeed, ancestor contexts), though not vice-versa.

Currently contexts can be arbitrarily nested, but I believe it will be
unlikely for client code to need a nesting structure more complex than
that of a singleton parent context with multiple child contexts.

Implementation-wise, this is all rather suboptimal, requiring repeated
playback of parent contexts, but fixing that would require deep surgery
to GCC proper e.g. being able to share GC heaps between in-process
invocations of the compiler.   I believe this API at least allows the
future possibility of such a refactoring internally without necessarily
having to break clients.

gcc/jit/
	* libgccjit.h (gcc_jit_context_new_child_context): New function.

	* libgccjit.map (gcc_jit_context_new_child_context): New function.

	* libgccjit.c (gcc_jit_context): Make the constructor explicit,
	with a parent context as a parameter.
	(gcc_jit_context_acquire): Create context with a NULL parent.
	(gcc_jit_context_new_child_context): New function, creating a
	context with the given parent.

	* internal-api.h (gcc::jit::recording::context::context): New
	explicit constructor, taking a parent context as a parameter.
	(gcc::jit::recording::context::m_parent_ctxt): New field.

	* internal-api.c (gcc::jit::recording::context::context): New
	explicit constructor, taking a parent context as a parameter.
	(gcc::jit::recording::context::replay_into): Replay parent contexts
	before replaying the context itself.

gcc/testsuite/
	* jit.dg/harness.h (test_jit): Add the possibility of turning off
	this function, if the newly-coined "TEST_ESCHEWS_TEST_JIT" is
	defined, for use by...
	* jit.dg/test-nested-contexts.c: New test case, adapting
	test-quadratic.c, but splitting it into a 3-deep arrangement of
	nested contexts, to test the implementation of child contexts.
---
 gcc/jit/ChangeLog.jit                       |  21 +
 gcc/jit/internal-api.c                      |  49 +++
 gcc/jit/internal-api.h                      |   3 +
 gcc/jit/libgccjit.c                         |  11 +-
 gcc/jit/libgccjit.h                         |  34 ++
 gcc/jit/libgccjit.map                       |   1 +
 gcc/testsuite/ChangeLog.jit                 |   9 +
 gcc/testsuite/jit.dg/harness.h              |   2 +
 gcc/testsuite/jit.dg/test-nested-contexts.c | 627 ++++++++++++++++++++++++++++
 9 files changed, 756 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/jit.dg/test-nested-contexts.c

diff --git a/gcc/jit/ChangeLog.jit b/gcc/jit/ChangeLog.jit
index ed0ffb7..67856f4 100644
--- a/gcc/jit/ChangeLog.jit
+++ b/gcc/jit/ChangeLog.jit
@@ -1,3 +1,24 @@
+2014-01-28  David Malcolm  <dmalcolm@redhat.com>
+
+	* libgccjit.h (gcc_jit_context_new_child_context): New function.
+
+	* libgccjit.map (gcc_jit_context_new_child_context): New function.
+
+	* libgccjit.c (gcc_jit_context): Make the constructor explicit,
+	with a parent context as a parameter.
+	(gcc_jit_context_acquire): Create context with a NULL parent.
+	(gcc_jit_context_new_child_context): New function, creating a
+	context with the given parent.
+
+	* internal-api.h (gcc::jit::recording::context::context): New
+	explicit constructor, taking a parent context as a parameter.
+	(gcc::jit::recording::context::m_parent_ctxt): New field.
+
+	* internal-api.c (gcc::jit::recording::context::context): New
+	explicit constructor, taking a parent context as a parameter.
+	(gcc::jit::recording::context::replay_into): Replay parent contexts
+	before replaying the context itself.
+
 2014-01-27  David Malcolm  <dmalcolm@redhat.com>
 
 	* internal-api.c (gcc::jit::playback::context::compile): Removal
diff --git a/gcc/jit/internal-api.c b/gcc/jit/internal-api.c
index 09b4415..30c578c 100644
--- a/gcc/jit/internal-api.c
+++ b/gcc/jit/internal-api.c
@@ -57,6 +57,35 @@ recording::playback_label (recording::label *lab)
 
 /* gcc::jit::recording::context:: */
 
+recording::context::context (context *parent_ctxt)
+  : m_parent_ctxt (parent_ctxt),
+    m_error_count (0),
+    m_mementos ()
+{
+  m_first_error_str[0] = '\0';
+
+  if (parent_ctxt)
+    {
+      /* Inherit options from parent.
+         Note that the first memcpy means copying pointers to strings.  */
+      memcpy (m_str_options,
+              parent_ctxt->m_str_options,
+              sizeof (m_str_options));
+      memcpy (m_int_options,
+              parent_ctxt->m_int_options,
+              sizeof (m_int_options));
+      memcpy (m_bool_options,
+              parent_ctxt->m_bool_options,
+              sizeof (m_bool_options));
+    }
+  else
+    {
+      memset (m_str_options, 0, sizeof (m_str_options));
+      memset (m_int_options, 0, sizeof (m_int_options));
+      memset (m_bool_options, 0, sizeof (m_bool_options));
+    }
+}
+
 recording::context::~context ()
 {
   int i;
@@ -72,6 +101,26 @@ recording::context::replay_into (replayer *r)
 {
   int i;
   memento *m;
+
+  /* If we have a parent context, we must replay it.  This will
+     recursively walk backwards up the historical tree, then replay things
+     forwards "in historical order", starting with the ultimate parent
+     context, until we reach the "this" context.
+
+     Note that we fully replay the parent, then fully replay the child,
+     which means that inter-context references can only exist from child
+     to parent, not the other way around.
+
+     All of this replaying is suboptimal - it would be better to do the
+     work for the parent context *once*, rather than replaying the parent
+     every time we replay each child.  However, fixing this requires deep
+     surgery to lifetime-management: we'd need every context family tree
+     to have its own GC heap, and to initialize the GCC code to use that
+     heap (with a mutex on such a heap).  */
+  if (m_parent_ctxt)
+    m_parent_ctxt->replay_into (r);
+
+  /* Replay this context's saved operations into r.  */
   FOR_EACH_VEC_ELT (m_mementos, i, m)
     {
       m->replay_into (r);
diff --git a/gcc/jit/internal-api.h b/gcc/jit/internal-api.h
index 94c149b..db7e048 100644
--- a/gcc/jit/internal-api.h
+++ b/gcc/jit/internal-api.h
@@ -121,6 +121,7 @@ playback_label (label *lab);
 class context
 {
 public:
+  context (context *parent_ctxt);
   ~context ();
 
   void record (memento *m) { m_mementos.safe_push (m); }
@@ -261,6 +262,8 @@ public:
   }
 
 private:
+  context *m_parent_ctxt;
+
   int m_error_count;
   char m_first_error_str[1024];
 
diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c
index b0b6020..7d1f9ae 100644
--- a/gcc/jit/libgccjit.c
+++ b/gcc/jit/libgccjit.c
@@ -10,6 +10,9 @@
 
 struct gcc_jit_context : public gcc::jit::recording::context
 {
+  gcc_jit_context (gcc_jit_context *parent_ctxt) :
+    context (parent_ctxt)
+  {}
 };
 
 struct gcc_jit_result : public gcc::jit::result
@@ -127,7 +130,7 @@ jit_error (gcc_jit_context *ctxt, const char *fmt, ...)
 gcc_jit_context *
 gcc_jit_context_acquire (void)
 {
-  return new gcc_jit_context ();
+  return new gcc_jit_context (NULL);
 }
 
 void
@@ -136,6 +139,12 @@ gcc_jit_context_release (gcc_jit_context *ctxt)
   delete ctxt;
 }
 
+gcc_jit_context *
+gcc_jit_context_new_child_context (gcc_jit_context *parent_ctxt)
+{
+  return new gcc_jit_context (parent_ctxt);
+}
+
 /**********************************************************************
  Functions for use within the code factory.
  **********************************************************************/
diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h
index fa71518..8384a58 100644
--- a/gcc/jit/libgccjit.h
+++ b/gcc/jit/libgccjit.h
@@ -753,6 +753,40 @@ extern void
 gcc_jit_loop_end (gcc_jit_loop *loop,
 		  gcc_jit_location *loc);
 
+/**********************************************************************
+ Nested contexts.
+ **********************************************************************/
+
+/* Given an existing JIT context, create a child context.
+
+   The child inherits a copy of all option-settings from the parent.
+
+   The child can reference objects created within the parent, but not
+   vice-versa.
+
+   The lifetime of the child context must be bounded by that of the
+   parent: you should release a child context before releasing the parent
+   context.
+
+   If you use a function from a parent context within a child context,
+   you have to compile the parent context before you can compile the
+   child context, and the gcc_jit_result of the parent context must
+   outlive the gcc_jit_result of the child context.
+
+   This allows caching of shared initializations.  For example, you could
+   create types and declarations of global functions in a parent context
+   once within a process, and then create child contexts whenever a
+   function or loop becomes hot. Each such child context can be used for
+   JIT-compiling just one function or loop, but can reference types
+   and helper functions created within the parent context.
+
+   Contexts can be arbitrarily nested, provided the above rules are
+   followed, but it's probably not worth going above 2 or 3 levels, and
+   there will likely be a performance hit for such nesting.  */
+
+extern gcc_jit_context *
+gcc_jit_context_new_child_context (gcc_jit_context *parent_ctxt);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index 3a123f3..fe2a492 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -9,6 +9,7 @@
     gcc_jit_context_new_array_lookup;
     gcc_jit_context_new_binary_op;
     gcc_jit_context_new_call;
+    gcc_jit_context_new_child_context;
     gcc_jit_context_new_comparison;
     gcc_jit_context_new_field;
     gcc_jit_context_new_function;
diff --git a/gcc/testsuite/ChangeLog.jit b/gcc/testsuite/ChangeLog.jit
index b150796..6b7413c 100644
--- a/gcc/testsuite/ChangeLog.jit
+++ b/gcc/testsuite/ChangeLog.jit
@@ -1,5 +1,14 @@
 2014-01-28  David Malcolm  <dmalcolm@redhat.com>
 
+	* jit.dg/harness.h (test_jit): Add the possibility of turning off
+	this function, if the newly-coined "TEST_ESCHEWS_TEST_JIT" is
+	defined, for use by...
+	* jit.dg/test-nested-contexts.c: New test case, adapting
+	test-quadratic.c, but splitting it into a 3-deep arrangement of
+	nested contexts, to test the implementation of child contexts.
+
+2014-01-28  David Malcolm  <dmalcolm@redhat.com>
+
 	* jit.dg/harness.h (test_jit): Move the various calls to set up
 	options on the context into...
 	(set_options): ...this new function.
diff --git a/gcc/testsuite/jit.dg/harness.h b/gcc/testsuite/jit.dg/harness.h
index 71a5fcf..d82513f 100644
--- a/gcc/testsuite/jit.dg/harness.h
+++ b/gcc/testsuite/jit.dg/harness.h
@@ -124,6 +124,7 @@ static void set_options (gcc_jit_context *ctxt, const char *argv0)
     1);
 }
 
+#ifndef TEST_ESCHEWS_TEST_JIT
 /* Run one iteration of the test.  */
 static void
 test_jit (const char *argv0, void *user_data)
@@ -149,6 +150,7 @@ test_jit (const char *argv0, void *user_data)
   /* Once we're done with the code, this unloads the built .so file: */
   gcc_jit_result_release (result);
 }
+#endif /* #ifndef TEST_ESCHEWS_TEST_JIT */
 
 /* We want to prefix all unit test results with the test, but dejagnu.exp's
    host_execute appears to get confused by the leading "./" of argv0,
diff --git a/gcc/testsuite/jit.dg/test-nested-contexts.c b/gcc/testsuite/jit.dg/test-nested-contexts.c
new file mode 100644
index 0000000..14f90e7
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-nested-contexts.c
@@ -0,0 +1,627 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#define TEST_ESCHEWS_TEST_JIT
+#define TEST_PROVIDES_MAIN
+#include "harness.h"
+
+struct quadratic
+{
+  double a;
+  double b;
+  double c;
+  double discriminant;
+};
+
+/* This is an adapted version of test-quadratic.c
+
+   Like that test, we'll try to inject the following code, but we'll
+   split it up into some nested contexts, in 3 levels, to test
+   how nested contexts work.
+
+   ***** In top-level context: *****
+
+     (shared type declarations, for int, double, struct quadratic);
+     extern double sqrt (double);
+
+   ***** In mid-level context: *****
+
+     void
+     calc_discriminant (struct quadratic *q)
+     {
+       // (b^2 - 4ac)
+       q->discriminant = (q->b * q->b) - (4 * q->a * q->c);
+     }
+
+   ***** In bottom context: *****
+
+     int
+     test_quadratic (double a, double b, double c, double *r1, double *r2)
+     {
+       struct quadratic q;
+       q.a = a;
+       q.b = b;
+       q.c = c;
+       calc_discriminant (&q);
+       if (q.discriminant > 0)
+	 {
+	    double s = sqrt (q.discriminant);
+	    *r1 = (-b + s) / (2 * a);
+	    *r2 = (-b - s) / (2 * a);
+	    return 2;
+	 }
+       else if (q.discriminant == 0)
+	 {
+	    *r1 = -b / (2 * a);
+	    return 1;
+	 }
+	 else return 0;
+     }
+*/
+
+struct top_level
+{
+  gcc_jit_context *ctxt;
+
+  /* "double" and "(double *)".  */
+  gcc_jit_type *numeric_type;
+  gcc_jit_type *numeric_type_ptr;
+
+  /* The value (double)0.  */
+  gcc_jit_rvalue *zero;
+
+  gcc_jit_type *int_type;
+
+  /* "struct quadratic" */
+  gcc_jit_type *struct_quadratic;
+  gcc_jit_field *a;
+  gcc_jit_field *b;
+  gcc_jit_field *c;
+  gcc_jit_field *discriminant;
+
+  /* "(struct quadratic *)" */
+  gcc_jit_type *quadratic_ptr;
+
+  gcc_jit_function *sqrt;
+};
+
+struct middle_level
+{
+  gcc_jit_context *ctxt;
+  gcc_jit_function *calc_discriminant;
+};
+
+struct bottom_level
+{
+  gcc_jit_context *ctxt;
+};
+
+static void
+make_types (struct top_level *top_level)
+{
+  top_level->numeric_type =
+    gcc_jit_context_get_type (top_level->ctxt, GCC_JIT_TYPE_DOUBLE);
+  top_level->numeric_type_ptr =
+    gcc_jit_type_get_pointer (top_level->numeric_type);
+  top_level->zero =
+    gcc_jit_context_zero (top_level->ctxt, top_level->numeric_type);
+
+  top_level->int_type =
+    gcc_jit_context_get_type (top_level->ctxt, GCC_JIT_TYPE_INT);
+
+  top_level->a =
+    gcc_jit_context_new_field (top_level->ctxt,
+			       NULL,
+			       top_level->numeric_type,
+			       "a");
+  top_level->b =
+    gcc_jit_context_new_field (top_level->ctxt,
+			       NULL,
+			       top_level->numeric_type,
+			       "b");
+  top_level->c =
+    gcc_jit_context_new_field (top_level->ctxt,
+			       NULL,
+			       top_level->numeric_type,
+			       "c");
+  top_level->discriminant =
+    gcc_jit_context_new_field (top_level->ctxt,
+			       NULL,
+			       top_level->numeric_type,
+			       "discriminant");
+  gcc_jit_field *fields[] = {top_level->a,
+			     top_level->b,
+			     top_level->c,
+			     top_level->discriminant};
+  top_level->struct_quadratic =
+    gcc_jit_context_new_struct_type (top_level->ctxt, NULL,
+				     "quadratic", 4, fields);
+  top_level->quadratic_ptr =
+    gcc_jit_type_get_pointer (top_level->struct_quadratic);
+}
+
+static void
+make_sqrt (struct top_level *top_level)
+{
+  gcc_jit_param *param_x =
+    gcc_jit_context_new_param (top_level->ctxt, NULL,
+			       top_level->numeric_type, "x");
+  top_level->sqrt =
+    gcc_jit_context_new_function (top_level->ctxt, NULL,
+				  GCC_JIT_FUNCTION_IMPORTED,
+				  top_level->numeric_type,
+				  "sqrt",
+				  1, &param_x,
+				  0);
+}
+
+static void
+make_calc_discriminant (struct top_level *top_level,
+			struct middle_level *middle_level)
+{
+  /* Build "calc_discriminant".  */
+  gcc_jit_param *param_q =
+    gcc_jit_context_new_param (middle_level->ctxt, NULL,
+			       top_level->quadratic_ptr, "q");
+  middle_level->calc_discriminant =
+    gcc_jit_context_new_function (middle_level->ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  top_level->numeric_type,
+				  "calc_discriminant",
+				  1, &param_q,
+				  0);
+  gcc_jit_function_add_comment (
+    middle_level->calc_discriminant, NULL,
+    "(b^2 - 4ac)");
+
+  gcc_jit_rvalue *q_a =
+    gcc_jit_lvalue_as_rvalue (
+	gcc_jit_rvalue_dereference_field (
+	  gcc_jit_param_as_rvalue (param_q),
+	  NULL, "a"));
+  gcc_jit_rvalue *q_b =
+    gcc_jit_lvalue_as_rvalue (
+	gcc_jit_rvalue_dereference_field (
+	  gcc_jit_param_as_rvalue (param_q),
+	  NULL, "b"));
+  gcc_jit_rvalue *q_c =
+    gcc_jit_lvalue_as_rvalue (
+	gcc_jit_rvalue_dereference_field (
+	  gcc_jit_param_as_rvalue (param_q),
+	  NULL, "c"));
+
+  gcc_jit_function_add_assignment (
+    middle_level->calc_discriminant, NULL,
+
+    /* q->discriminant =...  */
+    gcc_jit_rvalue_dereference_field (
+      gcc_jit_param_as_rvalue (param_q),
+      NULL,
+      "discriminant"),
+
+    /* (q->b * q->b) - (4 * q->a * q->c) */
+    gcc_jit_context_new_binary_op (
+      middle_level->ctxt, NULL,
+      GCC_JIT_BINARY_OP_MINUS,
+      top_level->numeric_type,
+
+      /* (q->b * q->b) */
+      gcc_jit_context_new_binary_op (
+	middle_level->ctxt, NULL,
+	GCC_JIT_BINARY_OP_MULT,
+	top_level->numeric_type,
+	q_b, q_b),
+
+      /* (4 * (q->a * q->c)) */
+      gcc_jit_context_new_binary_op (
+	middle_level->ctxt, NULL,
+	GCC_JIT_BINARY_OP_MULT,
+	top_level->numeric_type,
+	/* 4.0 */
+	gcc_jit_context_new_rvalue_from_int (
+	  middle_level->ctxt,
+	  top_level->numeric_type,
+	  4),
+	/* (q->a * q->c) */
+	gcc_jit_context_new_binary_op (
+	  middle_level->ctxt, NULL,
+	  GCC_JIT_BINARY_OP_MULT,
+	  top_level->numeric_type,
+	  q_a, q_c)))); /* end of gcc_jit_function_add_assignment call.  */
+}
+
+static void
+make_test_quadratic (struct top_level *top_level,
+		     struct middle_level *middle_level,
+		     struct bottom_level *bottom_level)
+{
+  gcc_jit_param *a =
+    gcc_jit_context_new_param (bottom_level->ctxt, NULL,
+			       top_level->numeric_type, "a");
+  gcc_jit_param *b =
+    gcc_jit_context_new_param (bottom_level->ctxt, NULL,
+			       top_level->numeric_type, "b");
+  gcc_jit_param *c =
+    gcc_jit_context_new_param (bottom_level->ctxt, NULL,
+			       top_level->numeric_type, "c");
+  gcc_jit_param *r1 =
+    gcc_jit_context_new_param (bottom_level->ctxt, NULL,
+			       top_level->numeric_type_ptr, "r1");
+  gcc_jit_param *r2 =
+    gcc_jit_context_new_param (bottom_level->ctxt, NULL,
+			       top_level->numeric_type_ptr, "r2");
+  gcc_jit_param *params[] = {a, b, c, r1, r2};
+  gcc_jit_function *test_quadratic =
+    gcc_jit_context_new_function (bottom_level->ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  top_level->int_type,
+				  "test_quadratic",
+				  5, params,
+				  0);
+
+  /* struct quadratic q; */
+  gcc_jit_lvalue *q =
+    gcc_jit_function_new_local (
+      test_quadratic, NULL,
+      top_level->struct_quadratic,
+      "q");
+  /* q.a = a; */
+  gcc_jit_function_add_assignment (
+    test_quadratic, NULL,
+    gcc_jit_lvalue_access_field (q, NULL, "a"),
+    gcc_jit_param_as_rvalue (a));
+  /* q.b = b; */
+  gcc_jit_function_add_assignment (
+    test_quadratic, NULL,
+    gcc_jit_lvalue_access_field (q, NULL, "b"),
+    gcc_jit_param_as_rvalue (b));
+  /* q.c = c; */
+  gcc_jit_function_add_assignment (
+    test_quadratic, NULL,
+    gcc_jit_lvalue_access_field (q, NULL, "c"),
+    gcc_jit_param_as_rvalue (c));
+  /* calc_discriminant (&q); */
+  gcc_jit_rvalue *address_of_q = gcc_jit_lvalue_get_address (q, NULL);
+  gcc_jit_function_add_eval (
+    test_quadratic, NULL,
+    gcc_jit_context_new_call (
+      bottom_level->ctxt, NULL,
+      middle_level->calc_discriminant,
+      1, &address_of_q));
+
+  gcc_jit_label *on_positive_discriminant
+    = gcc_jit_function_new_forward_label (test_quadratic,
+					  "positive_discriminant");
+
+  gcc_jit_label *on_nonpositive_discriminant
+    = gcc_jit_function_new_forward_label (test_quadratic,
+					  "nonpositive_discriminant");
+
+  gcc_jit_label *on_zero_discriminant
+    = gcc_jit_function_new_forward_label (test_quadratic,
+					  "zero_discriminant");
+
+  gcc_jit_label *on_negative_discriminant
+    = gcc_jit_function_new_forward_label (test_quadratic,
+					  "negative_discriminant");
+
+  gcc_jit_function_add_comment (
+    test_quadratic, NULL,
+    "if (q.discriminant > 0)");
+  gcc_jit_function_add_conditional (
+    test_quadratic, NULL,
+    gcc_jit_context_new_comparison (
+      bottom_level->ctxt, NULL,
+      GCC_JIT_COMPARISON_GT,
+      gcc_jit_rvalue_access_field (
+	gcc_jit_lvalue_as_rvalue (q),
+	NULL,
+	"discriminant"),
+      top_level->zero),
+    on_positive_discriminant,
+    on_nonpositive_discriminant);
+
+  gcc_jit_function_place_forward_label (
+    test_quadratic, NULL,
+    on_positive_discriminant);
+  /* double s = sqrt (q.discriminant); */
+  gcc_jit_lvalue *s = gcc_jit_function_new_local (
+    test_quadratic, NULL,
+    top_level->numeric_type,
+    "s");
+  gcc_jit_rvalue *discriminant_of_q =
+    gcc_jit_rvalue_access_field (gcc_jit_lvalue_as_rvalue (q),
+				 NULL,
+				 "discriminant");
+  gcc_jit_function_add_assignment (
+    test_quadratic, NULL,
+    s,
+    gcc_jit_context_new_call (
+      bottom_level->ctxt, NULL,
+      top_level->sqrt,
+      1, &discriminant_of_q));
+
+  gcc_jit_rvalue *minus_b =
+    gcc_jit_context_new_unary_op (
+      bottom_level->ctxt, NULL,
+      GCC_JIT_UNARY_OP_MINUS,
+      top_level->numeric_type,
+      gcc_jit_param_as_rvalue (b));
+  gcc_jit_rvalue *two_a =
+    gcc_jit_context_new_binary_op (
+      bottom_level->ctxt, NULL,
+      GCC_JIT_BINARY_OP_MULT,
+      top_level->numeric_type,
+      gcc_jit_context_new_rvalue_from_int (
+	bottom_level->ctxt,
+	top_level->numeric_type,
+	2),
+      gcc_jit_param_as_rvalue (a));
+
+  gcc_jit_function_add_comment (
+    test_quadratic, NULL,
+    "*r1 = (-b + s) / (2 * a);");
+  gcc_jit_function_add_assignment (
+    test_quadratic, NULL,
+
+    /* "*r1 = ..." */
+    gcc_jit_rvalue_dereference (
+      gcc_jit_param_as_rvalue (r1), NULL),
+
+    /* (-b + s) / (2 * a) */
+    gcc_jit_context_new_binary_op (
+      bottom_level->ctxt, NULL,
+      GCC_JIT_BINARY_OP_DIVIDE,
+      top_level->numeric_type,
+      gcc_jit_context_new_binary_op (
+	bottom_level->ctxt, NULL,
+	GCC_JIT_BINARY_OP_PLUS,
+	top_level->numeric_type,
+	minus_b,
+	gcc_jit_lvalue_as_rvalue (s)),
+      two_a));
+
+  gcc_jit_function_add_comment (
+    test_quadratic, NULL,
+    "*r2 = (-b - s) / (2 * a)");
+  gcc_jit_function_add_assignment (
+    test_quadratic, NULL,
+
+    /* "*r2 = ..." */
+    gcc_jit_rvalue_dereference (
+      gcc_jit_param_as_rvalue (r2), NULL),
+
+    /* (-b - s) / (2 * a) */
+    gcc_jit_context_new_binary_op (
+      bottom_level->ctxt, NULL,
+      GCC_JIT_BINARY_OP_DIVIDE,
+      top_level->numeric_type,
+      gcc_jit_context_new_binary_op (
+	bottom_level->ctxt, NULL,
+	GCC_JIT_BINARY_OP_MINUS,
+	top_level->numeric_type,
+	minus_b,
+	gcc_jit_lvalue_as_rvalue (s)),
+      two_a));
+
+  /* "return 2;" */
+  gcc_jit_function_add_return (
+    test_quadratic, NULL,
+    gcc_jit_context_new_rvalue_from_int (
+      bottom_level->ctxt,
+      top_level->int_type,
+      2));
+
+  /* "else if (q.discriminant == 0)" */
+  gcc_jit_function_place_forward_label (
+    test_quadratic, NULL,
+    on_nonpositive_discriminant);
+  gcc_jit_function_add_comment (
+    test_quadratic, NULL,
+    "else if (q.discriminant == 0)");
+  gcc_jit_function_add_conditional (
+    test_quadratic, NULL,
+    gcc_jit_context_new_comparison (
+      bottom_level->ctxt, NULL,
+      GCC_JIT_COMPARISON_EQ,
+      gcc_jit_rvalue_access_field (
+	gcc_jit_lvalue_as_rvalue (q),
+	NULL,
+	"discriminant"),
+      top_level->zero),
+    on_zero_discriminant,
+    on_negative_discriminant);
+
+  /* if (q.discriminant == 0) */
+  gcc_jit_function_place_forward_label (
+    test_quadratic, NULL,
+    on_zero_discriminant);
+
+  gcc_jit_function_add_comment (
+    test_quadratic, NULL,
+    "*r1 = -b / (2 * a);");
+  gcc_jit_function_add_assignment (
+    test_quadratic, NULL,
+
+    /* "*r1 = ..." */
+    gcc_jit_rvalue_dereference (
+      gcc_jit_param_as_rvalue (r1), NULL),
+
+    /* -b / (2 * a) */
+    gcc_jit_context_new_binary_op (
+      bottom_level->ctxt, NULL,
+      GCC_JIT_BINARY_OP_DIVIDE,
+      top_level->numeric_type,
+      minus_b,
+      two_a));
+
+  /* "return 1;" */
+  gcc_jit_function_add_return (
+    test_quadratic, NULL,
+      gcc_jit_context_one (bottom_level->ctxt, top_level->int_type));
+
+  /* else return 0; */
+  gcc_jit_function_place_forward_label (
+    test_quadratic, NULL,
+    on_negative_discriminant);
+  gcc_jit_function_add_return (
+    test_quadratic, NULL,
+    gcc_jit_context_zero (bottom_level->ctxt, top_level->int_type));
+}
+
+void
+verify_middle_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  struct quadratic q;
+
+  typedef void (*fn_type) (struct quadratic *q);
+  fn_type calc_discriminant =
+    (fn_type)gcc_jit_result_get_code (result,
+				      "calc_discriminant");
+  CHECK_NON_NULL (calc_discriminant);
+
+  q.a = 3;
+  q.b = 5;
+  q.c = 7;
+  q.discriminant = 0;
+  calc_discriminant (&q);
+
+  CHECK_VALUE (q.discriminant, -59);
+}
+
+void
+verify_bottom_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  typedef int (*fn_type) (double a, double b, double c,
+			  double *r1, double *r2);
+
+  CHECK_NON_NULL (result);
+
+  fn_type test_quadratic =
+    (fn_type)gcc_jit_result_get_code (result, "test_quadratic");
+  CHECK_NON_NULL (test_quadratic);
+
+  /* Verify that the code correctly solves quadratic equations.  */
+  double r1, r2;
+
+  /* This one has two solutions: */
+  CHECK_VALUE (test_quadratic (1, 3, -4, &r1, &r2), 2);
+  CHECK_VALUE (r1, 1);
+  CHECK_VALUE (r2, -4);
+
+  /* This one has one solution: */
+  CHECK_VALUE (test_quadratic (4, 4, 1, &r1, &r2), 1);
+  CHECK_VALUE (r1, -0.5);
+
+  /* This one has no real solutions: */
+  CHECK_VALUE (test_quadratic (4, 1, 1, &r1, &r2), 0);
+}
+
+int
+main (int argc, char **argv)
+{
+  int i, j, k;
+  const int NUM_TOP_ITERATIONS = 2;
+  const int NUM_MIDDLE_ITERATIONS = 2;
+  const int NUM_BOTTOM_ITERATIONS = 2;
+
+  /* We do the whole thing multiple times to shake out state-management
+     issues in the underlying code.  */
+
+  for (i = 1; i <= NUM_TOP_ITERATIONS; i++)
+    {
+      /* Create the top-level context.  */
+      snprintf (test, sizeof (test),
+		"%s iteration %d of %d of top level",
+		extract_progname (argv[0]),
+		i, NUM_TOP_ITERATIONS);
+
+      struct top_level top_level;
+      memset (&top_level, 0, sizeof (top_level));
+
+      top_level.ctxt = gcc_jit_context_acquire ();
+      set_options (top_level.ctxt, argv[0]);
+
+      make_types (&top_level);
+      make_sqrt (&top_level);
+
+      /* No errors should have occurred.  */
+      CHECK_VALUE (gcc_jit_context_get_first_error (top_level.ctxt), NULL);
+
+      for (j = 1; j <= NUM_MIDDLE_ITERATIONS; j++)
+	{
+	  /* Create and populate the middle-level context, using
+	     objects from the top-level context.  */
+	  snprintf (test, sizeof (test),
+		    ("%s iteration %d of %d of top level;"
+		     " %d of %d of middle level"),
+		    extract_progname (argv[0]),
+		    i, NUM_TOP_ITERATIONS,
+		    j, NUM_MIDDLE_ITERATIONS);
+
+	  struct middle_level middle_level;
+	  memset (&middle_level, 0, sizeof (middle_level));
+
+	  middle_level.ctxt =
+	    gcc_jit_context_new_child_context (top_level.ctxt);
+	  make_calc_discriminant (&top_level,
+				  &middle_level);
+
+	  /* No errors should have occurred.  */
+	  CHECK_VALUE (gcc_jit_context_get_first_error (middle_level.ctxt),
+		       NULL);
+
+	  gcc_jit_result *middle_result =
+	    gcc_jit_context_compile (middle_level.ctxt);
+	  CHECK_NON_NULL (middle_result);
+
+	  verify_middle_code (middle_level.ctxt, middle_result);
+
+	  for (k = 1; k <= NUM_BOTTOM_ITERATIONS; k++)
+	    {
+	      /* Create and populate the innermost context, using
+		 objects from the top-level and middle-level contexts.  */
+	      snprintf (test, sizeof (test),
+			("%s iteration %d of %d of top level;"
+			 " %d of %d of middle level;"
+			 " %d of %d of bottom level"),
+			extract_progname (argv[0]),
+			i, NUM_TOP_ITERATIONS,
+			j, NUM_MIDDLE_ITERATIONS,
+			k, NUM_BOTTOM_ITERATIONS);
+
+	      struct bottom_level bottom_level;
+	      memset (&bottom_level, 0, sizeof (bottom_level));
+
+	      bottom_level.ctxt =
+		gcc_jit_context_new_child_context (middle_level.ctxt);
+	      make_test_quadratic (&top_level,
+				   &middle_level,
+				   &bottom_level);
+
+	      /* No errors should have occurred.  */
+	      CHECK_VALUE (gcc_jit_context_get_first_error (bottom_level.ctxt),
+			   NULL);
+
+	      gcc_jit_result *bottom_result =
+		gcc_jit_context_compile (bottom_level.ctxt);
+	      verify_bottom_code (bottom_level.ctxt, bottom_result);
+	      gcc_jit_result_release (bottom_result);
+	      gcc_jit_context_release (bottom_level.ctxt);
+
+	    }
+
+	  gcc_jit_result_release (middle_result);
+	  gcc_jit_context_release (middle_level.ctxt);
+
+	}
+
+      gcc_jit_context_release (top_level.ctxt);
+   }
+
+  totals ();
+
+  return 0;
+}
-- 
1.7.11.7



More information about the Gcc-patches mailing list