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 <[email protected]>
+
+ * 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 <[email protected]>
* 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 <[email protected]>
+ * 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 <[email protected]>
+
* 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, ¶m_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, ¶m_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