Committed to branch dmalcolm/jit: Add testcase test-quadratic.c, written to achieve test coverage of gcc_jit_rvalue_access_field - with this commit, the test suite has at least one call to every public symbol in the API.
In the process, I found that GCC_JIT_BINARY_OP_DIVIDE on floating-point types would lead to a crash when expanding gimple to RTL, since the use of TRUNC_DIV_EXPR requires integer args. (Violating this assumption led to a crash when the division by 2 was turned into a shift, which the RTL expander assumes is on an integer). Hence we update GCC_JIT_BINARY_OP_DIVIDE to internally pick either RDIV_EXPR (floating point division) or TRUNC_DIV_EXPR (truncation towards zero) based on the result-type of the binary op (my intent being to be least-surprising to a C programmer). gcc/jit/ * libgccjit.h (enum gcc_jit_binary_op): We will use the result type to determine if GCC_JIT_BINARY_OP_DIVIDE should truncate towards zero, or be floating-point division. * internal-api.c (gcc::jit::context::new_binary_op): Likewise. gcc/testsuite/ * jit.dg/test-quadratic.c: New test case, written to achieve test coverage of gcc_jit_rvalue_access_field, but also exercising division of doubles. * jit.dg/test-combination.c: Add test-quadratic.c * jit.dg/test-expressions.c: Add TODOs. --- gcc/jit/ChangeLog.jit | 8 + gcc/jit/internal-api.c | 8 +- gcc/jit/libgccjit.h | 12 +- gcc/testsuite/ChangeLog.jit | 10 + gcc/testsuite/jit.dg/test-combination.c | 7 + gcc/testsuite/jit.dg/test-expressions.c | 3 + gcc/testsuite/jit.dg/test-quadratic.c | 488 ++++++++++++++++++++++++++++++++ 7 files changed, 532 insertions(+), 4 deletions(-) create mode 100644 gcc/testsuite/jit.dg/test-quadratic.c diff --git a/gcc/jit/ChangeLog.jit b/gcc/jit/ChangeLog.jit index cdf9ddb..f13d0c5 100644 --- a/gcc/jit/ChangeLog.jit +++ b/gcc/jit/ChangeLog.jit @@ -1,5 +1,13 @@ 2014-01-24 David Malcolm <dmalc...@redhat.com> + * libgccjit.h (enum gcc_jit_binary_op): We will use the result + type to determine if GCC_JIT_BINARY_OP_DIVIDE should + truncate towards zero, or be floating-point division. + + * internal-api.c (gcc::jit::context::new_binary_op): Likewise. + +2014-01-24 David Malcolm <dmalc...@redhat.com> + * internal-api.h (gcc::jit::context::get_str_option): New access method. (gcc::jit::context::get_int_option): Likewise. diff --git a/gcc/jit/internal-api.c b/gcc/jit/internal-api.c index 32af625..f95c09f 100644 --- a/gcc/jit/internal-api.c +++ b/gcc/jit/internal-api.c @@ -479,9 +479,13 @@ new_binary_op (location *loc, break; case GCC_JIT_BINARY_OP_DIVIDE: - inner_op = TRUNC_DIV_EXPR; + if (FLOAT_TYPE_P (result_type->as_tree ())) + /* Floating-point division: */ + inner_op = RDIV_EXPR; + else + /* Truncating to zero: */ + inner_op = TRUNC_DIV_EXPR; break; - /* do we want separate floor divide vs frac divide? */ case GCC_JIT_BINARY_OP_MODULO: inner_op = TRUNC_MOD_EXPR; diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h index baf1541..dd287e8 100644 --- a/gcc/jit/libgccjit.h +++ b/gcc/jit/libgccjit.h @@ -497,9 +497,17 @@ enum gcc_jit_binary_op /* Quotient of division of arithmetic values; analogous to: (EXPR_A) / (EXPR_B) - in C. */ + in C. + The result type affects the kind of division: if the result type is + integer-based, then the result is truncated towards zero, whereas + a floating-point result type indicates floating-point division. */ GCC_JIT_BINARY_OP_DIVIDE, - /* do we want separate floor divide vs frac divide? */ + + /* Quotient of division of floating-point values, without rounding; + analogous to: + (EXPR_A) / (EXPR_B) + in C. */ + GCC_JIT_BINARY_OP_FLOATING_DIVIDE, /* Remainder of division of arithmetic values; analogous to: (EXPR_A) / (EXPR_B) diff --git a/gcc/testsuite/ChangeLog.jit b/gcc/testsuite/ChangeLog.jit index 5cd4a63..02e73c7 100644 --- a/gcc/testsuite/ChangeLog.jit +++ b/gcc/testsuite/ChangeLog.jit @@ -1,5 +1,15 @@ 2014-01-23 David Malcolm <dmalc...@redhat.com> + * jit.dg/test-quadratic.c: New test case, written to achieve test + coverage of gcc_jit_rvalue_access_field, but also exercising + division of doubles. + + * jit.dg/test-combination.c: Add test-quadratic.c + + * jit.dg/test-expressions.c: Add TODOs. + +2014-01-23 David Malcolm <dmalc...@redhat.com> + * jit.dg/test-reading-struct.c: New test, to provide test coverage of gcc_jit_type_get_const and gcc_jit_lvalue_access_field, in the process uncovering bugs in how locals were handled. diff --git a/gcc/testsuite/jit.dg/test-combination.c b/gcc/testsuite/jit.dg/test-combination.c index 6a2372f..562f031 100644 --- a/gcc/testsuite/jit.dg/test-combination.c +++ b/gcc/testsuite/jit.dg/test-combination.c @@ -59,6 +59,13 @@ #undef code_making_callback #undef verify_code +/* test-quadratic.c */ +#define code_making_callback code_making_callback_quadratic +#define verify_code verify_code_quadratic +#include "test-quadratic.c" +#undef code_making_callback +#undef verify_code + /* test-reading-struct.c */ #define code_making_callback code_making_callback_reading_struct #define verify_code verify_code_reading_struct diff --git a/gcc/testsuite/jit.dg/test-expressions.c b/gcc/testsuite/jit.dg/test-expressions.c index c270082..b224347 100644 --- a/gcc/testsuite/jit.dg/test-expressions.c +++ b/gcc/testsuite/jit.dg/test-expressions.c @@ -158,6 +158,7 @@ make_tests_of_binary_ops (gcc_jit_context *ctxt) int_type, GCC_JIT_BINARY_OP_DIVIDE, "test_BINARY_OP_DIVIDE_on_int"); + /* TODO: test for DIVIDE on float or double */ make_test_of_binary_op (ctxt, int_type, GCC_JIT_BINARY_OP_MODULO, @@ -226,6 +227,8 @@ verify_binary_ops (gcc_jit_result *result) CHECK_VALUE (test_BINARY_OP_DIVIDE_on_int (-1, -4), (-1 / -4)); CHECK_VALUE (test_BINARY_OP_DIVIDE_on_int (60, 5), 12); + /* TODO: test for DIVIDE on float or double */ + test_fn test_BINARY_OP_MODULO_on_int = (test_fn)gcc_jit_result_get_code (result, "test_BINARY_OP_MODULO_on_int"); diff --git a/gcc/testsuite/jit.dg/test-quadratic.c b/gcc/testsuite/jit.dg/test-quadratic.c new file mode 100644 index 0000000..7b154fe --- /dev/null +++ b/gcc/testsuite/jit.dg/test-quadratic.c @@ -0,0 +1,488 @@ +#include <stdlib.h> +#include <stdio.h> + +#include "libgccjit.h" + +#include "harness.h" + +struct quadratic +{ + double a; + double b; + double c; + double discriminant; +}; + +/* 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; + } +*/ + +struct quadratic_test +{ + 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 *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 *calc_discriminant; + + gcc_jit_function *sqrt; + +}; + +static void +make_types (struct quadratic_test *testcase) +{ + testcase->numeric_type = + gcc_jit_context_get_type (testcase->ctxt, GCC_JIT_TYPE_DOUBLE); + testcase->numeric_type_ptr = + gcc_jit_type_get_pointer (testcase->numeric_type); + testcase->zero = + gcc_jit_context_zero (testcase->ctxt, testcase->numeric_type); + + testcase->int_type = + gcc_jit_context_get_type (testcase->ctxt, GCC_JIT_TYPE_INT); + + testcase->a = + gcc_jit_context_new_field (testcase->ctxt, + NULL, + testcase->numeric_type, + "a"); + testcase->b = + gcc_jit_context_new_field (testcase->ctxt, + NULL, + testcase->numeric_type, + "b"); + testcase->c = + gcc_jit_context_new_field (testcase->ctxt, + NULL, + testcase->numeric_type, + "c"); + testcase->discriminant = + gcc_jit_context_new_field (testcase->ctxt, + NULL, + testcase->numeric_type, + "discriminant"); + gcc_jit_field *fields[] = {testcase->a, + testcase->b, + testcase->c, + testcase->discriminant}; + testcase->quadratic = + gcc_jit_context_new_struct_type (testcase->ctxt, NULL, + "quadratic", 4, fields); + testcase->quadratic_ptr = gcc_jit_type_get_pointer (testcase->quadratic); +} + +static void +make_sqrt (struct quadratic_test *testcase) +{ + gcc_jit_param *param_x = + gcc_jit_context_new_param (testcase->ctxt, NULL, + testcase->numeric_type, "x"); + testcase->sqrt = + gcc_jit_context_new_function (testcase->ctxt, NULL, + GCC_JIT_FUNCTION_IMPORTED, + testcase->numeric_type, + "sqrt", + 1, ¶m_x, + 0); +} + +static void +make_calc_discriminant (struct quadratic_test *testcase) +{ + /* Build "calc_discriminant". */ + gcc_jit_param *param_q = + gcc_jit_context_new_param (testcase->ctxt, NULL, + testcase->quadratic_ptr, "q"); + testcase->calc_discriminant = + gcc_jit_context_new_function (testcase->ctxt, NULL, + GCC_JIT_FUNCTION_EXPORTED, + testcase->numeric_type, + "calc_discriminant", + 1, ¶m_q, + 0); + gcc_jit_function_add_comment ( + testcase->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 ( + testcase->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 ( + testcase->ctxt, NULL, + GCC_JIT_BINARY_OP_MINUS, + testcase->numeric_type, + + /* (q->b * q->b) */ + gcc_jit_context_new_binary_op ( + testcase->ctxt, NULL, + GCC_JIT_BINARY_OP_MULT, + testcase->numeric_type, + q_b, q_b), + + /* (4 * (q->a * q->c)) */ + gcc_jit_context_new_binary_op ( + testcase->ctxt, NULL, + GCC_JIT_BINARY_OP_MULT, + testcase->numeric_type, + /* 4.0 */ + gcc_jit_context_new_rvalue_from_int ( + testcase->ctxt, + testcase->numeric_type, + 4), + /* (q->a * q->c) */ + gcc_jit_context_new_binary_op ( + testcase->ctxt, NULL, + GCC_JIT_BINARY_OP_MULT, + testcase->numeric_type, + q_a, q_c)))); /* end of gcc_jit_function_add_assignment call. */ +} + +static void +make_test_quadratic (struct quadratic_test *testcase) +{ + gcc_jit_param *a = + gcc_jit_context_new_param (testcase->ctxt, NULL, + testcase->numeric_type, "a"); + gcc_jit_param *b = + gcc_jit_context_new_param (testcase->ctxt, NULL, + testcase->numeric_type, "b"); + gcc_jit_param *c = + gcc_jit_context_new_param (testcase->ctxt, NULL, + testcase->numeric_type, "c"); + gcc_jit_param *r1 = + gcc_jit_context_new_param (testcase->ctxt, NULL, + testcase->numeric_type_ptr, "r1"); + gcc_jit_param *r2 = + gcc_jit_context_new_param (testcase->ctxt, NULL, + testcase->numeric_type_ptr, "r2"); + gcc_jit_param *params[] = {a, b, c, r1, r2}; + gcc_jit_function *test_quadratic = + gcc_jit_context_new_function (testcase->ctxt, NULL, + GCC_JIT_FUNCTION_EXPORTED, + testcase->int_type, + "test_quadratic", + 5, params, + 0); + + /* struct quadratic q; */ + gcc_jit_lvalue *q = + gcc_jit_function_new_local ( + test_quadratic, NULL, + testcase->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 ( + testcase->ctxt, NULL, + testcase->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 ( + testcase->ctxt, NULL, + GCC_JIT_COMPARISON_GT, + gcc_jit_rvalue_access_field ( + gcc_jit_lvalue_as_rvalue (q), + NULL, + "discriminant"), + testcase->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, + testcase->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 ( + testcase->ctxt, NULL, + testcase->sqrt, + 1, &discriminant_of_q)); + + gcc_jit_rvalue *minus_b = + gcc_jit_context_new_unary_op ( + testcase->ctxt, NULL, + GCC_JIT_UNARY_OP_MINUS, + testcase->numeric_type, + gcc_jit_param_as_rvalue (b)); + gcc_jit_rvalue *two_a = + gcc_jit_context_new_binary_op ( + testcase->ctxt, NULL, + GCC_JIT_BINARY_OP_MULT, + testcase->numeric_type, + gcc_jit_context_new_rvalue_from_int ( + testcase->ctxt, + testcase->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 ( + testcase->ctxt, NULL, + GCC_JIT_BINARY_OP_DIVIDE, + testcase->numeric_type, + gcc_jit_context_new_binary_op ( + testcase->ctxt, NULL, + GCC_JIT_BINARY_OP_PLUS, + testcase->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 ( + testcase->ctxt, NULL, + GCC_JIT_BINARY_OP_DIVIDE, + testcase->numeric_type, + gcc_jit_context_new_binary_op ( + testcase->ctxt, NULL, + GCC_JIT_BINARY_OP_MINUS, + testcase->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 ( + testcase->ctxt, + testcase->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 ( + testcase->ctxt, NULL, + GCC_JIT_COMPARISON_EQ, + gcc_jit_rvalue_access_field ( + gcc_jit_lvalue_as_rvalue (q), + NULL, + "discriminant"), + testcase->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 ( + testcase->ctxt, NULL, + GCC_JIT_BINARY_OP_DIVIDE, + testcase->numeric_type, + minus_b, + two_a)); + + /* "return 1;" */ + gcc_jit_function_add_return ( + test_quadratic, NULL, + gcc_jit_context_one (testcase->ctxt, testcase->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 (testcase->ctxt, testcase->int_type)); +} + +int +code_making_callback (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); + return 0; +} + +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