| #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); |
| |
| static 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; |
| gcc_jit_type *void_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->void_type = |
| gcc_jit_context_get_type (testcase->ctxt, GCC_JIT_TYPE_VOID); |
| |
| 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_struct_as_type ( |
| 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_INTERNAL, |
| testcase->void_type, |
| "calc_discriminant", |
| 1, ¶m_q, |
| 0); |
| gcc_jit_block *blk = |
| gcc_jit_function_new_block (testcase->calc_discriminant, NULL); |
| gcc_jit_block_add_comment ( |
| blk, 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, testcase->a)); |
| gcc_jit_rvalue *q_b = |
| gcc_jit_lvalue_as_rvalue ( |
| gcc_jit_rvalue_dereference_field ( |
| gcc_jit_param_as_rvalue (param_q), |
| NULL, testcase->b)); |
| gcc_jit_rvalue *q_c = |
| gcc_jit_lvalue_as_rvalue ( |
| gcc_jit_rvalue_dereference_field ( |
| gcc_jit_param_as_rvalue (param_q), |
| NULL, testcase->c)); |
| |
| /* (q->b * q->b) - (4 * q->a * q->c) */ |
| gcc_jit_rvalue *rhs = |
| 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))); |
| |
| CHECK_STRING_VALUE ( |
| gcc_jit_object_get_debug_string (gcc_jit_rvalue_as_object (rhs)), |
| "q->b * q->b - (double)4 * q->a * q->c"); |
| |
| gcc_jit_block_add_assignment ( |
| blk, NULL, |
| |
| /* q->discriminant =... */ |
| gcc_jit_rvalue_dereference_field ( |
| gcc_jit_param_as_rvalue (param_q), |
| NULL, |
| testcase->discriminant), |
| rhs); |
| |
| gcc_jit_block_end_with_void_return (blk, NULL); |
| } |
| |
| 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"); |
| |
| gcc_jit_block *initial = |
| gcc_jit_function_new_block (test_quadratic, |
| "initial"); |
| gcc_jit_block *on_positive_discriminant |
| = gcc_jit_function_new_block (test_quadratic, |
| "positive_discriminant"); |
| |
| gcc_jit_block *on_nonpositive_discriminant |
| = gcc_jit_function_new_block (test_quadratic, |
| "nonpositive_discriminant"); |
| |
| gcc_jit_block *on_zero_discriminant |
| = gcc_jit_function_new_block (test_quadratic, |
| "zero_discriminant"); |
| |
| gcc_jit_block *on_negative_discriminant |
| = gcc_jit_function_new_block (test_quadratic, |
| "negative_discriminant"); |
| |
| /* Initial block. */ |
| /* q.a = a; */ |
| gcc_jit_block_add_assignment ( |
| initial, NULL, |
| gcc_jit_lvalue_access_field (q, NULL, testcase->a), |
| gcc_jit_param_as_rvalue (a)); |
| /* q.b = b; */ |
| gcc_jit_block_add_assignment ( |
| initial, NULL, |
| gcc_jit_lvalue_access_field (q, NULL, testcase->b), |
| gcc_jit_param_as_rvalue (b)); |
| /* q.c = c; */ |
| gcc_jit_block_add_assignment ( |
| initial, NULL, |
| gcc_jit_lvalue_access_field (q, NULL, testcase->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_block_add_eval ( |
| initial, NULL, |
| gcc_jit_context_new_call ( |
| testcase->ctxt, NULL, |
| testcase->calc_discriminant, |
| 1, &address_of_q)); |
| |
| gcc_jit_block_add_comment ( |
| initial, NULL, |
| "if (q.discriminant > 0)"); |
| gcc_jit_block_end_with_conditional ( |
| initial, 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, |
| testcase->discriminant), |
| testcase->zero), |
| on_positive_discriminant, |
| on_nonpositive_discriminant); |
| |
| /* Block: "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, |
| testcase->discriminant); |
| gcc_jit_block_add_assignment ( |
| on_positive_discriminant, 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_block_add_comment ( |
| on_positive_discriminant, NULL, |
| "*r1 = (-b + s) / (2 * a);"); |
| gcc_jit_block_add_assignment ( |
| on_positive_discriminant, 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_block_add_comment ( |
| on_positive_discriminant, NULL, |
| "*r2 = (-b - s) / (2 * a)"); |
| gcc_jit_block_add_assignment ( |
| on_positive_discriminant, 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_block_end_with_return ( |
| on_positive_discriminant, NULL, |
| gcc_jit_context_new_rvalue_from_int ( |
| testcase->ctxt, |
| testcase->int_type, |
| 2)); |
| |
| /* Block: "on_nonpositive_discriminant" */ |
| gcc_jit_block_add_comment ( |
| on_nonpositive_discriminant, NULL, |
| "else if (q.discriminant == 0)"); |
| gcc_jit_block_end_with_conditional ( |
| on_nonpositive_discriminant, 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, |
| testcase->discriminant), |
| testcase->zero), |
| on_zero_discriminant, |
| on_negative_discriminant); |
| |
| /* Block: "on_zero_discriminant" */ |
| gcc_jit_block_add_comment ( |
| on_zero_discriminant, NULL, |
| "*r1 = -b / (2 * a);"); |
| gcc_jit_block_add_assignment ( |
| on_zero_discriminant, 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)); |
| gcc_jit_block_end_with_return ( |
| /* "return 1;" */ |
| on_zero_discriminant, NULL, |
| gcc_jit_context_one (testcase->ctxt, testcase->int_type)); |
| |
| /* Block: "on_negative_discriminant" */ |
| gcc_jit_block_end_with_return ( |
| /* "else return 0;" */ |
| on_negative_discriminant, NULL, |
| gcc_jit_context_zero (testcase->ctxt, 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); |
| } |