#include #include #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; gcc_jit_type *void_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->void_type = gcc_jit_context_get_type (top_level->ctxt, GCC_JIT_TYPE_VOID); 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_struct_as_type ( 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->void_type, "calc_discriminant", 1, ¶m_q, 0); gcc_jit_block *blk = gcc_jit_function_new_block (middle_level->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, top_level->a)); gcc_jit_rvalue *q_b = gcc_jit_lvalue_as_rvalue ( gcc_jit_rvalue_dereference_field ( gcc_jit_param_as_rvalue (param_q), NULL, top_level->b)); gcc_jit_rvalue *q_c = gcc_jit_lvalue_as_rvalue ( gcc_jit_rvalue_dereference_field ( gcc_jit_param_as_rvalue (param_q), NULL, top_level->c)); gcc_jit_block_add_assignment ( blk, NULL, /* q->discriminant =... */ gcc_jit_rvalue_dereference_field ( gcc_jit_param_as_rvalue (param_q), NULL, top_level->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. */ gcc_jit_block_end_with_void_return (blk, NULL); } 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"); 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, top_level->a), gcc_jit_param_as_rvalue (a)); /* q.b = b; */ gcc_jit_block_add_assignment ( initial, NULL, gcc_jit_lvalue_access_field (q, NULL, top_level->b), gcc_jit_param_as_rvalue (b)); /* q.c = c; */ gcc_jit_block_add_assignment ( initial, NULL, gcc_jit_lvalue_access_field (q, NULL, top_level->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 ( bottom_level->ctxt, NULL, middle_level->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 ( bottom_level->ctxt, NULL, GCC_JIT_COMPARISON_GT, gcc_jit_rvalue_access_field ( gcc_jit_lvalue_as_rvalue (q), NULL, top_level->discriminant), top_level->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, top_level->numeric_type, "s"); gcc_jit_rvalue *discriminant_of_q = gcc_jit_rvalue_access_field (gcc_jit_lvalue_as_rvalue (q), NULL, top_level->discriminant); gcc_jit_block_add_assignment ( on_positive_discriminant, 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_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 ( 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_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 ( 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_block_end_with_return ( on_positive_discriminant, NULL, gcc_jit_context_new_rvalue_from_int ( bottom_level->ctxt, top_level->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 ( bottom_level->ctxt, NULL, GCC_JIT_COMPARISON_EQ, gcc_jit_rvalue_access_field ( gcc_jit_lvalue_as_rvalue (q), NULL, top_level->discriminant), top_level->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 ( bottom_level->ctxt, NULL, GCC_JIT_BINARY_OP_DIVIDE, top_level->numeric_type, minus_b, two_a)); /* "return 1;" */ gcc_jit_block_end_with_return ( on_zero_discriminant, NULL, gcc_jit_context_one (bottom_level->ctxt, top_level->int_type)); /* Block: "on_negative_discriminant" */ gcc_jit_block_end_with_return ( /* else return 0; */ on_negative_discriminant, 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. */ FILE *logfile = fopen ("test-nested-contexts.c.exe.log.txt", "w"); if (!logfile) fail ("error opening logfile"); 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 (); gcc_jit_context_set_logfile (top_level.ctxt, logfile, 0, 0); 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); gcc_jit_context_dump_to_file (top_level.ctxt, "dump-of-test-nested-contexts-top.c", 1); 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_context_dump_to_file (middle_level.ctxt, "dump-of-test-nested-contexts-middle.c", 1); 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_context_dump_to_file (bottom_level.ctxt, "dump-of-test-nested-contexts-bottom.c", 1); /* Dump a reproducer for the bottom context. The generated reproducer needs to also regenerate the parent contexts, so this gives us test coverage for that case. */ gcc_jit_context_dump_reproducer_to_file ( bottom_level.ctxt, "test-nested-contexts.c.exe.reproducer.c"); 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); } if (logfile) fclose (logfile); totals (); return 0; }