Committed to dmalcolm/jit branch: This is a work-in-progress, and currently doesn't achieve very deep coverage of the code, due to mismatching types.
gcc/testsuite/ * jit.dg/harness.h (main): Wrap with #ifndef TEST_PROVIDES_MAIN * jit.dg/test-fuzzer.c: New. --- gcc/testsuite/ChangeLog.jit | 5 + gcc/testsuite/jit.dg/harness.h | 3 + gcc/testsuite/jit.dg/test-fuzzer.c | 458 +++++++++++++++++++++++++++++++++++++ 3 files changed, 466 insertions(+) create mode 100644 gcc/testsuite/jit.dg/test-fuzzer.c diff --git a/gcc/testsuite/ChangeLog.jit b/gcc/testsuite/ChangeLog.jit index 0476507..18b6d9a 100644 --- a/gcc/testsuite/ChangeLog.jit +++ b/gcc/testsuite/ChangeLog.jit @@ -1,3 +1,8 @@ +2013-10-24 David Malcolm <dmalc...@redhat.com> + + * jit.dg/harness.h (main): Wrap with #ifndef TEST_PROVIDES_MAIN + * jit.dg/test-fuzzer.c: New. + 2013-10-22 David Malcolm <dmalc...@redhat.com> * jit.dg/harness.h (verify_code): Add context param so that diff --git a/gcc/testsuite/jit.dg/harness.h b/gcc/testsuite/jit.dg/harness.h index 8e75924..353c7c1 100644 --- a/gcc/testsuite/jit.dg/harness.h +++ b/gcc/testsuite/jit.dg/harness.h @@ -162,6 +162,7 @@ extract_progname (const char *argv0) return p; } +#ifndef TEST_PROVIDES_MAIN int main (int argc, char **argv) { @@ -183,4 +184,6 @@ main (int argc, char **argv) return 0; } +#endif /* #ifndef TEST_PROVIDES_MAIN */ + #endif /* #ifndef TEST_COMBINATION */ diff --git a/gcc/testsuite/jit.dg/test-fuzzer.c b/gcc/testsuite/jit.dg/test-fuzzer.c new file mode 100644 index 0000000..d35286b --- /dev/null +++ b/gcc/testsuite/jit.dg/test-fuzzer.c @@ -0,0 +1,458 @@ +/* Fuzz-testing of libgccjit API. + Currently this triggers internal compiler errors, typically due to type + mismatches. */ +#include <limits.h> +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> + +#include "libgccjit.h" + +#define TEST_PROVIDES_MAIN +#include "harness.h" + +typedef struct fuzzer +{ + gcc_jit_context *ctxt; + + unsigned int seed; + + int num_types; + gcc_jit_type **types; + + int num_globals; + gcc_jit_lvalue **globals; + + int num_funcs; + gcc_jit_function **funcs; + +} fuzzer; + +static void +fuzzer_init (fuzzer *f, gcc_jit_context *ctxt, unsigned int seed); + +static int +fuzzer_randrange (fuzzer *f, int min, int max); + +static gcc_jit_location * +get_random_location (fuzzer *f); + +static gcc_jit_type * +get_random_type (fuzzer *f); + +static gcc_jit_type * +make_random_type (fuzzer *f); + +static gcc_jit_lvalue * +make_random_global (fuzzer *f); + +static gcc_jit_function * +make_random_function (fuzzer *f); + +typedef struct function_fuzzer +{ + fuzzer *f; + + int num_params; + gcc_jit_param **params; + + gcc_jit_function *fn; + + int num_locals; + gcc_jit_lvalue **locals; + +} function_fuzzer; + +static void +function_fuzzer_add_stmt (function_fuzzer *ff); + +static gcc_jit_lvalue * +get_random_lvalue (function_fuzzer *ff, int max_depth); + +static gcc_jit_rvalue * +get_random_rvalue (function_fuzzer *ff, int max_depth); + +/* fuzzer defns. */ + +static void +fuzzer_init (fuzzer *f, gcc_jit_context *ctxt, unsigned int seed) +{ + int i; + memset (f, 0, sizeof (*f)); + f->ctxt = ctxt; + f->seed = seed; + + int num_types = fuzzer_randrange (f, 5, 10); + f->types = malloc (num_types * sizeof (gcc_jit_type *)); + + int num_funcs = fuzzer_randrange (f, 3, 5); + f->funcs = malloc (num_funcs * sizeof (gcc_jit_function *)); + + int num_globals = fuzzer_randrange (f, 5, 10); + f->globals = malloc (num_globals * sizeof (gcc_jit_lvalue *)); + + for (i = 0; i < num_types; i++) + { + gcc_jit_type *type = make_random_type (f); + assert (type); + f->types[f->num_types++] = type; + } + + for (i = 0; i < num_globals; i++) + f->globals[f->num_globals++] = make_random_global (f); + + for (i = 0; i < num_funcs; i++) + f->funcs[f->num_funcs++] = make_random_function (f); +} + +/* Get random int in inclusive range [min, max]. */ + +static int fuzzer_randrange (fuzzer *f, int min, int max) +{ + assert (min <= max); + int i = rand_r (&f->seed); + int result = (i % (max + 1 - min)) + min; + assert (result >= min); + assert (result <= max); + return result; +} + +static gcc_jit_location * +get_random_location (fuzzer *f) +{ + const char *filename = NULL; + + if (fuzzer_randrange (f, 0, 1)) + return NULL; + + switch (fuzzer_randrange (f, 1, 2)) + { + case 1: + filename = "foo.c"; + break; + case 2: + filename = "bar.c"; + break; + } + + return gcc_jit_context_new_location (f->ctxt, + filename, + fuzzer_randrange (f, 1, 1000), + fuzzer_randrange (f, 1, 1000)); +} + +const enum gcc_jit_types types[] = { + GCC_JIT_TYPE_VOID, + + GCC_JIT_TYPE_VOID_PTR, + + GCC_JIT_TYPE_CHAR, + GCC_JIT_TYPE_SIGNED_CHAR, + GCC_JIT_TYPE_UNSIGNED_CHAR, + + GCC_JIT_TYPE_SHORT, + GCC_JIT_TYPE_UNSIGNED_SHORT, + + GCC_JIT_TYPE_INT, + GCC_JIT_TYPE_UNSIGNED_INT, + + GCC_JIT_TYPE_LONG, + GCC_JIT_TYPE_UNSIGNED_LONG, + + GCC_JIT_TYPE_LONG_LONG, + GCC_JIT_TYPE_UNSIGNED_LONG_LONG, + + GCC_JIT_TYPE_FLOAT, + GCC_JIT_TYPE_DOUBLE, + GCC_JIT_TYPE_LONG_DOUBLE, + + GCC_JIT_TYPE_CONST_CHAR_PTR, + + GCC_JIT_TYPE_SIZE_T, + + GCC_JIT_TYPE_FILE_PTR +}; +#define NUM_TYPES (sizeof(types)/sizeof(types[0])) + +static gcc_jit_type * +get_random_type (fuzzer *f) +{ + int i = fuzzer_randrange (f, 0, (NUM_TYPES - 1) + f->num_types); + if (i < NUM_TYPES) + return gcc_jit_context_get_type (f->ctxt, types[i]); + assert ((i - NUM_TYPES) < f->num_types); + assert (f->types[i - NUM_TYPES]); + return f->types[i - NUM_TYPES]; +} + +static gcc_jit_type * +make_random_type (fuzzer *f) +{ + switch (fuzzer_randrange (f, 0, 5)) + { + case 0: + return gcc_jit_type_get_pointer (get_random_type (f)); + case 1: + return gcc_jit_type_get_const (get_random_type (f)); + default: + { + /* Create a struct. */ + int num_fields = fuzzer_randrange (f, 0, 10); + gcc_jit_field **fields = \ + malloc (num_fields * sizeof (gcc_jit_field *)); + int i; + for (i = 0; i < num_fields ; i++) + { + char field_name[256]; + sprintf (field_name, "field%i", i); + fields[i] = gcc_jit_context_new_field (f->ctxt, + get_random_location (f), + get_random_type (f), + field_name); + } + char struct_name[256]; + sprintf (struct_name, "s%i", f->num_types); + gcc_jit_type *struct_ = \ + gcc_jit_context_new_struct_type (f->ctxt, + get_random_location (f), + struct_name, + num_fields, + fields); + free (fields); + return struct_; + } + } +} + +static gcc_jit_lvalue * +make_random_global (fuzzer *f) +{ + char global_name[256]; + sprintf (global_name, "g%i", f->num_globals); + return gcc_jit_context_new_global (f->ctxt, + get_random_location (f), + get_random_type (f), + global_name); +} + +static gcc_jit_function * +make_random_function (fuzzer *f) +{ + char func_name[256]; + sprintf (func_name, "fn%i", f->num_funcs); + + function_fuzzer *ff = malloc (sizeof (function_fuzzer)); + memset (ff, 0, sizeof (*ff)); + + ff->f = f; + + ff->num_params = fuzzer_randrange (f, 0, 10); + ff->params = malloc (ff->num_params * sizeof (gcc_jit_param *)); + int i; + for (i = 0; i < ff->num_params; i++) + { + char param_name[256]; + sprintf (param_name, "param%i", i); + ff->params[i] = \ + gcc_jit_context_new_param (f->ctxt, + get_random_location (f), + get_random_type (f), + param_name); + } + + enum gcc_jit_function_kind kind = + ((enum gcc_jit_function_kind) + fuzzer_randrange (f, 0, GCC_JIT_FUNCTION_IMPORTED)); + + ff->fn = \ + gcc_jit_context_new_function ( + f->ctxt, + get_random_location (f), + kind, + get_random_type (f), + func_name, + ff->num_params, + ff->params, + fuzzer_randrange (f, 0, 1)); + + /* Create locals. */ + if (kind != GCC_JIT_FUNCTION_IMPORTED) + { + ff->num_locals = fuzzer_randrange (f, 0, 10); + ff->locals = malloc (ff->num_locals * sizeof (gcc_jit_lvalue *)); + for (i = 0; i < ff->num_locals; i++) + { + char local_name[256]; + sprintf (local_name, "local%i", i); + ff->locals[i] = + gcc_jit_function_new_local (ff->fn, + get_random_location (f), + get_random_type (f), + local_name); + } + } + /* TODO: use locals. */ + + if (kind != GCC_JIT_FUNCTION_IMPORTED) + { + /* TODO: create body */ + int num_stmts = fuzzer_randrange (f, 0, 10); + for (i = 0; i < num_stmts; i++) + function_fuzzer_add_stmt (ff); + } + + gcc_jit_function *result = ff->fn; + + free (ff->params); + free (ff); + + return result; +} + +/* function_fuzzer defns. */ + +static void function_fuzzer_add_stmt (function_fuzzer *ff) +{ + gcc_jit_function_add_eval (ff->fn, + get_random_location (ff->f), + get_random_rvalue (ff, 4)); + gcc_jit_function_add_assignment (ff->fn, + get_random_location (ff->f), + get_random_lvalue (ff, 4), + get_random_rvalue (ff, 4)); + /* TODO: place more kinds of statement */ + /* TODO: labels */ +} + +static gcc_jit_lvalue *get_random_lvalue (function_fuzzer *ff, int max_depth) +{ + int choice = fuzzer_randrange (ff->f, 0, + ff->num_params + + ff->num_locals + + ff->f->num_globals - 1); + if (choice < ff->num_params) + return gcc_jit_param_as_lvalue (ff->params[choice]); + choice -= ff->num_params; + + if (choice < ff->num_locals) + return ff->locals[choice]; + choice -= ff->num_locals; + + assert (choice < ff->f->num_globals); + return ff->f->globals[choice]; +} + +static gcc_jit_rvalue *get_random_rvalue (function_fuzzer *ff, int max_depth) +{ + int use_lvalue = fuzzer_randrange (ff->f, 0, 1); + if (use_lvalue) + return gcc_jit_lvalue_as_rvalue (get_random_lvalue (ff, max_depth)); + + int choice = fuzzer_randrange (ff->f, 0, 1); + + /* Compound op: */ + switch (choice) + { + case 0: + return gcc_jit_context_new_string_literal (ff->f->ctxt, "hello"); + case 1: + return gcc_jit_context_new_rvalue_from_int ( + ff->f->ctxt, + get_random_type (ff->f), + fuzzer_randrange (ff->f, 0, INT_MAX)); + case 2: + return gcc_jit_context_new_rvalue_from_double ( + ff->f->ctxt, + get_random_type (ff->f), + ((double)fuzzer_randrange (ff->f, 0, INT_MAX)) + / (double)fuzzer_randrange (ff->f, 0, INT_MAX)); + case 3: + return gcc_jit_context_new_unary_op ( + ff->f->ctxt, + get_random_location (ff->f), + ((enum gcc_jit_unary_op) + fuzzer_randrange (ff->f, 0, GCC_JIT_UNARY_OP_LOGICAL_NEGATE)), + get_random_type (ff->f), + get_random_rvalue (ff, max_depth - 1)); + case 4: + return gcc_jit_context_new_binary_op ( + ff->f->ctxt, + get_random_location (ff->f), + ((enum gcc_jit_binary_op) + fuzzer_randrange (ff->f, 0, GCC_JIT_BINARY_OP_LOGICAL_OR)), + get_random_type (ff->f), + get_random_rvalue (ff, max_depth - 1), + get_random_rvalue (ff, max_depth - 1)); + case 5: + return gcc_jit_lvalue_get_address ( + get_random_lvalue (ff, max_depth - 1), + get_random_location (ff->f)); + + /* TODO: + - comparisons + - calls + - array lookup + - fields + - dereferencing */ + } + return NULL; +} + + +/* Top-level defns for use by harness. */ +int +code_making_callback (gcc_jit_context *ctxt, void *user_data) +{ + fuzzer f; + int seed = *(int*)user_data; + + fuzzer_init (&f, ctxt, seed); + + return 0; +} + +static int num_completed_compilations = 0; + +void +verify_code (gcc_jit_context *ctxt, gcc_jit_result *result) +{ + /* We can make no guarantees about whether we built something + valid or not, and the result might have an infinite loop, + so we can't execute it. + + If we survive to reach here, note the fact for DejaGnu. */ + pass ("%s: survived compilation", test); + if (result) + num_completed_compilations++; +} + +static void +test_fuzzer (const char *argv0, int seed) +{ + test_jit (argv0, &seed); +} + +int +main (int argc, char **argv) +{ + int i, seed; + const int NUM_ITERATIONS = 2; + const int NUM_SEEDS = 100; + for (i = 1; i <= NUM_ITERATIONS; i++) + { + for (seed = 0; seed < NUM_SEEDS ; seed++) + { + snprintf (test, sizeof (test), + "%s iteration %d of %d; seed %d of %d", + extract_progname (argv[0]), + i, NUM_ITERATIONS, seed, NUM_SEEDS); + test_fuzzer (argv[0], seed); + } + } + pass ("%s: survived running all tests", extract_progname (argv[0])); + printf ("%s: num completed compilations: %d\n", extract_progname (argv[0]), + num_completed_compilations); + totals (); + + return 0; +} -- 1.7.11.7