1271093Sbr/* Fuzz-testing of libgccjit API.
2271093Sbr   Currently this triggers internal compiler errors, typically due to type
3271093Sbr   mismatches.  */
4271093Sbr#include <limits.h>
5271093Sbr#include <stdlib.h>
6271093Sbr#include <stdio.h>
7271093Sbr#include <assert.h>
8271093Sbr
9271093Sbr#include "libgccjit.h"
10271093Sbr
11271093Sbr#define TEST_PROVIDES_MAIN
12271093Sbr#include "harness.h"
13271093Sbr
14271093Sbrtypedef struct fuzzer
15271093Sbr{
16271093Sbr  gcc_jit_context *ctxt;
17271093Sbr
18271186Sbr  unsigned int seed;
19271431Sbr
20272896Sbr  int num_types;
21272120Sbr  gcc_jit_type **types;
22272120Sbr
23273380Sbr  int num_globals;
24272712Sbr  gcc_jit_lvalue **globals;
25273469Sbr
26273469Sbr  int num_funcs;
27273278Sbr  gcc_jit_function **funcs;
28273469Sbr
29275647Sbr} fuzzer;
30275050Sbr
31275647Sbrstatic void
32fuzzer_init (fuzzer *f, gcc_jit_context *ctxt, unsigned int seed);
33
34static int
35fuzzer_randrange (fuzzer *f, int min, int max);
36
37static gcc_jit_location *
38get_random_location (fuzzer *f);
39
40static gcc_jit_type *
41get_random_type (fuzzer *f);
42
43static gcc_jit_type *
44make_random_type (fuzzer *f);
45
46static gcc_jit_lvalue *
47make_random_global (fuzzer *f);
48
49static gcc_jit_function *
50make_random_function (fuzzer *f);
51
52typedef struct function_fuzzer
53{
54  fuzzer *f;
55
56  int num_params;
57  gcc_jit_param **params;
58
59  gcc_jit_function *fn;
60
61  int num_locals;
62  gcc_jit_lvalue **locals;
63
64  gcc_jit_block *block;
65
66} function_fuzzer;
67
68static void
69function_fuzzer_add_stmt (function_fuzzer *ff);
70
71static gcc_jit_lvalue *
72get_random_lvalue (function_fuzzer *ff, int max_depth);
73
74static gcc_jit_rvalue *
75get_random_rvalue (function_fuzzer *ff, int max_depth);
76
77/* fuzzer defns.  */
78
79static void
80fuzzer_init (fuzzer *f, gcc_jit_context *ctxt, unsigned int seed)
81{
82  int i;
83  memset (f, 0, sizeof (*f));
84  f->ctxt = ctxt;
85  f->seed = seed;
86
87  int num_types = fuzzer_randrange (f, 5, 10);
88  f->types = malloc (num_types * sizeof (gcc_jit_type *));
89
90  int num_funcs = fuzzer_randrange (f, 3, 5);
91  f->funcs = malloc (num_funcs * sizeof (gcc_jit_function *));
92
93  int num_globals = fuzzer_randrange (f, 5, 10);
94  f->globals = malloc (num_globals * sizeof (gcc_jit_lvalue *));
95
96  for (i = 0; i < num_types; i++)
97    {
98      gcc_jit_type *type = make_random_type (f);
99      assert (type);
100      f->types[f->num_types++] = type;
101    }
102
103  for (i = 0; i < num_globals; i++)
104    f->globals[f->num_globals++] = make_random_global (f);
105
106  for (i = 0; i < num_funcs; i++)
107    f->funcs[f->num_funcs++] = make_random_function (f);
108
109  /* Now clean out f.  */
110  free (f->types);
111  free (f->funcs);
112  free (f->globals);
113}
114
115/* Get random int in inclusive range [min, max].  */
116
117static int fuzzer_randrange (fuzzer *f, int min, int max)
118{
119  assert (min <= max);
120  int i = rand_r (&f->seed);
121  int result = (i % (max + 1 - min)) + min;
122  assert (result >= min);
123  assert (result <= max);
124  return result;
125}
126
127static gcc_jit_location *
128get_random_location (fuzzer *f)
129{
130  const char *filename = NULL;
131
132  if (fuzzer_randrange (f, 0, 1))
133    return NULL;
134
135  switch (fuzzer_randrange (f, 1, 2))
136    {
137    case 1:
138      filename = "foo.c";
139      break;
140    case 2:
141      filename = "bar.c";
142      break;
143    }
144
145  return gcc_jit_context_new_location (f->ctxt,
146				       filename,
147				       fuzzer_randrange (f, 1, 1000),
148				       fuzzer_randrange (f, 1, 1000));
149}
150
151const enum gcc_jit_types types[] = {
152  GCC_JIT_TYPE_VOID,
153
154  GCC_JIT_TYPE_VOID_PTR,
155
156  GCC_JIT_TYPE_CHAR,
157  GCC_JIT_TYPE_SIGNED_CHAR,
158  GCC_JIT_TYPE_UNSIGNED_CHAR,
159
160  GCC_JIT_TYPE_SHORT,
161  GCC_JIT_TYPE_UNSIGNED_SHORT,
162
163  GCC_JIT_TYPE_INT,
164  GCC_JIT_TYPE_UNSIGNED_INT,
165
166  GCC_JIT_TYPE_LONG,
167  GCC_JIT_TYPE_UNSIGNED_LONG,
168
169  GCC_JIT_TYPE_LONG_LONG,
170  GCC_JIT_TYPE_UNSIGNED_LONG_LONG,
171
172  GCC_JIT_TYPE_FLOAT,
173  GCC_JIT_TYPE_DOUBLE,
174  GCC_JIT_TYPE_LONG_DOUBLE,
175
176  GCC_JIT_TYPE_CONST_CHAR_PTR,
177
178  GCC_JIT_TYPE_SIZE_T,
179
180  GCC_JIT_TYPE_FILE_PTR
181};
182#define NUM_TYPES (sizeof(types)/sizeof(types[0]))
183
184static gcc_jit_type *
185get_random_type (fuzzer *f)
186{
187  int i = fuzzer_randrange (f, 0, (NUM_TYPES - 1) + f->num_types);
188  if (i < NUM_TYPES)
189    return gcc_jit_context_get_type (f->ctxt, types[i]);
190  assert ((i - NUM_TYPES) < f->num_types);
191  assert (f->types[i - NUM_TYPES]);
192  return f->types[i - NUM_TYPES];
193}
194
195static gcc_jit_type *
196make_random_type (fuzzer *f)
197{
198  switch (fuzzer_randrange (f, 0, 5))
199    {
200    case 0:
201      return gcc_jit_type_get_pointer (get_random_type (f));
202    case 1:
203      return gcc_jit_type_get_const (get_random_type (f));
204    default:
205      {
206	/* Create a struct.  */
207	int num_fields = fuzzer_randrange (f, 0, 10);
208	gcc_jit_field **fields = \
209	  malloc (num_fields * sizeof (gcc_jit_field *));
210	int i;
211	for (i = 0; i < num_fields ; i++)
212	  {
213	    char field_name[256];
214	    sprintf (field_name, "field%i", i);
215	    fields[i] = gcc_jit_context_new_field (f->ctxt,
216						   get_random_location (f),
217						   get_random_type (f),
218						   field_name);
219	  }
220	char struct_name[256];
221	sprintf (struct_name, "s%i", f->num_types);
222	gcc_jit_struct *struct_ = \
223	  gcc_jit_context_new_struct_type (f->ctxt,
224					   get_random_location (f),
225					   struct_name,
226					   num_fields,
227					   fields);
228	free (fields);
229	return gcc_jit_struct_as_type (struct_);
230      }
231    }
232}
233
234static gcc_jit_lvalue *
235make_random_global (fuzzer *f)
236{
237  char global_name[256];
238  sprintf (global_name, "g%i", f->num_globals);
239  return gcc_jit_context_new_global (f->ctxt,
240				     get_random_location (f),
241				     GCC_JIT_GLOBAL_EXPORTED,
242				     get_random_type (f),
243				     global_name);
244}
245
246static gcc_jit_function *
247make_random_function (fuzzer *f)
248{
249  char func_name[256];
250  sprintf (func_name, "fn%i", f->num_funcs);
251
252  function_fuzzer *ff = malloc (sizeof (function_fuzzer));
253  memset (ff, 0, sizeof (*ff));
254
255  ff->f = f;
256
257  ff->num_params = fuzzer_randrange (f, 0, 10);
258  ff->params = malloc (ff->num_params * sizeof (gcc_jit_param *));
259  int i;
260  for (i = 0; i < ff->num_params; i++)
261    {
262      char param_name[256];
263      sprintf (param_name, "param%i", i);
264      ff->params[i] = \
265	gcc_jit_context_new_param (f->ctxt,
266				   get_random_location (f),
267				   get_random_type (f),
268				   param_name);
269    }
270
271  enum gcc_jit_function_kind kind =
272    ((enum gcc_jit_function_kind)
273     fuzzer_randrange (f, 0, GCC_JIT_FUNCTION_IMPORTED));
274
275  ff->fn = \
276    gcc_jit_context_new_function (
277      f->ctxt,
278      get_random_location (f),
279      kind,
280      get_random_type (f),
281      func_name,
282      ff->num_params,
283      ff->params,
284      fuzzer_randrange (f, 0, 1));
285  ff->block = gcc_jit_function_new_block (ff->fn, NULL);
286
287  /* Create locals.  */
288  if (kind != GCC_JIT_FUNCTION_IMPORTED)
289    {
290      ff->num_locals = fuzzer_randrange (f, 0, 10);
291      ff->locals = malloc (ff->num_locals * sizeof (gcc_jit_lvalue *));
292      for (i = 0; i < ff->num_locals; i++)
293	{
294	  char local_name[256];
295	  sprintf (local_name, "local%i", i);
296	  ff->locals[i] =
297	    gcc_jit_function_new_local (ff->fn,
298					get_random_location (f),
299					get_random_type (f),
300					local_name);
301	}
302    }
303  /* TODO: use locals.  */
304
305  if (kind != GCC_JIT_FUNCTION_IMPORTED)
306    {
307      /* TODO: create body */
308      int num_stmts = fuzzer_randrange (f, 0, 10);
309      for (i = 0; i < num_stmts; i++)
310	function_fuzzer_add_stmt (ff);
311    }
312
313  gcc_jit_block_end_with_return (ff->block, NULL, get_random_rvalue (ff, 3));
314
315
316  gcc_jit_function *result = ff->fn;
317
318  free (ff->locals);
319  free (ff->params);
320  free (ff);
321
322  return result;
323}
324
325/* function_fuzzer defns.  */
326
327static void function_fuzzer_add_stmt (function_fuzzer *ff)
328{
329  gcc_jit_block_add_eval (ff->block,
330			  get_random_location (ff->f),
331			  get_random_rvalue (ff, 4));
332  gcc_jit_block_add_assignment (ff->block,
333				get_random_location (ff->f),
334				get_random_lvalue (ff, 4),
335				get_random_rvalue (ff, 4));
336  /* TODO: place more kinds of statement */
337  /* TODO: labels  */
338}
339
340static gcc_jit_lvalue *get_random_lvalue (function_fuzzer *ff, int max_depth)
341{
342  int choice = fuzzer_randrange (ff->f, 0,
343				 ff->num_params
344				 + ff->num_locals
345				 + ff->f->num_globals - 1);
346  if (choice < ff->num_params)
347    return gcc_jit_param_as_lvalue (ff->params[choice]);
348  choice -= ff->num_params;
349
350  if (choice < ff->num_locals)
351    return ff->locals[choice];
352  choice -= ff->num_locals;
353
354  assert (choice < ff->f->num_globals);
355  return ff->f->globals[choice];
356}
357
358static gcc_jit_rvalue *get_random_rvalue (function_fuzzer *ff, int max_depth)
359{
360  int use_lvalue = fuzzer_randrange (ff->f, 0, 1);
361  if (use_lvalue)
362    return gcc_jit_lvalue_as_rvalue (get_random_lvalue (ff, max_depth));
363
364  int choice = fuzzer_randrange (ff->f, 0, 1);
365
366  /* Compound op: */
367  switch (choice)
368    {
369    case 0:
370      return gcc_jit_context_new_string_literal (ff->f->ctxt, "hello");
371    case 1:
372      return gcc_jit_context_new_rvalue_from_int (
373	ff->f->ctxt,
374	get_random_type (ff->f),
375	fuzzer_randrange (ff->f, 0, INT_MAX));
376    case 2:
377      return gcc_jit_context_new_rvalue_from_double (
378	ff->f->ctxt,
379	get_random_type (ff->f),
380	((double)fuzzer_randrange (ff->f, 0, INT_MAX))
381	 / (double)fuzzer_randrange (ff->f, 0, INT_MAX));
382    case 3:
383      return gcc_jit_context_new_unary_op (
384	ff->f->ctxt,
385	get_random_location (ff->f),
386	((enum gcc_jit_unary_op)
387	 fuzzer_randrange (ff->f, 0, GCC_JIT_UNARY_OP_LOGICAL_NEGATE)),
388	get_random_type (ff->f),
389	get_random_rvalue (ff, max_depth - 1));
390    case 4:
391      return gcc_jit_context_new_binary_op (
392	ff->f->ctxt,
393	get_random_location (ff->f),
394	((enum gcc_jit_binary_op)
395	 fuzzer_randrange (ff->f, 0, GCC_JIT_BINARY_OP_LOGICAL_OR)),
396	get_random_type (ff->f),
397	get_random_rvalue (ff, max_depth - 1),
398	get_random_rvalue (ff, max_depth - 1));
399    case 5:
400      return gcc_jit_lvalue_get_address (
401	get_random_lvalue (ff, max_depth - 1),
402	get_random_location (ff->f));
403
404      /* TODO:
405	 - comparisons
406	 - calls
407	 - array lookup
408	 - fields
409	 - dereferencing */
410    }
411  return NULL;
412}
413
414
415/* Top-level defns for use by harness.	*/
416void
417create_code (gcc_jit_context *ctxt, void *user_data)
418{
419  fuzzer f;
420  int seed = *(int*)user_data;
421
422  fuzzer_init (&f, ctxt, seed);
423}
424
425static int num_completed_compilations = 0;
426
427void
428verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
429{
430  /* We can make no guarantees about whether we built something
431     valid or not, and the result might have an infinite loop,
432     so we can't execute it.
433
434     If we survive to reach here, note the fact for DejaGnu.  */
435  pass ("%s: survived compilation", test);
436  if (result)
437    num_completed_compilations++;
438}
439
440static void
441test_fuzzer (const char *argv0, int seed)
442{
443  test_jit (argv0, &seed);
444}
445
446int
447main (int argc, char **argv)
448{
449  int i, seed;
450  const int NUM_ITERATIONS = 2;
451  const int NUM_SEEDS = 100;
452  for (i = 1; i <= NUM_ITERATIONS; i++)
453    {
454      for (seed = 0; seed < NUM_SEEDS ; seed++)
455	{
456	  snprintf (test, sizeof (test),
457		    "%s iteration %d of %d; seed %d of %d",
458		    extract_progname (argv[0]),
459		    i, NUM_ITERATIONS, seed, NUM_SEEDS);
460	  test_fuzzer (argv[0], seed);
461	}
462    }
463  pass ("%s: survived running all tests", extract_progname (argv[0]));
464  note ("%s: num completed compilations: %d", extract_progname (argv[0]),
465	num_completed_compilations);
466  totals ();
467
468  return 0;
469}
470