1#include <math.h>
2#include <stdlib.h>
3#include <stdio.h>
4
5#include "libgccjit.h"
6
7#include "harness.h"
8
9/**********************************************************************
10 GCC_JIT_FUNCTION_ALWAYS_INLINE and GCC_JIT_FUNCTION_INTERNAL
11 **********************************************************************/
12static void
13create_test_of_hidden_function (gcc_jit_context *ctxt,
14				enum gcc_jit_function_kind hidden_kind,
15				const char *hidden_func_name,
16				const char *visible_func_name)
17{
18  /* Let's try to inject the equivalent of:
19     static double hidden_mult (double a, double b)
20     {
21       return x * x;
22     }
23     double my_square (double x)
24     {
25       return my_mult (x, x);
26     }
27
28     where hidden_mult can potentially be
29       inline  __attribute__((always_inline)).  */
30  gcc_jit_type *double_type =
31    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_DOUBLE);
32
33  /* Create "my_mult" */
34  gcc_jit_param *param_a =
35    gcc_jit_context_new_param (ctxt, NULL, double_type, "a");
36  gcc_jit_param *param_b =
37    gcc_jit_context_new_param (ctxt, NULL, double_type, "b");
38  gcc_jit_param *params[2] = {param_a, param_b};
39  gcc_jit_function *my_mult =
40    gcc_jit_context_new_function (ctxt, NULL,
41				  hidden_kind,
42                                  double_type,
43                                  hidden_func_name,
44                                  2, params,
45                                  0);
46  gcc_jit_block *body_of_my_mult =
47    gcc_jit_function_new_block (my_mult, NULL);
48  gcc_jit_block_end_with_return (
49    body_of_my_mult, NULL,
50    gcc_jit_context_new_binary_op (
51      ctxt, NULL,
52      GCC_JIT_BINARY_OP_MULT,
53      double_type,
54      gcc_jit_param_as_rvalue (param_a),
55      gcc_jit_param_as_rvalue (param_b)));
56
57  /* Create "my_square" */
58  gcc_jit_param *param_x =
59    gcc_jit_context_new_param (ctxt, NULL, double_type, "x");
60  gcc_jit_function *my_square =
61    gcc_jit_context_new_function (ctxt, NULL,
62                                  GCC_JIT_FUNCTION_EXPORTED,
63                                  double_type,
64                                  visible_func_name,
65                                  1, &param_x,
66                                  0);
67  gcc_jit_block *body_of_my_square =
68    gcc_jit_function_new_block (my_square, NULL);
69  gcc_jit_rvalue *args[2] = {gcc_jit_param_as_rvalue (param_x),
70			     gcc_jit_param_as_rvalue (param_x)};
71  gcc_jit_block_end_with_return (
72    body_of_my_square, NULL,
73    gcc_jit_context_new_call (
74      ctxt, NULL,
75      my_mult,
76      2, args));
77}
78
79static void
80create_tests_of_hidden_functions (gcc_jit_context *ctxt)
81{
82  create_test_of_hidden_function (ctxt,
83				  GCC_JIT_FUNCTION_INTERNAL,
84				  "my_internal_mult",
85				  "my_square_with_internal");
86  create_test_of_hidden_function (ctxt,
87				  GCC_JIT_FUNCTION_ALWAYS_INLINE,
88				  "my_always_inline_mult",
89				  "my_square_with_always_inline");
90}
91
92static void
93verify_hidden_functions (gcc_jit_context *ctxt, gcc_jit_result *result)
94{
95  CHECK_NON_NULL (result);
96
97  /* GCC_JIT_FUNCTION_INTERNAL and GCC_JIT_FUNCTION_ALWAYS_INLINE
98     functions should not be accessible in the result.  */
99  CHECK_VALUE (NULL, gcc_jit_result_get_code (result, "my_internal_mult"));
100  CHECK_VALUE (NULL, gcc_jit_result_get_code (result, "my_always_inline_mult"));
101
102  typedef double (*fn_type) (double);
103  fn_type my_square_with_internal =
104    (fn_type)gcc_jit_result_get_code (result, "my_square_with_internal");
105  CHECK_NON_NULL (my_square_with_internal);
106  CHECK_VALUE (my_square_with_internal (5.0), 25.0);
107
108  fn_type my_square_with_always_inline =
109    (fn_type)gcc_jit_result_get_code (result, "my_square_with_always_inline");
110  CHECK_NON_NULL (my_square_with_always_inline);
111  CHECK_VALUE (my_square_with_always_inline (5.0), 25.0);
112}
113
114/**********************************************************************
115 Builtin functions
116 **********************************************************************/
117
118static void
119create_test_of_builtin_strcmp (gcc_jit_context *ctxt)
120{
121  /* Let's try to inject the equivalent of:
122       int
123       test_of_builtin_strcmp (const char *a, const char *b)
124       {
125         return __builtin_strcmp (a, b);
126       }
127  */
128  gcc_jit_type *int_type =
129    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
130  gcc_jit_type *const_char_ptr_type =
131    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_CONST_CHAR_PTR);
132
133  /* Get the built-in function.  */
134  gcc_jit_function *builtin_fn =
135    gcc_jit_context_get_builtin_function (ctxt, "strcmp");
136
137  CHECK_STRING_VALUE (
138    gcc_jit_object_get_debug_string (gcc_jit_function_as_object (builtin_fn)),
139    "strcmp");
140
141  /* Build the test_fn.  */
142  gcc_jit_param *param_a =
143    gcc_jit_context_new_param (ctxt, NULL, const_char_ptr_type, "a");
144  gcc_jit_param *param_b =
145    gcc_jit_context_new_param (ctxt, NULL, const_char_ptr_type, "b");
146  gcc_jit_param *params[2] = {param_a, param_b};
147  gcc_jit_function *test_fn =
148    gcc_jit_context_new_function (ctxt, NULL,
149                                  GCC_JIT_FUNCTION_EXPORTED,
150                                  int_type,
151                                  "test_of_builtin_strcmp",
152                                  2, params,
153                                  0);
154  gcc_jit_rvalue *args[2] = {gcc_jit_param_as_rvalue (param_a),
155			     gcc_jit_param_as_rvalue (param_b)};
156  gcc_jit_rvalue *call =
157    gcc_jit_context_new_call (ctxt,
158                              NULL,
159                              builtin_fn,
160                              2, args);
161  CHECK_STRING_VALUE (
162    gcc_jit_object_get_debug_string (gcc_jit_rvalue_as_object (call)),
163    "strcmp (a, b)");
164
165  gcc_jit_block *initial =
166    gcc_jit_function_new_block (test_fn, "initial");
167  gcc_jit_block_end_with_return (initial, NULL, call);
168}
169
170static char *trig_sincos_dump;
171static char *trig_statistics_dump;
172
173static void
174create_test_of_builtin_trig (gcc_jit_context *ctxt)
175{
176  /* Let's try to inject the equivalent of:
177       int
178       test_of_builtin_trig (double theta)
179       {
180         return 2 * sin (theta) * cos (theta);
181       }
182       (in theory, optimizable to sin (2 * theta))
183  */
184
185  gcc_jit_context_enable_dump (ctxt,
186			       "tree-sincos",
187			       &trig_sincos_dump);
188  gcc_jit_context_enable_dump (ctxt,
189			       "statistics",
190			       &trig_statistics_dump);
191
192  gcc_jit_type *double_t =
193    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_DOUBLE);
194
195  /* Get the built-in functions.  */
196  gcc_jit_function *builtin_sin =
197    gcc_jit_context_get_builtin_function (ctxt, "sin");
198  gcc_jit_function *builtin_cos =
199    gcc_jit_context_get_builtin_function (ctxt, "cos");
200
201  /* Build the test_fn.  */
202  gcc_jit_param *param_theta =
203    gcc_jit_context_new_param (ctxt, NULL, double_t, "theta");
204  gcc_jit_function *test_fn =
205    gcc_jit_context_new_function (ctxt, NULL,
206                                  GCC_JIT_FUNCTION_EXPORTED,
207                                  double_t,
208                                  "test_of_builtin_trig",
209                                  1, &param_theta,
210                                  0);
211  gcc_jit_rvalue *args[1] = {gcc_jit_param_as_rvalue (param_theta)};
212  gcc_jit_rvalue *two =
213    gcc_jit_context_new_rvalue_from_int (ctxt, double_t, 2);
214  gcc_jit_rvalue *ret =
215    gcc_jit_context_new_binary_op (
216      ctxt, NULL,
217      GCC_JIT_BINARY_OP_MULT,
218      double_t,
219      two,
220      gcc_jit_context_new_binary_op (
221        ctxt, NULL,
222	GCC_JIT_BINARY_OP_MULT,
223	double_t,
224	gcc_jit_context_new_call (ctxt, NULL,
225				  builtin_sin,
226				  1, args),
227	gcc_jit_context_new_call (ctxt, NULL,
228				  builtin_cos,
229				  1, args)));
230  CHECK_STRING_VALUE (
231    gcc_jit_object_get_debug_string (gcc_jit_rvalue_as_object (ret)),
232    "(double)2 * sin (theta) * cos (theta)");
233
234  gcc_jit_block *initial =
235    gcc_jit_function_new_block (test_fn, "initial");
236  gcc_jit_block_end_with_return (initial, NULL, ret);
237}
238
239static void
240create_use_of_builtins (gcc_jit_context *ctxt)
241{
242  create_test_of_builtin_strcmp (ctxt);
243  create_test_of_builtin_trig (ctxt);
244}
245
246static void
247verify_test_of_builtin_strcmp (gcc_jit_context *ctxt, gcc_jit_result *result)
248{
249  typedef int (*fn_type) (const char *, const char *);
250  CHECK_NON_NULL (result);
251
252  fn_type test_of_builtin_strcmp =
253    (fn_type)gcc_jit_result_get_code (result, "test_of_builtin_strcmp");
254  CHECK_NON_NULL (test_of_builtin_strcmp);
255
256  /* Verify that it correctly called strcmp.  */
257  CHECK_VALUE (test_of_builtin_strcmp ("foo", "foo"), 0);
258  CHECK (test_of_builtin_strcmp ("foo", "bar") > 0);
259  CHECK (test_of_builtin_strcmp ("bar", "foo") < 0);
260}
261
262static void
263verify_test_of_builtin_trig (gcc_jit_context *ctxt, gcc_jit_result *result)
264{
265  typedef double (*fn_type) (double);
266  CHECK_NON_NULL (result);
267
268  fn_type test_of_builtin_trig =
269    (fn_type)gcc_jit_result_get_code (result, "test_of_builtin_trig");
270  CHECK_NON_NULL (test_of_builtin_trig);
271
272  /* Verify that it correctly computes
273        sin (2 * theta)
274     (perhaps calling sin and cos). */
275  CHECK_DOUBLE_VALUE (test_of_builtin_trig (0.0         ),  0.0);
276  CHECK_DOUBLE_VALUE (test_of_builtin_trig (M_PI_4      ),  1.0);
277  CHECK_DOUBLE_VALUE (test_of_builtin_trig (M_PI_2      ),  0.0);
278  CHECK_DOUBLE_VALUE (test_of_builtin_trig (M_PI_4 * 3.0), -1.0);
279  CHECK_DOUBLE_VALUE (test_of_builtin_trig (M_PI        ),  0.0);
280
281  /* PR jit/64020:
282     The "sincos" pass merges sin/cos calls into the cexpi builtin.
283     Verify that a dump of the "sincos" pass was provided, and that it
284     shows a call to the cexpi builtin on a SSA name of "theta".  */
285  CHECK_NON_NULL (trig_sincos_dump);
286  CHECK_STRING_CONTAINS (trig_sincos_dump, " = __builtin_cexpi (theta_");
287  free (trig_sincos_dump);
288
289  /* Similarly, verify that the statistics dump was provided, and that
290     it shows the sincos optimization.  */
291  CHECK_NON_NULL (trig_statistics_dump);
292  CHECK_STRING_CONTAINS (
293    trig_statistics_dump,
294    "sincos \"sincos statements inserted\" \"test_of_builtin_trig\" 1");
295  free (trig_statistics_dump);
296}
297
298static void
299verify_use_of_builtins (gcc_jit_context *ctxt, gcc_jit_result *result)
300{
301  verify_test_of_builtin_strcmp (ctxt, result);
302  verify_test_of_builtin_trig (ctxt, result);
303}
304
305/**********************************************************************
306 "void" return
307 **********************************************************************/
308
309static void
310create_use_of_void_return (gcc_jit_context *ctxt)
311{
312  /* Let's try to inject the equivalent of:
313       void
314       test_of_void_return (int *out)
315       {
316         *out = 1;
317	 return;
318       }
319  */
320  gcc_jit_type *void_t =
321    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
322  gcc_jit_type *int_t =
323    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
324  gcc_jit_type *int_ptr_t =
325    gcc_jit_type_get_pointer (int_t);
326
327  /* Build the test_fn.  */
328  gcc_jit_param *param_out =
329    gcc_jit_context_new_param (ctxt, NULL, int_ptr_t, "out");
330  gcc_jit_function *test_fn =
331    gcc_jit_context_new_function (ctxt, NULL,
332                                  GCC_JIT_FUNCTION_EXPORTED,
333                                  void_t,
334                                  "test_of_void_return",
335                                  1, &param_out,
336                                  0);
337  gcc_jit_block *initial =
338    gcc_jit_function_new_block (test_fn, "initial");
339
340  gcc_jit_block_add_assignment (
341    initial, NULL,
342    /* "*out = ..." */
343    gcc_jit_rvalue_dereference (gcc_jit_param_as_rvalue (param_out),
344				NULL),
345    gcc_jit_context_one (ctxt, int_t));
346  gcc_jit_block_end_with_void_return (initial, NULL);
347}
348
349static void
350verify_void_return (gcc_jit_context *ctxt, gcc_jit_result *result)
351{
352  typedef void (*fn_type) (int *);
353  CHECK_NON_NULL (result);
354
355  fn_type test_of_void_return =
356    (fn_type)gcc_jit_result_get_code (result, "test_of_void_return");
357  CHECK_NON_NULL (test_of_void_return);
358
359  int i;
360  test_of_void_return (&i);
361  CHECK_VALUE (i, 1); /* ensure correct value was written back */
362}
363
364/**********************************************************************
365 Code for harness
366 **********************************************************************/
367
368void
369create_code (gcc_jit_context *ctxt, void *user_data)
370{
371  create_tests_of_hidden_functions (ctxt);
372  create_use_of_builtins (ctxt);
373  create_use_of_void_return (ctxt);
374}
375
376
377void
378verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
379{
380  verify_hidden_functions (ctxt, result);
381  verify_use_of_builtins (ctxt, result);
382  verify_void_return (ctxt, result);
383}
384