1 /*
2 Code shared between multiple testcases.
3
4 This file contains "main" and support code.
5 Each testcase should implement the following hooks:
6
7 extern void
8 create_code (gcc_jit_context *ctxt, void * user_data);
9
10 and, #ifndef TEST_COMPILING_TO_FILE,
11
12 extern void
13 verify_code (gcc_jit_context *ctxt, gcc_jit_result *result);
14 */
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <unistd.h>
18
19 /* test-threads.c use threads, but dejagnu.h isn't thread-safe; there's a
20 shared "buffer", and the counts of passed/failed etc are globals.
21
22 The solution is to use macros to rename "pass" and "fail", replacing them
23 with mutex-guarded alternatives. */
24 #ifdef MAKE_DEJAGNU_H_THREADSAFE
25 #define pass dejagnu_pass
26 #define fail dejagnu_fail
27 #define note dejagnu_note
28 #endif
29
30 #include <dejagnu.h>
31
32 #ifdef MAKE_DEJAGNU_H_THREADSAFE
33 #undef pass
34 #undef fail
35 #undef note
36 #endif
37
38 static char test[1024];
39
40 #define CHECK_NON_NULL(PTR) \
41 do { \
42 if ((PTR) != NULL) \
43 { \
44 pass ("%s: %s: %s is non-null", \
45 test, __func__, #PTR); \
46 } \
47 else \
48 { \
49 fail ("%s: %s: %s is NULL", \
50 test, __func__, #PTR); \
51 abort (); \
52 } \
53 } while (0)
54
55 #define CHECK_VALUE(ACTUAL, EXPECTED) \
56 do { \
57 if ((ACTUAL) == (EXPECTED)) \
58 { \
59 pass ("%s: %s: actual: %s == expected: %s", \
60 test, __func__, #ACTUAL, #EXPECTED); \
61 } \
62 else \
63 { \
64 fail ("%s: %s: actual: %s != expected: %s", \
65 test, __func__, #ACTUAL, #EXPECTED); \
66 fprintf (stderr, "incorrect value\n"); \
67 abort (); \
68 } \
69 } while (0)
70
71 #define CHECK_DOUBLE_VALUE(ACTUAL, EXPECTED) \
72 do { \
73 double expected = (EXPECTED); \
74 double actual = (ACTUAL); \
75 if (abs (actual - expected) < 0.00001) \
76 { \
77 pass ("%s: %s: actual: %s == expected: %s", \
78 __func__, test, #ACTUAL, #EXPECTED); \
79 } \
80 else \
81 { \
82 fail ("%s: %s: actual: %s != expected: %s", \
83 __func__, test, #ACTUAL, #EXPECTED); \
84 fprintf (stderr, "incorrect value: %f\n", actual); \
85 abort (); \
86 } \
87 } while (0)
88
89 #define CHECK_STRING_VALUE(ACTUAL, EXPECTED) \
90 check_string_value (__func__, (ACTUAL), (EXPECTED));
91
92 #define CHECK_STRING_STARTS_WITH(ACTUAL, EXPECTED_PREFIX) \
93 check_string_starts_with (__func__, (ACTUAL), (EXPECTED_PREFIX));
94
95 #define CHECK_STRING_CONTAINS(ACTUAL, EXPECTED_SUBSTRING) \
96 check_string_contains (__func__, #ACTUAL, (ACTUAL), (EXPECTED_SUBSTRING));
97
98 #define CHECK(COND) \
99 do { \
100 if (COND) \
101 { \
102 pass ("%s: %s: %s", test, __func__, #COND); \
103 } \
104 else \
105 { \
106 fail ("%s: %s: %s", test, __func__, #COND); \
107 abort (); \
108 } \
109 } while (0)
110
111 #define CHECK_NO_ERRORS(CTXT) \
112 do { \
113 const char *err = gcc_jit_context_get_first_error (CTXT); \
114 if (err) \
115 fail ("%s: %s: error unexpectedly occurred: %s", test, __func__, err); \
116 else \
117 pass ("%s: %s: no errors occurred", test, __func__); \
118 } while (0)
119
120 /* Hooks that testcases should provide. */
121 extern void
122 create_code (gcc_jit_context *ctxt, void * user_data);
123
124 #ifndef TEST_COMPILING_TO_FILE
125 extern void
126 verify_code (gcc_jit_context *ctxt, gcc_jit_result *result);
127 #endif
128
129 extern void check_string_value (const char *funcname,
130 const char *actual, const char *expected);
131
132 extern void
133 check_string_starts_with (const char *funcname,
134 const char *actual,
135 const char *expected_prefix);
136
137 extern void
138 check_string_contains (const char *funcname,
139 const char *name,
140 const char *actual,
141 const char *expected_substring);
142
143 /* Implement framework needed for turning the testcase hooks into an
144 executable. test-combination.c and test-threads.c each combine multiple
145 testcases into larger testcases, so we have COMBINED_TEST as a way of
146 temporarily turning off this part of harness.h. */
147 #ifndef COMBINED_TEST
148
check_string_value(const char * funcname,const char * actual,const char * expected)149 void check_string_value (const char *funcname,
150 const char *actual, const char *expected)
151 {
152 if (actual && !expected)
153 {
154 fail ("%s: %s: actual: \"%s\" != expected: NULL",
155 funcname, test, actual);
156 fprintf (stderr, "incorrect value\n");
157 abort ();
158 }
159 if (expected && !actual)
160 {
161 fail ("%s: %s: actual: NULL != expected: \"%s\"",
162 funcname, test, expected);
163 fprintf (stderr, "incorrect value\n");
164 abort ();
165 }
166 if (actual && expected)
167 {
168 if (strcmp (actual, expected))
169 {
170 fail ("%s: %s: actual: \"%s\" != expected: \"%s\"",
171 test, funcname, actual, expected);
172 fprintf (stderr, "incorrect valuen");
173 abort ();
174 }
175 pass ("%s: %s: actual: \"%s\" == expected: \"%s\"",
176 test, funcname, actual, expected);
177 }
178 else
179 pass ("%s: actual: NULL == expected: NULL");
180 }
181
182 void
check_string_starts_with(const char * funcname,const char * actual,const char * expected_prefix)183 check_string_starts_with (const char *funcname,
184 const char *actual,
185 const char *expected_prefix)
186 {
187 if (!actual)
188 {
189 fail ("%s: %s: actual: NULL != expected prefix: \"%s\"",
190 test, funcname, expected_prefix);
191 fprintf (stderr, "incorrect value\n");
192 abort ();
193 }
194
195 if (strncmp (actual, expected_prefix, strlen (expected_prefix)))
196 {
197 fail ("%s: %s: actual: \"%s\" did not begin with expected prefix: \"%s\"",
198 test, funcname, actual, expected_prefix);
199 fprintf (stderr, "incorrect value\n");
200 abort ();
201 }
202
203 pass ("%s: actual: \"%s\" begins with expected prefix: \"%s\"",
204 test, actual, expected_prefix);
205 }
206
207 void
check_string_contains(const char * funcname,const char * name,const char * actual,const char * expected_substring)208 check_string_contains (const char *funcname,
209 const char *name,
210 const char *actual,
211 const char *expected_substring)
212 {
213 if (!actual)
214 {
215 fail ("%s: %s, %s: actual: NULL does not contain expected substring: \"%s\"",
216 test, funcname, name, expected_substring);
217 fprintf (stderr, "incorrect value\n");
218 abort ();
219 }
220
221 if (!strstr (actual, expected_substring))
222 {
223 fail ("%s: %s: %s: actual: \"%s\" did not contain expected substring: \"%s\"",
224 test, funcname, name, actual, expected_substring);
225 fprintf (stderr, "incorrect value\n");
226 abort ();
227 }
228
229 pass ("%s: %s: %s: found substring: \"%s\"",
230 test, funcname, name, expected_substring);
231 }
232
233 #ifndef TEST_ESCHEWS_SET_OPTIONS
set_options(gcc_jit_context * ctxt,const char * argv0)234 static void set_options (gcc_jit_context *ctxt, const char *argv0)
235 {
236 /* Set up options. */
237 gcc_jit_context_set_str_option (
238 ctxt,
239 GCC_JIT_STR_OPTION_PROGNAME,
240 argv0);
241 gcc_jit_context_set_int_option (
242 ctxt,
243 GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL,
244 3);
245 gcc_jit_context_set_bool_option (
246 ctxt,
247 GCC_JIT_BOOL_OPTION_DEBUGINFO,
248 1);
249 gcc_jit_context_set_bool_option (
250 ctxt,
251 GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE,
252 0);
253 gcc_jit_context_set_bool_option (
254 ctxt,
255 GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE,
256 0);
257 gcc_jit_context_set_bool_option (
258 ctxt,
259 GCC_JIT_BOOL_OPTION_SELFCHECK_GC,
260 1);
261 gcc_jit_context_set_bool_option (
262 ctxt,
263 GCC_JIT_BOOL_OPTION_DUMP_SUMMARY,
264 0);
265 }
266 #endif /* #ifndef TEST_ESCHEWS_SET_OPTIONS */
267
268 /* Concatenate two strings. The result must be released using "free". */
269
270 char *
concat_strings(const char * prefix,const char * suffix)271 concat_strings (const char *prefix, const char *suffix)
272 {
273 char *result = (char *)malloc (strlen (prefix) + strlen (suffix) + 1);
274 if (!result)
275 {
276 fail ("malloc failure");
277 return NULL;
278 }
279 strcpy (result, prefix);
280 strcpy (result + strlen (prefix), suffix);
281 result[strlen (prefix) + strlen (suffix)] = '\0';
282 return result;
283 }
284
285 #ifndef TEST_ESCHEWS_TEST_JIT
286 /* Set up logging to a logfile of the form "test-FOO.exe.log.txt".
287
288 For example,
289 SRCDIR/gcc/testsuite/jit.dg/test-hello-world.c
290 is built as:
291 BUILDDIR/gcc/testsuite/jit/test-hello-world.c.exe
292 and is logged to
293 BUILDDIR/gcc/testsuite/jit/test-hello-world.c.exe.log.txt
294
295 The logfile must be closed by the caller.
296
297 Note that not every testcase enables logging. */
298 static FILE *
set_up_logging(gcc_jit_context * ctxt,const char * argv0)299 set_up_logging (gcc_jit_context *ctxt, const char *argv0)
300 {
301 const char *logfile_name_suffix = ".log.txt";
302 char *logfile_name = NULL;
303 FILE *logfile = NULL;
304
305 /* Build a logfile name of the form "test-FOO.exe.log.txt". */
306 logfile_name = concat_strings (argv0, logfile_name_suffix);
307 if (!logfile_name)
308 return NULL;
309 logfile = fopen (logfile_name, "w");
310 CHECK_NON_NULL (logfile);
311 free (logfile_name);
312
313 if (logfile)
314 gcc_jit_context_set_logfile (ctxt, logfile, 0, 0);
315
316 return logfile;
317 }
318
319 /* Exercise the API entrypoint:
320 gcc_jit_context_dump_reproducer_to_file
321 by calling it on the context, using the path expected by jit.exp. */
322 static void
dump_reproducer(gcc_jit_context * ctxt,const char * argv0)323 dump_reproducer (gcc_jit_context *ctxt, const char *argv0)
324 {
325 char *reproducer_name;
326 reproducer_name = concat_strings (argv0, ".reproducer.c");
327 if (!reproducer_name)
328 return;
329 note ("%s: writing reproducer to %s", test, reproducer_name);
330 gcc_jit_context_dump_reproducer_to_file (ctxt, reproducer_name);
331 free (reproducer_name);
332 }
333
334 /* Run one iteration of the test. */
335 static void
test_jit(const char * argv0,void * user_data)336 test_jit (const char *argv0, void *user_data)
337 {
338 gcc_jit_context *ctxt;
339 FILE *logfile;
340 #ifndef TEST_COMPILING_TO_FILE
341 gcc_jit_result *result;
342 #endif
343
344 #ifdef TEST_COMPILING_TO_FILE
345 unlink (OUTPUT_FILENAME);
346 #endif
347
348 ctxt = gcc_jit_context_acquire ();
349 if (!ctxt)
350 {
351 fail ("gcc_jit_context_acquire failed");
352 return;
353 }
354
355 logfile = set_up_logging (ctxt, argv0);
356
357 set_options (ctxt, argv0);
358
359 create_code (ctxt, user_data);
360
361 dump_reproducer (ctxt, argv0);
362
363 #ifdef TEST_COMPILING_TO_FILE
364 gcc_jit_context_compile_to_file (ctxt,
365 (OUTPUT_KIND),
366 (OUTPUT_FILENAME));
367 CHECK_NO_ERRORS (ctxt);
368 #else /* #ifdef TEST_COMPILING_TO_FILE */
369 /* This actually calls into GCC and runs the build, all
370 in a mutex for now. */
371 result = gcc_jit_context_compile (ctxt);
372
373 verify_code (ctxt, result);
374 #endif
375
376 gcc_jit_context_release (ctxt);
377
378 #ifndef TEST_COMPILING_TO_FILE
379 /* Once we're done with the code, this unloads the built .so file: */
380 gcc_jit_result_release (result);
381 #endif
382
383 if (logfile)
384 fclose (logfile);
385 }
386 #endif /* #ifndef TEST_ESCHEWS_TEST_JIT */
387
388 /* We want to prefix all unit test results with the test, but dejagnu.exp's
389 host_execute appears to get confused by the leading "./" of argv0,
390 leading to all tests simply reporting as a single period character ".".
391
392 Hence strip out the final component of the path to the program name,
393 so that we can use that in unittest reports. */
394 const char*
extract_progname(const char * argv0)395 extract_progname (const char *argv0)
396 {
397 const char *p;
398
399 p = argv0 + strlen (argv0);
400 while (p != argv0 && p[-1] != '/')
401 --p;
402 return p;
403 }
404
405 #ifndef TEST_PROVIDES_MAIN
406 int
main(int argc,char ** argv)407 main (int argc, char **argv)
408 {
409 int i;
410
411 for (i = 1; i <= 5; i++)
412 {
413 snprintf (test, sizeof (test),
414 "%s iteration %d of %d",
415 extract_progname (argv[0]),
416 i, 5);
417
418 //printf ("ITERATION %d\n", i);
419 test_jit (argv[0], NULL);
420 //printf ("\n");
421 }
422
423 totals ();
424
425 return 0;
426 }
427 #endif /* #ifndef TEST_PROVIDES_MAIN */
428
429 #endif /* #ifndef COMBINED_TEST */
430