Committed to branch dmalcolm/jit: The libgccjit C API is relatively low-level and thus should be easy to maintain a stable ABI for, and to wrap from many different languages.
However, it's rather verbose. This commit adds an example of a C++ wrapper, which makes use of methods, overloading, and inheritance, giving a much more terse API. It's all done as small inline functions wrapped around the raw C pointers, within one header, so it shouldn't need to be built as a separate library, hence this shouldn't add an ABI (I hope), just an API. As an example, I ported test-quadratic.c to it (as a new file: test-quadratic.cc), and was able to considerably reduce the line-count whilst fitting within a 80-column limit: # line, word, byte counts: $ wc \ gcc/testsuite/jit.dg/test-quadratic.c \ gcc/testsuite/jit.dg/test-quadratic.cc 487 1066 12748 gcc/testsuite/jit.dg/test-quadratic.c 369 911 9985 gcc/testsuite/jit.dg/test-quadratic.cc 856 1977 22733 total Caveat: I haven't yet managed to get DejaGnu to run this as part of the test suite; I've been building/running it manually for now. gcc/jit/ * libgccjit++.h: New file - a C++ wrapper for the libgccjit.h API. * TODO.rst ("Test Suite"): New section, adding note about C++ tests. gcc/testsuite/ * jit.dg/test-quadratic.cc: New file - a translation of test-quadratic.c to the libgccjit++.h C++ API. --- gcc/jit/ChangeLog.jit | 7 + gcc/jit/TODO.rst | 4 + gcc/jit/libgccjit++.h | 829 +++++++++++++++++++++++++++++++++ gcc/testsuite/ChangeLog.jit | 5 + gcc/testsuite/jit.dg/test-quadratic.cc | 369 +++++++++++++++ 5 files changed, 1214 insertions(+) create mode 100644 gcc/jit/libgccjit++.h create mode 100644 gcc/testsuite/jit.dg/test-quadratic.cc diff --git a/gcc/jit/ChangeLog.jit b/gcc/jit/ChangeLog.jit index 4babb1e..0dd95f2 100644 --- a/gcc/jit/ChangeLog.jit +++ b/gcc/jit/ChangeLog.jit @@ -1,5 +1,12 @@ 2014-01-31 David Malcolm <dmalc...@redhat.com> + * libgccjit++.h: New file - a C++ wrapper for the libgccjit.h API. + + * TODO.rst ("Test Suite"): New section, adding note about C++ + tests. + +2014-01-31 David Malcolm <dmalc...@redhat.com> + * libgccjit.h (gcc_jit_context_new_rvalue_from_int): Give the type parameter a more descriptive name. (gcc_jit_context_zero): Likewise. diff --git a/gcc/jit/TODO.rst b/gcc/jit/TODO.rst index 5b6e0f7..0572b78 100644 --- a/gcc/jit/TODO.rst +++ b/gcc/jit/TODO.rst @@ -133,6 +133,10 @@ Initial Release * gcc_jit_loop_end: verify that loops are validly nested? +Test suite +========== +* get DejaGnu to build and run C++ testcases + Future milestones ================= * try porting llvmpipe to gcc diff --git a/gcc/jit/libgccjit++.h b/gcc/jit/libgccjit++.h new file mode 100644 index 0000000..8636dd1 --- /dev/null +++ b/gcc/jit/libgccjit++.h @@ -0,0 +1,829 @@ +/* A C++ API for libgccjit, purely as inline wrapper functions. */ + +#ifndef LIBGCCJIT_PLUS_PLUS_H +#define LIBGCCJIT_PLUS_PLUS_H + +#include "libgccjit.h" + +#include <vector> + +/**************************************************************************** + C++ API + ****************************************************************************/ + +namespace gccjit +{ + class context; + class location; + class field; + class type; + class param; + class function; + class label; + class rvalue; + class lvalue; + + class context + { + public: + static context acquire (); + context (); + context (gcc_jit_context *ctxt); + + location + new_location (const char *filename, + int line, + int column); + + type get_type (enum gcc_jit_types kind); + + field new_field (type type_, const char *name); + field new_field (location loc, type type_, const char *name); + + type new_struct_type (const char *name, + std::vector<field> fields); + type new_struct_type (location loc, + const char *name, + std::vector<field> fields); + + param new_param (type type_, + const char *name); + param new_param (location loc, + type type_, + const char *name); + + function new_function (enum gcc_jit_function_kind kind, + type return_type, + const char *name, + std::vector<param> params, + int is_variadic); + function new_function (location loc, + enum gcc_jit_function_kind kind, + type return_type, + const char *name, + std::vector<param> params, + int is_variadic); + + rvalue new_rvalue (type numeric_type, + int value); + rvalue zero (type numeric_type); + rvalue one (type numeric_type); + rvalue new_rvalue (type numeric_type, + double value); + rvalue new_rvalue (type pointer_type, + void *value); + rvalue new_rvalue (const char *value); + + rvalue new_unary_op (enum gcc_jit_unary_op op, + type result_type, + rvalue a); + rvalue new_unary_op (location loc, + enum gcc_jit_unary_op op, + type result_type, + rvalue a); + + rvalue new_binary_op (enum gcc_jit_binary_op op, + type result_type, + rvalue a, rvalue b); + rvalue new_binary_op (location loc, + enum gcc_jit_binary_op op, + type result_type, + rvalue a, rvalue b); + + rvalue new_comparison (enum gcc_jit_comparison op, + rvalue a, rvalue b); + rvalue new_comparison (location loc, + enum gcc_jit_comparison op, + rvalue a, rvalue b); + + rvalue new_call (function func, + std::vector<rvalue> args); + rvalue new_call (location loc, + function func, + std::vector<rvalue> args); + + public: + gcc_jit_context *m_inner_ctxt; + }; + + class location + { + public: + location (); + location (gcc_jit_location *loc); + + public: + gcc_jit_location *m_inner_loc; + }; + + class field + { + public: + field (); + field (gcc_jit_field *inner); + + public: + gcc_jit_field *m_inner_field; + }; + + class type + { + public: + type (); + type (gcc_jit_type *inner); + + type get_pointer (); + + public: + gcc_jit_type *m_inner_type; + }; + + class function + { + public: + function (); + function (gcc_jit_function *func); + + label new_forward_label (const char *name); + + lvalue new_local (type type_, + const char *name); + lvalue new_local (location loc, + type type_, + const char *name); + + void add_eval (rvalue rvalue); + void add_eval (location loc, + rvalue rvalue); + + void add_assignment (lvalue lvalue, + rvalue rvalue); + void add_assignment (location loc, + lvalue lvalue, + rvalue rvalue); + + void add_assignment_op (lvalue lvalue, + enum gcc_jit_binary_op op, + rvalue rvalue); + void add_assignment_op (location loc, + lvalue lvalue, + enum gcc_jit_binary_op op, + rvalue rvalue); + + void add_comment (location loc, + const char *text); + void add_comment (const char *text); + + void add_conditional (location loc, + rvalue boolval, + label on_true, + label on_false); + void add_conditional (rvalue boolval, + label on_true, + label on_false); + + label add_label (location loc, + const char *name); + label add_label (const char *name); + + void place_forward_label (location loc, + label lab); + void place_forward_label (label lab); + + void add_jump (location loc, + label target); + void add_jump (label target); + + void add_return (location loc, + rvalue rvalue); + void add_return (rvalue rvalue); + + public: + gcc_jit_function *m_inner_func; + }; + + class label + { + public: + label (); + label (gcc_jit_label *inner); + + public: + gcc_jit_label *m_inner_label; + }; + + class rvalue + { + public: + rvalue (); + rvalue (gcc_jit_rvalue *inner); + + rvalue access_field (field field); + rvalue access_field (location loc, + field field); + + lvalue dereference_field (field field); + lvalue dereference_field (location loc, + field field); + + lvalue dereference (); + lvalue dereference (location loc); + + public: + gcc_jit_rvalue *m_inner_rvalue; + }; + + class lvalue : public rvalue + { + public: + lvalue (); + lvalue (gcc_jit_lvalue *inner); + + gcc_jit_lvalue *get_inner_lvalue (); + + lvalue access_field (field field); + lvalue access_field (location loc, + field field); + + rvalue get_address (); + rvalue get_address (location loc); + }; + + class param : public lvalue + { + public: + param (); + param (gcc_jit_param *inner); + + gcc_jit_param *get_inner_param (); + }; +} + +/**************************************************************************** + Implementation of the API + ****************************************************************************/ +namespace gccjit { + +// class context +inline context context::acquire () +{ + return context (gcc_jit_context_acquire ()); +} +inline context::context () : m_inner_ctxt (NULL) {} +inline context::context (gcc_jit_context *inner) : m_inner_ctxt (inner) {} + +inline location +context::new_location (const char *filename, + int line, + int column) +{ + return location (gcc_jit_context_new_location (m_inner_ctxt, + filename, + line, + column)); +} + +inline type +context::get_type (enum gcc_jit_types kind) +{ + return type (gcc_jit_context_get_type (m_inner_ctxt, kind)); +} + +inline field +context::new_field (type type_, const char *name) +{ + return new_field (location (), type_, name); +} +inline field +context::new_field (location loc, type type_, const char *name) +{ + return field (gcc_jit_context_new_field (m_inner_ctxt, + loc.m_inner_loc, + type_.m_inner_type, + name)); +} + +inline type +context::new_struct_type (const char *name, + std::vector<field> fields) +{ + return new_struct_type (location (), + name, + fields); +} +inline type +context::new_struct_type (location loc, + const char *name, + std::vector<field> fields) +{ + /* Treat std::vector as an array, relying on it not being resized: */ + field *as_array_of_wrappers = &fields[0]; + + /* Treat the array as being of the underlying pointers, relying on + the wrapper type being such a pointer internally. */ + gcc_jit_field **as_array_of_ptrs = + reinterpret_cast<gcc_jit_field **> (as_array_of_wrappers); + + return type (gcc_jit_context_new_struct_type (m_inner_ctxt, + loc.m_inner_loc, + name, + fields.size (), + as_array_of_ptrs)); +} + +inline param +context::new_param (type type_, + const char *name) +{ + return new_param (location (), + type_, + name); +} +inline param +context::new_param (location loc, + type type_, + const char *name) +{ + return param (gcc_jit_context_new_param (m_inner_ctxt, + loc.m_inner_loc, + type_.m_inner_type, + name)); +} + +inline function +context::new_function (enum gcc_jit_function_kind kind, + type return_type, + const char *name, + std::vector<param> params, + int is_variadic) +{ + return new_function (location (), + kind, + return_type, + name, + params, + is_variadic); +} +inline function +context::new_function (location loc, + enum gcc_jit_function_kind kind, + type return_type, + const char *name, + std::vector<param> params, + int is_variadic) +{ + /* Treat std::vector as an array, relying on it not being resized: */ + param *as_array_of_wrappers = ¶ms[0]; + + /* Treat the array as being of the underlying pointers, relying on + the wrapper type being such a pointer internally. */ + gcc_jit_param **as_array_of_ptrs = + reinterpret_cast<gcc_jit_param **> (as_array_of_wrappers); + + return function (gcc_jit_context_new_function (m_inner_ctxt, + loc.m_inner_loc, + kind, + return_type.m_inner_type, + name, + params.size (), + as_array_of_ptrs, + is_variadic)); +} + +inline rvalue +context::new_rvalue (type numeric_type, + int value) +{ + return rvalue ( + gcc_jit_context_new_rvalue_from_int (m_inner_ctxt, + numeric_type.m_inner_type, + value)); +} + +inline rvalue +context::zero (type numeric_type) +{ + return rvalue (gcc_jit_context_zero (m_inner_ctxt, + numeric_type.m_inner_type)); +} + +inline rvalue +context::one (type numeric_type) +{ + return rvalue (gcc_jit_context_one (m_inner_ctxt, + numeric_type.m_inner_type)); +} + +inline rvalue +context::new_rvalue (type numeric_type, + double value) +{ + return rvalue ( + gcc_jit_context_new_rvalue_from_double (m_inner_ctxt, + numeric_type.m_inner_type, + value)); +} + +inline rvalue +context::new_rvalue (type pointer_type, + void *value) +{ + return rvalue ( + gcc_jit_context_new_rvalue_from_ptr (m_inner_ctxt, + pointer_type.m_inner_type, + value)); +} + +inline rvalue +context::new_rvalue (const char *value) +{ + return rvalue ( + gcc_jit_context_new_string_literal (m_inner_ctxt, value)); +} + +inline rvalue +context::new_unary_op (enum gcc_jit_unary_op op, + type result_type, + rvalue a) +{ + return new_unary_op (location (), + op, + result_type, + a); +} +inline rvalue +context::new_unary_op (location loc, + enum gcc_jit_unary_op op, + type result_type, + rvalue a) +{ + return rvalue (gcc_jit_context_new_unary_op (m_inner_ctxt, + loc.m_inner_loc, + op, + result_type.m_inner_type, + a.m_inner_rvalue)); +} + +inline rvalue +context::new_binary_op (enum gcc_jit_binary_op op, + type result_type, + rvalue a, rvalue b) +{ + return new_binary_op (location (), + op, + result_type, + a, b); +} +inline rvalue +context::new_binary_op (location loc, + enum gcc_jit_binary_op op, + type result_type, + rvalue a, rvalue b) +{ + return rvalue (gcc_jit_context_new_binary_op (m_inner_ctxt, + loc.m_inner_loc, + op, + result_type.m_inner_type, + a.m_inner_rvalue, + b.m_inner_rvalue)); +} + +inline rvalue +context::new_comparison (enum gcc_jit_comparison op, + rvalue a, rvalue b) +{ + return new_comparison (location (), + op, + a, b); +} +inline rvalue +context::new_comparison (location loc, + enum gcc_jit_comparison op, + rvalue a, rvalue b) +{ + return rvalue (gcc_jit_context_new_comparison (m_inner_ctxt, + loc.m_inner_loc, + op, + a.m_inner_rvalue, + b.m_inner_rvalue)); +} + +inline rvalue +context::new_call (function func, + std::vector<rvalue> args) +{ + return new_call (location (), + func, + args); +} + +inline rvalue +context::new_call (location loc, + function func, + std::vector<rvalue> args) +{ + /* Treat std::vector as an array, relying on it not being resized: */ + rvalue *as_array_of_wrappers = &args[0]; + + /* Treat the array as being of the underlying pointers, relying on + the wrapper type being such a pointer internally. */ + gcc_jit_rvalue **as_array_of_ptrs = + reinterpret_cast<gcc_jit_rvalue **> (as_array_of_wrappers); + return gcc_jit_context_new_call (m_inner_ctxt, + loc.m_inner_loc, + func.m_inner_func, + args.size (), + as_array_of_ptrs); +} + +// class location +inline location::location () : m_inner_loc (NULL) {} +inline location::location (gcc_jit_location *loc) : m_inner_loc (loc) {} + +// class field +inline field::field () : m_inner_field (NULL) {} +inline field::field (gcc_jit_field *inner) : m_inner_field (inner) {} + +// class type +inline type::type () : m_inner_type (NULL) {} +inline type::type (gcc_jit_type *inner) : m_inner_type (inner) {} + +inline type +type::get_pointer () +{ + return type (gcc_jit_type_get_pointer (m_inner_type)); +} + +// class function +inline function::function () : m_inner_func (NULL) {} +inline function::function (gcc_jit_function *inner) : m_inner_func (inner) {} + +inline label +function::new_forward_label (const char *name) +{ + return label (gcc_jit_function_new_forward_label (m_inner_func, name)); +} + +inline lvalue +function::new_local (type type_, + const char *name) +{ + return new_local (location (), type_, name); +} + +inline lvalue +function::new_local (location loc, + type type_, + const char *name) +{ + return lvalue (gcc_jit_function_new_local (m_inner_func, + loc.m_inner_loc, + type_.m_inner_type, + name)); +} + +inline void +function::add_eval (rvalue rvalue) +{ + add_eval (location (), + rvalue); +} +inline void +function::add_eval (location loc, + rvalue rvalue) +{ + gcc_jit_function_add_eval (m_inner_func, + loc.m_inner_loc, + rvalue.m_inner_rvalue); +} + +inline void +function::add_assignment (lvalue lvalue, + rvalue rvalue) +{ + add_assignment (location (), + lvalue, + rvalue); +} +inline void +function::add_assignment (location loc, + lvalue lvalue, + rvalue rvalue) +{ + gcc_jit_function_add_assignment (m_inner_func, + loc.m_inner_loc, + lvalue.get_inner_lvalue (), + rvalue.m_inner_rvalue); +} + +inline void +function::add_assignment_op (lvalue lvalue, + enum gcc_jit_binary_op op, + rvalue rvalue) +{ + add_assignment_op (location (), + lvalue, + op, + rvalue); +} +inline void +function::add_assignment_op (location loc, + lvalue lvalue, + enum gcc_jit_binary_op op, + rvalue rvalue) +{ + gcc_jit_function_add_assignment_op (m_inner_func, + loc.m_inner_loc, + lvalue.get_inner_lvalue (), + op, + rvalue.m_inner_rvalue); +} + +inline void +function::add_comment (const char *text) +{ + add_comment (location (), text); +} +inline void +function::add_comment (location loc, + const char *text) +{ + gcc_jit_function_add_comment (m_inner_func, + loc.m_inner_loc, + text); +} + +inline void +function::add_conditional (rvalue boolval, + label on_true, + label on_false) +{ + return add_conditional (location (), boolval, on_true, on_false); +} +inline void +function::add_conditional (location loc, + rvalue boolval, + label on_true, + label on_false) +{ + gcc_jit_function_add_conditional (m_inner_func, + loc.m_inner_loc, + boolval.m_inner_rvalue, + on_true.m_inner_label, + on_false.m_inner_label); +} + +inline label +function::add_label (const char *name) +{ + return add_label (location (), name); +} +inline label +function::add_label (location loc, + const char *name) +{ + return label (gcc_jit_function_add_label (m_inner_func, + loc.m_inner_loc, + name)); +} + +inline void +function::place_forward_label (label lab) +{ + return place_forward_label (location (), lab); +} +inline void +function::place_forward_label (location loc, + label lab) +{ + gcc_jit_function_place_forward_label (m_inner_func, + loc.m_inner_loc, + lab.m_inner_label); +} + +inline void +function::add_jump (label target) +{ + add_jump (location (), target); +} +inline void +function::add_jump (location loc, + label target) +{ + gcc_jit_function_add_jump (m_inner_func, + loc.m_inner_loc, + target.m_inner_label); +} + +inline void +function::add_return (rvalue rvalue) +{ + add_return (location (), rvalue); +} +inline void +function::add_return (location loc, + rvalue rvalue) +{ + gcc_jit_function_add_return (m_inner_func, + loc.m_inner_loc, + rvalue.m_inner_rvalue); +} + +// class label +inline label::label () : m_inner_label (NULL) {} +inline label::label (gcc_jit_label *inner) : m_inner_label (inner) {} + +// class rvalue +inline rvalue::rvalue () : m_inner_rvalue (NULL) {} +inline rvalue::rvalue (gcc_jit_rvalue *inner) : m_inner_rvalue (inner) {} + +inline rvalue +rvalue::access_field (field field) +{ + return access_field (location (), field); +} +inline rvalue +rvalue::access_field (location loc, + field field) +{ + return rvalue (gcc_jit_rvalue_access_field (m_inner_rvalue, + loc.m_inner_loc, + field.m_inner_field)); +} + +inline lvalue +rvalue::dereference_field (field field) +{ + return dereference_field (location (), + field); +} + +inline lvalue +rvalue::dereference_field (location loc, + field field) +{ + return lvalue (gcc_jit_rvalue_dereference_field (m_inner_rvalue, + loc.m_inner_loc, + field.m_inner_field)); +} + +inline lvalue +rvalue::dereference () +{ + return dereference (location ()); +} +inline lvalue +rvalue::dereference (location loc) +{ + return lvalue (gcc_jit_rvalue_dereference (m_inner_rvalue, + loc.m_inner_loc)); +} + +// class lvalue : public rvalue +inline lvalue::lvalue () : rvalue () {} +inline lvalue::lvalue (gcc_jit_lvalue *inner) + : rvalue (gcc_jit_lvalue_as_rvalue (inner)) +{} + +inline gcc_jit_lvalue * +lvalue::get_inner_lvalue () +{ + /* Manual downcast: */ + return reinterpret_cast<gcc_jit_lvalue *> (m_inner_rvalue); +} + +inline lvalue +lvalue::access_field (field field) +{ + return access_field (location (), field); +} +inline lvalue +lvalue::access_field (location loc, + field field) +{ + return lvalue (gcc_jit_lvalue_access_field (get_inner_lvalue (), + loc.m_inner_loc, + field.m_inner_field)); +} + +inline rvalue +lvalue::get_address () +{ + return get_address (location ()); +} +inline rvalue +lvalue::get_address (location loc) +{ + return rvalue (gcc_jit_lvalue_get_address (get_inner_lvalue (), + loc.m_inner_loc)); +} + +// class param : public lvalue +inline param::param () : lvalue () {} +inline param::param (gcc_jit_param *inner) + : lvalue (gcc_jit_param_as_lvalue (inner)) +{} + +} // namespace gccjit + +#endif /* #ifndef LIBGCCJIT_PLUS_PLUS_H */ diff --git a/gcc/testsuite/ChangeLog.jit b/gcc/testsuite/ChangeLog.jit index 47dface..d59b578 100644 --- a/gcc/testsuite/ChangeLog.jit +++ b/gcc/testsuite/ChangeLog.jit @@ -1,3 +1,8 @@ +2014-01-31 David Malcolm <dmalc...@redhat.com> + + * jit.dg/test-quadratic.cc: New file - a translation of + test-quadratic.c to the libgccjit++.h C++ API. + 2014-01-30 David Malcolm <dmalc...@redhat.com> * jit.dg/test-error-label-already-placed.c: New test case. diff --git a/gcc/testsuite/jit.dg/test-quadratic.cc b/gcc/testsuite/jit.dg/test-quadratic.cc new file mode 100644 index 0000000..0151926 --- /dev/null +++ b/gcc/testsuite/jit.dg/test-quadratic.cc @@ -0,0 +1,369 @@ +/* Test of C++ API. */ +#include <stdlib.h> +#include <stdio.h> + +#include "libgccjit++.h" + +#include "harness.h" + +struct quadratic +{ + double a; + double b; + double c; + double discriminant; +}; + +/* As per test-quadratic.c, let's try to inject the equivalent of: + + extern double sqrt (double); + + void + calc_discriminant (struct quadratic *q) + { + // (b^2 - 4ac) + q->discriminant = (q->b * q->b) - (4 * q->a * q->c); + } + + 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; + } + + However, we'll use the C++ bindings. +*/ + +/**************************************************************************** + Test case + ****************************************************************************/ + +struct quadratic_test +{ + gccjit::context ctxt; + + /* "double" and "(double *)". */ + gccjit::type numeric_type; + gccjit::type numeric_type_ptr; + + /* The value (double)0. */ + gccjit::rvalue zero; + + gccjit::type int_type; + + /* "struct quadratic" */ + gccjit::type quadratic; + gccjit::field a; + gccjit::field b; + gccjit::field c; + gccjit::field discriminant; + + /* "(struct quadratic *)" */ + gccjit::type quadratic_ptr; + + gccjit::function calc_discriminant; + + gccjit::function sqrt; + +}; + +static void +make_types (quadratic_test &testcase) +{ + testcase.numeric_type = testcase.ctxt.get_type (GCC_JIT_TYPE_DOUBLE); + testcase.numeric_type_ptr = testcase.numeric_type.get_pointer (); + testcase.zero = testcase.ctxt.zero (testcase.numeric_type); + + testcase.int_type = testcase.ctxt.get_type (GCC_JIT_TYPE_INT); + + testcase.a = testcase.ctxt.new_field (testcase.numeric_type, "a"); + testcase.b = testcase.ctxt.new_field (testcase.numeric_type, "b"); + testcase.c = testcase.ctxt.new_field (testcase.numeric_type, "c"); + testcase.discriminant = + testcase.ctxt.new_field (testcase.numeric_type, "discriminant"); + std::vector<gccjit::field> fields (4); + fields[0] = testcase.a; + fields[1] = testcase.b; + fields[2] = testcase.c; + fields[3] = testcase.discriminant; + testcase.quadratic = + testcase.ctxt.new_struct_type ( + "quadratic", + fields); + testcase.quadratic_ptr = testcase.quadratic.get_pointer (); +} + +static void +make_sqrt (quadratic_test &testcase) +{ + std::vector<gccjit::param> params (1); + params[0] = + testcase.ctxt.new_param (testcase.numeric_type, "x"); + testcase.sqrt = + testcase.ctxt.new_function (GCC_JIT_FUNCTION_IMPORTED, + testcase.numeric_type, + "sqrt", + params, + 0); +} + +static void +make_calc_discriminant (quadratic_test &testcase) +{ + /* Build "calc_discriminant". */ + gccjit::param param_q = + testcase.ctxt.new_param (testcase.quadratic_ptr, "q"); + std::vector <gccjit::param> params (1); + params[0] = param_q; + testcase.calc_discriminant = + testcase.ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED, + testcase.numeric_type, + "calc_discriminant", + params, + 0); + testcase.calc_discriminant.add_comment ("(b^2 - 4ac)"); + + gccjit::rvalue q_a = param_q.dereference_field (testcase.a); + gccjit::rvalue q_b = param_q.dereference_field (testcase.b); + gccjit::rvalue q_c = param_q.dereference_field (testcase.c); + + testcase.calc_discriminant.add_assignment ( + /* q->discriminant =... */ + param_q.dereference_field (testcase.discriminant), + + /* (q->b * q->b) - (4 * q->a * q->c) */ + testcase.ctxt.new_binary_op ( + GCC_JIT_BINARY_OP_MINUS, + testcase.numeric_type, + + /* (q->b * q->b) */ + testcase.ctxt.new_binary_op ( + GCC_JIT_BINARY_OP_MULT, + testcase.numeric_type, + q_b, q_b), + + /* (4 * (q->a * q->c)) */ + testcase.ctxt.new_binary_op ( + GCC_JIT_BINARY_OP_MULT, + testcase.numeric_type, + /* 4.0 */ + testcase.ctxt.new_rvalue ( + testcase.numeric_type, + 4), + /* (q->a * q->c) */ + testcase.ctxt.new_binary_op ( + GCC_JIT_BINARY_OP_MULT, + testcase.numeric_type, + q_a, q_c)))); /* end of add_assignment call. */ +} + +static void +make_test_quadratic (quadratic_test &testcase) +{ + gccjit::param a = testcase.ctxt.new_param (testcase.numeric_type, "a"); + gccjit::param b = testcase.ctxt.new_param (testcase.numeric_type, "b"); + gccjit::param c = testcase.ctxt.new_param (testcase.numeric_type, "c"); + gccjit::param r1 = + testcase.ctxt.new_param (testcase.numeric_type_ptr, "r1"); + gccjit::param r2 = + testcase.ctxt.new_param (testcase.numeric_type_ptr, "r2"); + + std::vector<gccjit::param> params (5); + params[0] = a; + params[1] = b; + params[2] = c; + params[3] = r1; + params[4] = r2; + + gccjit::function test_quadratic = + testcase.ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED, + testcase.int_type, + "test_quadratic", + params, + 0); + + /* struct quadratic q; */ + gccjit::lvalue q = test_quadratic.new_local (testcase.quadratic, "q"); + /* q.a = a; */ + test_quadratic.add_assignment (q.access_field (testcase.a), a); + /* q.b = b; */ + test_quadratic.add_assignment (q.access_field (testcase.b), b); + /* q.c = c; */ + test_quadratic.add_assignment (q.access_field (testcase.c), c); + /* calc_discriminant (&q); */ + gccjit::rvalue address_of_q = q.get_address (); + std::vector<gccjit::rvalue> args (1); + args[0] = address_of_q; + test_quadratic.add_eval ( + testcase.ctxt.new_call ( + testcase.calc_discriminant, + args)); + + gccjit::label on_positive_discriminant + = test_quadratic.new_forward_label ("positive_discriminant"); + + gccjit::label on_nonpositive_discriminant + = test_quadratic.new_forward_label ("nonpositive_discriminant"); + + gccjit::label on_zero_discriminant + = test_quadratic.new_forward_label ("zero_discriminant"); + + gccjit::label on_negative_discriminant + = test_quadratic.new_forward_label ("negative_discriminant"); + + test_quadratic.add_comment ("if (q.discriminant > 0)"); + test_quadratic.add_conditional ( + testcase.ctxt.new_comparison ( + GCC_JIT_COMPARISON_GT, + q.access_field (testcase.discriminant), + testcase.zero), + on_positive_discriminant, + on_nonpositive_discriminant); + + test_quadratic.place_forward_label (on_positive_discriminant); + /* double s = sqrt (q.discriminant); */ + gccjit::lvalue s = test_quadratic.new_local (testcase.numeric_type, "s"); + gccjit::rvalue discriminant_of_q = q.access_field (testcase.discriminant); + test_quadratic.add_assignment ( + s, + testcase.ctxt.new_call ( + testcase.sqrt, + std::vector<gccjit::rvalue> (1, discriminant_of_q))); + + gccjit::rvalue minus_b = + testcase.ctxt.new_unary_op ( + GCC_JIT_UNARY_OP_MINUS, + testcase.numeric_type, + b); + gccjit::rvalue two_a = + testcase.ctxt.new_binary_op ( + GCC_JIT_BINARY_OP_MULT, + testcase.numeric_type, + testcase.ctxt.new_rvalue (testcase.numeric_type, 2), + a); + + test_quadratic.add_comment ("*r1 = (-b + s) / (2 * a);"); + test_quadratic.add_assignment ( + /* "*r1 = ..." */ + r1.dereference (), + + /* (-b + s) / (2 * a) */ + testcase.ctxt.new_binary_op ( + GCC_JIT_BINARY_OP_DIVIDE, + testcase.numeric_type, + testcase.ctxt.new_binary_op ( + GCC_JIT_BINARY_OP_PLUS, + testcase.numeric_type, + minus_b, + s), + two_a)); + + test_quadratic.add_comment ("*r2 = (-b - s) / (2 * a)"); + test_quadratic.add_assignment ( + /* "*r2 = ..." */ + r2.dereference (), + + /* (-b - s) / (2 * a) */ + testcase.ctxt.new_binary_op ( + GCC_JIT_BINARY_OP_DIVIDE, + testcase.numeric_type, + testcase.ctxt.new_binary_op ( + GCC_JIT_BINARY_OP_MINUS, + testcase.numeric_type, + minus_b, + s), + two_a)); + + /* "return 2;" */ + test_quadratic.add_return (testcase.ctxt.new_rvalue (testcase.int_type, 2)); + + /* "else if (q.discriminant == 0)" */ + test_quadratic.place_forward_label (on_nonpositive_discriminant); + test_quadratic.add_comment ("else if (q.discriminant == 0)"); + test_quadratic.add_conditional ( + testcase.ctxt.new_comparison ( + GCC_JIT_COMPARISON_EQ, + q.access_field (testcase.discriminant), + testcase.zero), + on_zero_discriminant, + on_negative_discriminant); + + /* if (q.discriminant == 0) */ + test_quadratic.place_forward_label (on_zero_discriminant); + + test_quadratic.add_comment ("*r1 = -b / (2 * a);"); + test_quadratic.add_assignment ( + /* "*r1 = ..." */ + r1.dereference (), + + /* -b / (2 * a) */ + testcase.ctxt.new_binary_op ( + GCC_JIT_BINARY_OP_DIVIDE, + testcase.numeric_type, + minus_b, + two_a)); + + /* "return 1;" */ + test_quadratic.add_return (testcase.ctxt.one (testcase.int_type)); + + /* else return 0; */ + test_quadratic.place_forward_label (on_negative_discriminant); + test_quadratic.add_return (testcase.ctxt.zero (testcase.int_type)); +} + +void +create_code (gcc_jit_context *ctxt, void *user_data) +{ + struct quadratic_test testcase; + memset (&testcase, 0, sizeof (testcase)); + testcase.ctxt = ctxt; + make_types (testcase); + make_sqrt (testcase); + make_calc_discriminant (testcase); + make_test_quadratic (testcase); +} + +void +verify_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); +} -- 1.7.11.7