/* Code shared between multiple testcases. This file contains "main" and support code. Each testcase should implement the following hooks: extern void create_code (gcc_jit_context *ctxt, void * user_data); and, #ifndef TEST_COMPILING_TO_FILE, extern void verify_code (gcc_jit_context *ctxt, gcc_jit_result *result); */ #include #include #include /* test-threads.c use threads, but dejagnu.h isn't thread-safe; there's a shared "buffer", and the counts of passed/failed etc are globals. The solution is to use macros to rename "pass" and "fail", replacing them with mutex-guarded alternatives. */ #ifdef MAKE_DEJAGNU_H_THREADSAFE #define pass dejagnu_pass #define fail dejagnu_fail #define note dejagnu_note #endif #include #ifdef MAKE_DEJAGNU_H_THREADSAFE #undef pass #undef fail #undef note #endif static char test[1024]; #define CHECK_NON_NULL(PTR) \ do { \ if ((PTR) != NULL) \ { \ pass ("%s: %s: %s is non-null", \ test, __func__, #PTR); \ } \ else \ { \ fail ("%s: %s: %s is NULL", \ test, __func__, #PTR); \ abort (); \ } \ } while (0) #define CHECK_VALUE(ACTUAL, EXPECTED) \ do { \ if ((ACTUAL) == (EXPECTED)) \ { \ pass ("%s: %s: actual: %s == expected: %s", \ test, __func__, #ACTUAL, #EXPECTED); \ } \ else \ { \ fail ("%s: %s: actual: %s != expected: %s", \ test, __func__, #ACTUAL, #EXPECTED); \ fprintf (stderr, "incorrect value\n"); \ abort (); \ } \ } while (0) #define CHECK_DOUBLE_VALUE(ACTUAL, EXPECTED) \ do { \ double expected = (EXPECTED); \ double actual = (ACTUAL); \ if (abs (actual - expected) < 0.00001) \ { \ pass ("%s: %s: actual: %s == expected: %s", \ __func__, test, #ACTUAL, #EXPECTED); \ } \ else \ { \ fail ("%s: %s: actual: %s != expected: %s", \ __func__, test, #ACTUAL, #EXPECTED); \ fprintf (stderr, "incorrect value: %f\n", actual); \ abort (); \ } \ } while (0) #define CHECK_STRING_VALUE(ACTUAL, EXPECTED) \ check_string_value (__func__, (ACTUAL), (EXPECTED)); #define CHECK_STRING_STARTS_WITH(ACTUAL, EXPECTED_PREFIX) \ check_string_starts_with (__func__, (ACTUAL), (EXPECTED_PREFIX)); #define CHECK_STRING_CONTAINS(ACTUAL, EXPECTED_SUBSTRING) \ check_string_contains (__func__, #ACTUAL, (ACTUAL), (EXPECTED_SUBSTRING)); #define CHECK(COND) \ do { \ if (COND) \ { \ pass ("%s: %s: %s", test, __func__, #COND); \ } \ else \ { \ fail ("%s: %s: %s", test, __func__, #COND); \ abort (); \ } \ } while (0) #define CHECK_NO_ERRORS(CTXT) \ do { \ const char *err = gcc_jit_context_get_first_error (CTXT); \ if (err) \ fail ("%s: %s: error unexpectedly occurred: %s", test, __func__, err); \ else \ pass ("%s: %s: no errors occurred", test, __func__); \ } while (0) /* Hooks that testcases should provide. */ extern void create_code (gcc_jit_context *ctxt, void * user_data); #ifndef TEST_COMPILING_TO_FILE extern void verify_code (gcc_jit_context *ctxt, gcc_jit_result *result); #endif extern void check_string_value (const char *funcname, const char *actual, const char *expected); extern void check_string_starts_with (const char *funcname, const char *actual, const char *expected_prefix); extern void check_string_contains (const char *funcname, const char *name, const char *actual, const char *expected_substring); /* Implement framework needed for turning the testcase hooks into an executable. test-combination.c and test-threads.c each combine multiple testcases into larger testcases, so we have COMBINED_TEST as a way of temporarily turning off this part of harness.h. */ #ifndef COMBINED_TEST void check_string_value (const char *funcname, const char *actual, const char *expected) { if (actual && !expected) { fail ("%s: %s: actual: \"%s\" != expected: NULL", funcname, test, actual); fprintf (stderr, "incorrect value\n"); abort (); } if (expected && !actual) { fail ("%s: %s: actual: NULL != expected: \"%s\"", funcname, test, expected); fprintf (stderr, "incorrect value\n"); abort (); } if (actual && expected) { if (strcmp (actual, expected)) { fail ("%s: %s: actual: \"%s\" != expected: \"%s\"", test, funcname, actual, expected); fprintf (stderr, "incorrect valuen"); abort (); } pass ("%s: %s: actual: \"%s\" == expected: \"%s\"", test, funcname, actual, expected); } else pass ("%s: actual: NULL == expected: NULL"); } void check_string_starts_with (const char *funcname, const char *actual, const char *expected_prefix) { if (!actual) { fail ("%s: %s: actual: NULL != expected prefix: \"%s\"", test, funcname, expected_prefix); fprintf (stderr, "incorrect value\n"); abort (); } if (strncmp (actual, expected_prefix, strlen (expected_prefix))) { fail ("%s: %s: actual: \"%s\" did not begin with expected prefix: \"%s\"", test, funcname, actual, expected_prefix); fprintf (stderr, "incorrect value\n"); abort (); } pass ("%s: actual: \"%s\" begins with expected prefix: \"%s\"", test, actual, expected_prefix); } void check_string_contains (const char *funcname, const char *name, const char *actual, const char *expected_substring) { if (!actual) { fail ("%s: %s, %s: actual: NULL does not contain expected substring: \"%s\"", test, funcname, name, expected_substring); fprintf (stderr, "incorrect value\n"); abort (); } if (!strstr (actual, expected_substring)) { fail ("%s: %s: %s: actual: \"%s\" did not contain expected substring: \"%s\"", test, funcname, name, actual, expected_substring); fprintf (stderr, "incorrect value\n"); abort (); } pass ("%s: %s: %s: found substring: \"%s\"", test, funcname, name, expected_substring); } #ifndef TEST_ESCHEWS_SET_OPTIONS static void set_options (gcc_jit_context *ctxt, const char *argv0) { /* Set up options. */ gcc_jit_context_set_str_option ( ctxt, GCC_JIT_STR_OPTION_PROGNAME, argv0); gcc_jit_context_set_int_option ( ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3); gcc_jit_context_set_bool_option ( ctxt, GCC_JIT_BOOL_OPTION_DEBUGINFO, 1); gcc_jit_context_set_bool_option ( ctxt, GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE, 0); gcc_jit_context_set_bool_option ( ctxt, GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, 0); gcc_jit_context_set_bool_option ( ctxt, GCC_JIT_BOOL_OPTION_SELFCHECK_GC, 1); gcc_jit_context_set_bool_option ( ctxt, GCC_JIT_BOOL_OPTION_DUMP_SUMMARY, 0); } #endif /* #ifndef TEST_ESCHEWS_SET_OPTIONS */ /* Concatenate two strings. The result must be released using "free". */ char * concat_strings (const char *prefix, const char *suffix) { char *result = (char *)malloc (strlen (prefix) + strlen (suffix) + 1); if (!result) { fail ("malloc failure"); return NULL; } strcpy (result, prefix); strcpy (result + strlen (prefix), suffix); result[strlen (prefix) + strlen (suffix)] = '\0'; return result; } #ifndef TEST_ESCHEWS_TEST_JIT /* Set up logging to a logfile of the form "test-FOO.exe.log.txt". For example, SRCDIR/gcc/testsuite/jit.dg/test-hello-world.c is built as: BUILDDIR/gcc/testsuite/jit/test-hello-world.c.exe and is logged to BUILDDIR/gcc/testsuite/jit/test-hello-world.c.exe.log.txt The logfile must be closed by the caller. Note that not every testcase enables logging. */ static FILE * set_up_logging (gcc_jit_context *ctxt, const char *argv0) { const char *logfile_name_suffix = ".log.txt"; char *logfile_name = NULL; FILE *logfile = NULL; /* Build a logfile name of the form "test-FOO.exe.log.txt". */ logfile_name = concat_strings (argv0, logfile_name_suffix); if (!logfile_name) return NULL; logfile = fopen (logfile_name, "w"); CHECK_NON_NULL (logfile); free (logfile_name); if (logfile) gcc_jit_context_set_logfile (ctxt, logfile, 0, 0); return logfile; } /* Exercise the API entrypoint: gcc_jit_context_dump_reproducer_to_file by calling it on the context, using the path expected by jit.exp. */ static void dump_reproducer (gcc_jit_context *ctxt, const char *argv0) { char *reproducer_name; reproducer_name = concat_strings (argv0, ".reproducer.c"); if (!reproducer_name) return; note ("%s: writing reproducer to %s", test, reproducer_name); gcc_jit_context_dump_reproducer_to_file (ctxt, reproducer_name); free (reproducer_name); } /* Run one iteration of the test. */ static void test_jit (const char *argv0, void *user_data) { gcc_jit_context *ctxt; FILE *logfile; #ifndef TEST_COMPILING_TO_FILE gcc_jit_result *result; #endif #ifdef TEST_COMPILING_TO_FILE unlink (OUTPUT_FILENAME); #endif ctxt = gcc_jit_context_acquire (); if (!ctxt) { fail ("gcc_jit_context_acquire failed"); return; } logfile = set_up_logging (ctxt, argv0); set_options (ctxt, argv0); create_code (ctxt, user_data); dump_reproducer (ctxt, argv0); #ifdef TEST_COMPILING_TO_FILE gcc_jit_context_compile_to_file (ctxt, (OUTPUT_KIND), (OUTPUT_FILENAME)); CHECK_NO_ERRORS (ctxt); #else /* #ifdef TEST_COMPILING_TO_FILE */ /* This actually calls into GCC and runs the build, all in a mutex for now. */ result = gcc_jit_context_compile (ctxt); verify_code (ctxt, result); #endif gcc_jit_context_release (ctxt); #ifndef TEST_COMPILING_TO_FILE /* Once we're done with the code, this unloads the built .so file: */ gcc_jit_result_release (result); #endif if (logfile) fclose (logfile); } #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, leading to all tests simply reporting as a single period character ".". Hence strip out the final component of the path to the program name, so that we can use that in unittest reports. */ const char* extract_progname (const char *argv0) { const char *p; p = argv0 + strlen (argv0); while (p != argv0 && p[-1] != '/') --p; return p; } #ifndef TEST_PROVIDES_MAIN int main (int argc, char **argv) { int i; for (i = 1; i <= 5; i++) { snprintf (test, sizeof (test), "%s iteration %d of %d", extract_progname (argv[0]), i, 5); //printf ("ITERATION %d\n", i); test_jit (argv[0], NULL); //printf ("\n"); } totals (); return 0; } #endif /* #ifndef TEST_PROVIDES_MAIN */ #endif /* #ifndef COMBINED_TEST */