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 <dmalc...@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 <dmalc...@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 <dmalc...@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 <dmalc...@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, ¶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