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