From d9b950dd440fe2029a111cda56add2c9e91123b9 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Mon, 6 Jun 2016 17:11:30 +0000 Subject: [PATCH] Selftest framework gcc/ChangeLog: * Makefile.in (OBJS): Add function-tests.o, hash-map-tests.o, hash-set-tests.o, rtl-tests.o, selftest-run-tests.o. (OBJS-libcommon): Add selftest.o. (OBJS-libcommon-target): Add selftest.o. (all.internal): Add "selftest". (all.cross): Likewise. (selftest): New phony target. (s-selftest): New target. (selftest-gdb): New phony target. (COLLECT2_OBJS): Add selftest.o. * bitmap.c: Include "selftest.h". (selftest::test_gc_alloc): New function. (selftest::test_set_range): New function. (selftest::test_clear_bit_in_middle): New function. (selftest::test_copying): New function. (selftest::test_bitmap_single_bit_set_p): New function. (selftest::bitmap_c_tests): New function. * common.opt (fself-test): New. * diagnostic-show-locus.c: Include "selftest.h". (make_range): New function. (test_range_contains_point_for_single_point): New function. (test_range_contains_point_for_single_line): New function. (test_range_contains_point_for_multiple_lines): New function. (assert_eq): New function. (test_get_line_width_without_trailing_whitespace): New function. (selftest::diagnostic_show_locus_c_tests): New function. * et-forest.c: Include "selftest.h". (selftest::test_single_node): New function. (selftest::test_simple_tree): New function. (selftest::test_disconnected_nodes): New function. (selftest::et_forest_c_tests): New function. * fold-const.c: Include "selftest.h". (selftest::assert_binop_folds_to_const): New function. (selftest::assert_binop_folds_to_nonlvalue): New function. (selftest::test_arithmetic_folding): New function. (selftest::fold_const_c_tests): New function. * function-tests.c: New file. * gimple.c: Include "selftest.h". Include "gimple-pretty-print.h". (selftest::verify_gimple_pp): New function. (selftest::test_assign_single): New function. (selftest::test_assign_binop): New function. (selftest::test_nop_stmt): New function. (selftest::test_return_stmt): New function. (selftest::test_return_without_value): New function. (selftest::gimple_c_tests): New function. * hash-map-tests.c: New file. * hash-set-tests.c: New file. * input.c: Include "selftest.h". (selftest::assert_loceq): New function. (selftest::test_accessing_ordinary_linemaps): New function. (selftest::test_unknown_location): New function. (selftest::test_builtins): New function. (selftest::test_reading_source_line): New function. (selftest::input_c_tests): New function. * rtl-tests.c: New file. * selftest-run-tests.c: New file. * selftest.c: New file. * selftest.h: New file. * spellcheck.c: Include "selftest.h". (selftest::levenshtein_distance_unit_test_oneway): New function, adapted from testsuite/gcc.dg/plugin/levenshtein_plugin.c. (selftest::levenshtein_distance_unit_test): Likewise. (selftest::spellcheck_c_tests): Likewise. * toplev.c: Include selftest.h. (toplev::run_self_tests): New. (toplev::main): Handle -fself-test. * toplev.h (toplev::run_self_tests): New. * tree.c: Include "selftest.h". (selftest::test_integer_constants): New function. (selftest::test_identifiers): New function. (selftest::test_labels): New function. (selftest::tree_c_tests): New function. * tree-cfg.c: Include "selftest.h". (selftest::push_fndecl): New function. (selftest::test_linear_chain): New function. (selftest::test_diamond): New function. (selftest::test_fully_connected): New function. (selftest::tree_cfg_c_tests): New function. * vec.c: Include "selftest.h". (selftest::safe_push_range): New function. (selftest::test_quick_push): New function. (selftest::test_safe_push): New function. (selftest::test_truncate): New function. (selftest::test_safe_grow_cleared): New function. (selftest::test_pop): New function. (selftest::test_safe_insert): New function. (selftest::test_ordered_remove): New function. (selftest::test_unordered_remove): New function. (selftest::test_block_remove): New function. (selftest::reverse_cmp): New function. (selftest::test_qsort): New function. (selftest::vec_c_tests): New function.c. * wide-int.cc: Include selftest.h and wide-int-print.h. (selftest::from_int ): New function. (selftest::from_int ): New function. (selftest::from_int ): New function. (selftest::assert_deceq): New function. (selftest::assert_hexeq): New function. (selftest::test_printing ): New function template. (selftest::test_ops ): New function template. (selftest::test_comparisons ): New function template. (selftest::run_all_wide_int_tests ): New function template. (selftest::wide_int_cc_tests): New function. gcc/testsuite/ChangeLog: * gcc.dg/plugin/levenshtein-test-1.c: Delete. * gcc.dg/plugin/levenshtein_plugin.c: Delete. * gcc.dg/plugin/plugin.exp (plugin_test_list): Remove the above. From-SVN: r237144 --- gcc/ChangeLog | 109 +++ gcc/Makefile.in | 31 +- gcc/bitmap.c | 113 +++ gcc/common.opt | 4 + gcc/diagnostic-show-locus.c | 166 +++++ gcc/et-forest.c | 118 ++++ gcc/fold-const.c | 80 +++ gcc/function-tests.c | 658 ++++++++++++++++++ gcc/gimple.c | 137 ++++ gcc/hash-map-tests.c | 93 +++ gcc/hash-set-tests.c | 69 ++ gcc/input.c | 116 +++ gcc/rtl-tests.c | 117 ++++ gcc/selftest-run-tests.c | 77 ++ gcc/selftest.c | 47 ++ gcc/selftest.h | 153 ++++ gcc/spellcheck.c | 58 ++ gcc/testsuite/ChangeLog | 7 + .../gcc.dg/plugin/levenshtein-test-1.c | 9 - .../gcc.dg/plugin/levenshtein_plugin.c | 64 -- gcc/testsuite/gcc.dg/plugin/plugin.exp | 1 - gcc/toplev.c | 26 + gcc/toplev.h | 2 + gcc/tree-cfg.c | 278 ++++++++ gcc/tree.c | 62 ++ gcc/vec.c | 192 +++++ gcc/wide-int.cc | 170 +++++ 27 files changed, 2878 insertions(+), 79 deletions(-) create mode 100644 gcc/function-tests.c create mode 100644 gcc/hash-map-tests.c create mode 100644 gcc/hash-set-tests.c create mode 100644 gcc/rtl-tests.c create mode 100644 gcc/selftest-run-tests.c create mode 100644 gcc/selftest.c create mode 100644 gcc/selftest.h delete mode 100644 gcc/testsuite/gcc.dg/plugin/levenshtein-test-1.c delete mode 100644 gcc/testsuite/gcc.dg/plugin/levenshtein_plugin.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 3e6879849f2..68a7b78f4b6 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,112 @@ +2016-06-06 David Malcolm + + * Makefile.in (OBJS): Add function-tests.o, + hash-map-tests.o, hash-set-tests.o, rtl-tests.o, + selftest-run-tests.o. + (OBJS-libcommon): Add selftest.o. + (OBJS-libcommon-target): Add selftest.o. + (all.internal): Add "selftest". + (all.cross): Likewise. + (selftest): New phony target. + (s-selftest): New target. + (selftest-gdb): New phony target. + (COLLECT2_OBJS): Add selftest.o. + * bitmap.c: Include "selftest.h". + (selftest::test_gc_alloc): New function. + (selftest::test_set_range): New function. + (selftest::test_clear_bit_in_middle): New function. + (selftest::test_copying): New function. + (selftest::test_bitmap_single_bit_set_p): New function. + (selftest::bitmap_c_tests): New function. + * common.opt (fself-test): New. + * diagnostic-show-locus.c: Include "selftest.h". + (make_range): New function. + (test_range_contains_point_for_single_point): New function. + (test_range_contains_point_for_single_line): New function. + (test_range_contains_point_for_multiple_lines): New function. + (assert_eq): New function. + (test_get_line_width_without_trailing_whitespace): New function. + (selftest::diagnostic_show_locus_c_tests): New function. + * et-forest.c: Include "selftest.h". + (selftest::test_single_node): New function. + (selftest::test_simple_tree): New function. + (selftest::test_disconnected_nodes): New function. + (selftest::et_forest_c_tests): New function. + * fold-const.c: Include "selftest.h". + (selftest::assert_binop_folds_to_const): New function. + (selftest::assert_binop_folds_to_nonlvalue): New function. + (selftest::test_arithmetic_folding): New function. + (selftest::fold_const_c_tests): New function. + * function-tests.c: New file. + * gimple.c: Include "selftest.h". + Include "gimple-pretty-print.h". + (selftest::verify_gimple_pp): New function. + (selftest::test_assign_single): New function. + (selftest::test_assign_binop): New function. + (selftest::test_nop_stmt): New function. + (selftest::test_return_stmt): New function. + (selftest::test_return_without_value): New function. + (selftest::gimple_c_tests): New function. + * hash-map-tests.c: New file. + * hash-set-tests.c: New file. + * input.c: Include "selftest.h". + (selftest::assert_loceq): New function. + (selftest::test_accessing_ordinary_linemaps): New function. + (selftest::test_unknown_location): New function. + (selftest::test_builtins): New function. + (selftest::test_reading_source_line): New function. + (selftest::input_c_tests): New function. + * rtl-tests.c: New file. + * selftest-run-tests.c: New file. + * selftest.c: New file. + * selftest.h: New file. + * spellcheck.c: Include "selftest.h". + (selftest::levenshtein_distance_unit_test_oneway): New function, + adapted from testsuite/gcc.dg/plugin/levenshtein_plugin.c. + (selftest::levenshtein_distance_unit_test): Likewise. + (selftest::spellcheck_c_tests): Likewise. + * toplev.c: Include selftest.h. + (toplev::run_self_tests): New. + (toplev::main): Handle -fself-test. + * toplev.h (toplev::run_self_tests): New. + * tree.c: Include "selftest.h". + (selftest::test_integer_constants): New function. + (selftest::test_identifiers): New function. + (selftest::test_labels): New function. + (selftest::tree_c_tests): New function. + * tree-cfg.c: Include "selftest.h". + (selftest::push_fndecl): New function. + (selftest::test_linear_chain): New function. + (selftest::test_diamond): New function. + (selftest::test_fully_connected): New function. + (selftest::tree_cfg_c_tests): New function. + * vec.c: Include "selftest.h". + (selftest::safe_push_range): New function. + (selftest::test_quick_push): New function. + (selftest::test_safe_push): New function. + (selftest::test_truncate): New function. + (selftest::test_safe_grow_cleared): New function. + (selftest::test_pop): New function. + (selftest::test_safe_insert): New function. + (selftest::test_ordered_remove): New function. + (selftest::test_unordered_remove): New function. + (selftest::test_block_remove): New function. + (selftest::reverse_cmp): New function. + (selftest::test_qsort): New function. + (selftest::vec_c_tests): New function.c. + * wide-int.cc: Include selftest.h and wide-int-print.h. + (selftest::from_int ): New function. + (selftest::from_int ): New function. + (selftest::from_int ): New function. + (selftest::assert_deceq): New function. + (selftest::assert_hexeq): New function. + (selftest::test_printing ): New function template. + (selftest::test_ops ): New function template. + (selftest::test_comparisons ): New function template. + (selftest::run_all_wide_int_tests ): New function + template. + (selftest::wide_int_cc_tests): New function. + 2016-06-06 Kyrylo Tkachov PR middle-end/37780 diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 2d6f1e8d02f..fdcc42aa309 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1264,6 +1264,7 @@ OBJS = \ fold-const.o \ fold-const-call.o \ function.o \ + function-tests.o \ fwprop.o \ gcc-rich-location.o \ gcse.o \ @@ -1299,6 +1300,8 @@ OBJS = \ graphite-sese-to-poly.o \ gtype-desc.o \ haifa-sched.o \ + hash-map-tests.o \ + hash-set-tests.o \ hsa.o \ hsa-gen.o \ hsa-regalloc.o \ @@ -1399,6 +1402,7 @@ OBJS = \ resource.o \ rtl-chkp.o \ rtl-error.o \ + rtl-tests.o \ rtl.o \ rtlhash.o \ rtlanal.o \ @@ -1411,6 +1415,7 @@ OBJS = \ sel-sched-ir.o \ sel-sched-dump.o \ sel-sched.o \ + selftest-run-tests.o \ sese.o \ shrink-wrap.o \ simplify-rtx.o \ @@ -1543,13 +1548,14 @@ OBJS = \ # no target dependencies. OBJS-libcommon = diagnostic.o diagnostic-color.o diagnostic-show-locus.o \ pretty-print.o intl.o \ - vec.o input.o version.o hash-table.o ggc-none.o memory-block.o + vec.o input.o version.o hash-table.o ggc-none.o memory-block.o \ + selftest.o # Objects in libcommon-target.a, used by drivers and by the core # compiler and containing target-dependent code. OBJS-libcommon-target = $(common_out_object_file) prefix.o params.o \ opts.o opts-common.o options.o vec.o hooks.o common/common-targhooks.o \ - hash-table.o file-find.o spellcheck.o + hash-table.o file-find.o spellcheck.o selftest.o # This lists all host objects for the front ends. ALL_HOST_FRONTEND_OBJS = $(foreach v,$(CONFIG_LANGUAGES),$($(v)_OBJS)) @@ -1816,10 +1822,10 @@ config.status: $(srcdir)/configure $(srcdir)/config.gcc quickstrap: all cd $(toplevel_builddir) && $(MAKE) all-target-libgcc -all.internal: start.encap rest.encap doc +all.internal: start.encap rest.encap doc selftest # This is what to compile if making a cross-compiler. all.cross: native gcc-cross$(exeext) cpp$(exeext) specs \ - libgcc-support lang.all.cross doc @GENINSRC@ srcextra + libgcc-support lang.all.cross doc selftest @GENINSRC@ srcextra # This is what must be made before installing GCC and converting libraries. start.encap: native xgcc$(exeext) cpp$(exeext) specs \ libgcc-support lang.start.encap @GENINSRC@ srcextra @@ -1839,6 +1845,21 @@ endif # This does the things that can't be done on the host machine. rest.cross: specs +# Run the selftests during the build once we have a driver and a cc1, +# so that self-test failures are caught as early as possible. +# Use "s-selftest" to ensure that we only run the selftests if the +# driver or cc1 change. +.PHONY: selftest +selftest: s-selftest +s-selftest: $(GCC_PASSES) cc1$(exeext) stmp-int-hdrs + $(GCC_FOR_TARGET) -xc -S -c /dev/null -fself-test + $(STAMP) $@ + +# Convenience method for running selftests under gdb: +.PHONY: selftest-gdb +selftest-gdb: $(GCC_PASSES) cc1$(exeext) stmp-int-hdrs + $(GCC_FOR_TARGET) -xc -S -c /dev/null -fself-test -wrapper gdb,--args + # Recompile all the language-independent object files. # This is used only if the user explicitly asks for it. compilations: $(BACKEND) @@ -1986,7 +2007,7 @@ gcc-nm.c: gcc-ar.c cp $^ $@ COLLECT2_OBJS = collect2.o collect2-aix.o tlink.o vec.o ggc-none.o \ - collect-utils.o file-find.o hash-table.o + collect-utils.o file-find.o hash-table.o selftest.o COLLECT2_LIBS = @COLLECT2_LIBS@ collect2$(exeext): $(COLLECT2_OBJS) $(LIBDEPS) # Don't try modifying collect2 (aka ld) in place--it might be linking this. diff --git a/gcc/bitmap.c b/gcc/bitmap.c index 010cf752d76..6206535a8ea 100644 --- a/gcc/bitmap.c +++ b/gcc/bitmap.c @@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see #include "system.h" #include "coretypes.h" #include "bitmap.h" +#include "selftest.h" /* Memory allocation statistics purpose instance. */ mem_alloc_description bitmap_mem_desc; @@ -2162,5 +2163,117 @@ debug (const bitmap_head *ptr) fprintf (stderr, "\n"); } +#if CHECKING_P + +namespace selftest { + +/* Selftests for bitmaps. */ + +/* Freshly-created bitmaps ought to be empty. */ + +static void +test_gc_alloc () +{ + bitmap b = bitmap_gc_alloc (); + ASSERT_TRUE (bitmap_empty_p (b)); +} + +/* Verify bitmap_set_range. */ + +static void +test_set_range () +{ + bitmap b = bitmap_gc_alloc (); + ASSERT_TRUE (bitmap_empty_p (b)); + + bitmap_set_range (b, 7, 5); + ASSERT_FALSE (bitmap_empty_p (b)); + ASSERT_EQ (5, bitmap_count_bits (b)); + + /* Verify bitmap_bit_p at the boundaries. */ + ASSERT_FALSE (bitmap_bit_p (b, 6)); + ASSERT_TRUE (bitmap_bit_p (b, 7)); + ASSERT_TRUE (bitmap_bit_p (b, 11)); + ASSERT_FALSE (bitmap_bit_p (b, 12)); +} + +/* Verify splitting a range into two pieces using bitmap_clear_bit. */ + +static void +test_clear_bit_in_middle () +{ + bitmap b = bitmap_gc_alloc (); + + /* Set b to [100..200]. */ + bitmap_set_range (b, 100, 100); + ASSERT_EQ (100, bitmap_count_bits (b)); + + /* Clear a bit in the middle. */ + bool changed = bitmap_clear_bit (b, 150); + ASSERT_TRUE (changed); + ASSERT_EQ (99, bitmap_count_bits (b)); + ASSERT_TRUE (bitmap_bit_p (b, 149)); + ASSERT_FALSE (bitmap_bit_p (b, 150)); + ASSERT_TRUE (bitmap_bit_p (b, 151)); +} + +/* Verify bitmap_copy. */ + +static void +test_copying () +{ + bitmap src = bitmap_gc_alloc (); + bitmap_set_range (src, 40, 10); + + bitmap dst = bitmap_gc_alloc (); + ASSERT_FALSE (bitmap_equal_p (src, dst)); + bitmap_copy (dst, src); + ASSERT_TRUE (bitmap_equal_p (src, dst)); + + /* Verify that we can make them unequal again... */ + bitmap_set_range (src, 70, 5); + ASSERT_FALSE (bitmap_equal_p (src, dst)); + + /* ...and that changing src after the copy didn't affect + the other: */ + ASSERT_FALSE (bitmap_bit_p (dst, 70)); +} + +/* Verify bitmap_single_bit_set_p. */ + +static void +test_bitmap_single_bit_set_p () +{ + bitmap b = bitmap_gc_alloc (); + + ASSERT_FALSE (bitmap_single_bit_set_p (b)); + + bitmap_set_range (b, 42, 1); + ASSERT_TRUE (bitmap_single_bit_set_p (b)); + ASSERT_EQ (42, bitmap_first_set_bit (b)); + + bitmap_set_range (b, 1066, 1); + ASSERT_FALSE (bitmap_single_bit_set_p (b)); + ASSERT_EQ (42, bitmap_first_set_bit (b)); + + bitmap_clear_range (b, 0, 100); + ASSERT_TRUE (bitmap_single_bit_set_p (b)); + ASSERT_EQ (1066, bitmap_first_set_bit (b)); +} + +/* Run all of the selftests within this file. */ + +void +bitmap_c_tests () +{ + test_gc_alloc (); + test_set_range (); + test_clear_bit_in_middle (); + test_copying (); + test_bitmap_single_bit_set_p (); +} + +} // namespace selftest +#endif /* CHECKING_P */ #include "gt-bitmap.h" diff --git a/gcc/common.opt b/gcc/common.opt index 2bb576c51c4..632dd311fba 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -2066,6 +2066,10 @@ fselective-scheduling2 Common Report Var(flag_selective_scheduling2) Optimization Run selective scheduling after reload. +fself-test +Common Undocumented Var(flag_self_test) +Run self-tests. + fsel-sched-pipelining Common Report Var(flag_sel_sched_pipelining) Init(0) Optimization Perform software pipelining of inner loops during selective scheduling. diff --git a/gcc/diagnostic-show-locus.c b/gcc/diagnostic-show-locus.c index eeccee51716..7aab658b697 100644 --- a/gcc/diagnostic-show-locus.c +++ b/gcc/diagnostic-show-locus.c @@ -27,6 +27,7 @@ along with GCC; see the file COPYING3. If not see #include "backtrace.h" #include "diagnostic.h" #include "diagnostic-color.h" +#include "selftest.h" #ifdef HAVE_TERMIOS_H # include @@ -442,6 +443,123 @@ layout_range::contains_point (int row, int column) const return column <= m_finish.m_column; } +#if CHECKING_P + +/* A helper function for testing layout_range::contains_point. */ + +static layout_range +make_range (int start_line, int start_col, int end_line, int end_col) +{ + const expanded_location start_exploc + = {"test.c", start_line, start_col, NULL, false}; + const expanded_location finish_exploc + = {"test.c", end_line, end_col, NULL, false}; + return layout_range (&start_exploc, &finish_exploc, false, + &start_exploc); +} + +/* Selftests for layout_range::contains_point. */ + +/* Selftest for layout_range::contains_point where the layout_range + is a range with start==end i.e. a single point. */ + +static void +test_range_contains_point_for_single_point () +{ + layout_range point = make_range (7, 10, 7, 10); + + /* Before the line. */ + ASSERT_FALSE (point.contains_point (6, 1)); + + /* On the line, but before start. */ + ASSERT_FALSE (point.contains_point (7, 9)); + + /* At the point. */ + ASSERT_TRUE (point.contains_point (7, 10)); + + /* On the line, after the point. */ + ASSERT_FALSE (point.contains_point (7, 11)); + + /* After the line. */ + ASSERT_FALSE (point.contains_point (8, 1)); +} + +/* Selftest for layout_range::contains_point where the layout_range + is the single-line range shown as "Example A" above. */ + +static void +test_range_contains_point_for_single_line () +{ + layout_range example_a = make_range (2, 22, 2, 38); + + /* Before the line. */ + ASSERT_FALSE (example_a.contains_point (1, 1)); + + /* On the line, but before start. */ + ASSERT_FALSE (example_a.contains_point (2, 21)); + + /* On the line, at the start. */ + ASSERT_TRUE (example_a.contains_point (2, 22)); + + /* On the line, within the range. */ + ASSERT_TRUE (example_a.contains_point (2, 23)); + + /* On the line, at the end. */ + ASSERT_TRUE (example_a.contains_point (2, 38)); + + /* On the line, after the end. */ + ASSERT_FALSE (example_a.contains_point (2, 39)); + + /* After the line. */ + ASSERT_FALSE (example_a.contains_point (2, 39)); +} + +/* Selftest for layout_range::contains_point where the layout_range + is the multi-line range shown as "Example B" above. */ + +static void +test_range_contains_point_for_multiple_lines () +{ + layout_range example_b = make_range (3, 14, 5, 8); + + /* Before first line. */ + ASSERT_FALSE (example_b.contains_point (1, 1)); + + /* On the first line, but before start. */ + ASSERT_FALSE (example_b.contains_point (3, 13)); + + /* At the start. */ + ASSERT_TRUE (example_b.contains_point (3, 14)); + + /* On the first line, within the range. */ + ASSERT_TRUE (example_b.contains_point (3, 15)); + + /* On an interior line. + The column number should not matter; try various boundary + values. */ + ASSERT_TRUE (example_b.contains_point (4, 1)); + ASSERT_TRUE (example_b.contains_point (4, 7)); + ASSERT_TRUE (example_b.contains_point (4, 8)); + ASSERT_TRUE (example_b.contains_point (4, 9)); + ASSERT_TRUE (example_b.contains_point (4, 13)); + ASSERT_TRUE (example_b.contains_point (4, 14)); + ASSERT_TRUE (example_b.contains_point (4, 15)); + + /* On the final line, before the end. */ + ASSERT_TRUE (example_b.contains_point (5, 7)); + + /* On the final line, at the end. */ + ASSERT_TRUE (example_b.contains_point (5, 8)); + + /* On the final line, after the end. */ + ASSERT_FALSE (example_b.contains_point (5, 9)); + + /* After the line. */ + ASSERT_FALSE (example_b.contains_point (6, 1)); +} + +#endif /* #if CHECKING_P */ + /* Given a source line LINE of length LINE_WIDTH, determine the width without any trailing whitespace. */ @@ -465,6 +583,34 @@ get_line_width_without_trailing_whitespace (const char *line, int line_width) return result; } +#if CHECKING_P + +/* A helper function for testing get_line_width_without_trailing_whitespace. */ + +static void +assert_eq (const char *line, int expected_width) +{ + int actual_value + = get_line_width_without_trailing_whitespace (line, strlen (line)); + ASSERT_EQ (actual_value, expected_width); +} + +/* Verify that get_line_width_without_trailing_whitespace is sane for + various inputs. It is not required to handle newlines. */ + +static void +test_get_line_width_without_trailing_whitespace () +{ + assert_eq ("", 0); + assert_eq (" ", 0); + assert_eq ("\t", 0); + assert_eq ("hello world", 11); + assert_eq ("hello world ", 11); + assert_eq ("hello world \t\t ", 11); +} + +#endif /* #if CHECKING_P */ + /* Helper function for layout's ctor, for sanitizing locations relative to the primary location within a diagnostic. @@ -1171,3 +1317,23 @@ diagnostic_show_locus (diagnostic_context * context, pp_set_prefix (context->printer, saved_prefix); } + +#if CHECKING_P + +namespace selftest { + +/* Run all of the selftests within this file. */ + +void +diagnostic_show_locus_c_tests () +{ + test_range_contains_point_for_single_point (); + test_range_contains_point_for_single_line (); + test_range_contains_point_for_multiple_lines (); + + test_get_line_width_without_trailing_whitespace (); +} + +} // namespace selftest + +#endif /* #if CHECKING_P */ diff --git a/gcc/et-forest.c b/gcc/et-forest.c index cd36752a277..766a1a3f35b 100644 --- a/gcc/et-forest.c +++ b/gcc/et-forest.c @@ -27,6 +27,7 @@ License along with libiberty; see the file COPYING3. If not see #include "coretypes.h" #include "alloc-pool.h" #include "et-forest.h" +#include "selftest.h" /* We do not enable this with CHECKING_P, since it is awfully slow. */ #undef DEBUG_ET @@ -764,3 +765,120 @@ et_root (struct et_node *node) return r->of; } + +#if CHECKING_P + +namespace selftest { + +/* Selftests for et-forest.c. */ + +/* Perform sanity checks for a tree consisting of a single node. */ + +static void +test_single_node () +{ + void *test_data = (void *)0xcafebabe; + + et_node *n = et_new_tree (test_data); + ASSERT_EQ (n->data, test_data); + ASSERT_EQ (n, et_root (n)); + et_free_tree (n); +} + +/* Test of this tree: + a + / \ + / \ + b c + / \ | + d e f. */ + +static void +test_simple_tree () +{ + et_node *a = et_new_tree (NULL); + et_node *b = et_new_tree (NULL); + et_node *c = et_new_tree (NULL); + et_node *d = et_new_tree (NULL); + et_node *e = et_new_tree (NULL); + et_node *f = et_new_tree (NULL); + + et_set_father (b, a); + et_set_father (c, a); + et_set_father (d, b); + et_set_father (e, b); + et_set_father (f, c); + + ASSERT_TRUE (et_below (a, a)); + ASSERT_TRUE (et_below (b, a)); + ASSERT_TRUE (et_below (c, a)); + ASSERT_TRUE (et_below (d, a)); + ASSERT_TRUE (et_below (e, a)); + ASSERT_TRUE (et_below (f, a)); + + ASSERT_FALSE (et_below (a, b)); + ASSERT_TRUE (et_below (b, b)); + ASSERT_FALSE (et_below (c, b)); + ASSERT_TRUE (et_below (d, b)); + ASSERT_TRUE (et_below (e, b)); + ASSERT_FALSE (et_below (f, b)); + + ASSERT_FALSE (et_below (a, c)); + ASSERT_FALSE (et_below (b, c)); + ASSERT_TRUE (et_below (c, c)); + ASSERT_FALSE (et_below (d, c)); + ASSERT_FALSE (et_below (e, c)); + ASSERT_TRUE (et_below (f, c)); + + ASSERT_FALSE (et_below (a, d)); + ASSERT_FALSE (et_below (b, d)); + ASSERT_FALSE (et_below (c, d)); + ASSERT_TRUE (et_below (d, d)); + ASSERT_FALSE (et_below (e, d)); + ASSERT_FALSE (et_below (f, d)); + + ASSERT_FALSE (et_below (a, e)); + ASSERT_FALSE (et_below (b, e)); + ASSERT_FALSE (et_below (c, e)); + ASSERT_FALSE (et_below (d, e)); + ASSERT_TRUE (et_below (e, e)); + ASSERT_FALSE (et_below (f, e)); + + ASSERT_FALSE (et_below (a, f)); + ASSERT_FALSE (et_below (b, f)); + ASSERT_FALSE (et_below (c, f)); + ASSERT_FALSE (et_below (d, f)); + ASSERT_FALSE (et_below (e, f)); + ASSERT_TRUE (et_below (f, f)); + + et_free_tree_force (a); +} + +/* Verify that two disconnected nodes are unrelated. */ + +static void +test_disconnected_nodes () +{ + et_node *a = et_new_tree (NULL); + et_node *b = et_new_tree (NULL); + + ASSERT_FALSE (et_below (a, b)); + ASSERT_FALSE (et_below (b, a)); + + et_free_tree (a); + et_free_tree (b); +} + +/* Run all of the selftests within this file. */ + +void +et_forest_c_tests () +{ + test_single_node (); + test_simple_tree (); + test_disconnected_nodes (); +} + +} // namespace selftest + +#endif /* CHECKING_P */ diff --git a/gcc/fold-const.c b/gcc/fold-const.c index 5058746c4eb..0efa9d5b0b1 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -76,6 +76,7 @@ along with GCC; see the file COPYING3. If not see #include "case-cfn-macros.h" #include "stringpool.h" #include "tree-ssanames.h" +#include "selftest.h" #ifndef LOAD_EXTEND_OP #define LOAD_EXTEND_OP(M) UNKNOWN @@ -14496,3 +14497,82 @@ c_getstr (tree src) return TREE_STRING_POINTER (src) + tree_to_uhwi (offset_node); } + +#if CHECKING_P + +namespace selftest { + +/* Helper functions for writing tests of folding trees. */ + +/* Verify that the binary op (LHS CODE RHS) folds to CONSTANT. */ + +static void +assert_binop_folds_to_const (tree lhs, enum tree_code code, tree rhs, + tree constant) +{ + ASSERT_EQ (constant, fold_build2 (code, TREE_TYPE (lhs), lhs, rhs)); +} + +/* Verify that the binary op (LHS CODE RHS) folds to an NON_LVALUE_EXPR + wrapping WRAPPED_EXPR. */ + +static void +assert_binop_folds_to_nonlvalue (tree lhs, enum tree_code code, tree rhs, + tree wrapped_expr) +{ + tree result = fold_build2 (code, TREE_TYPE (lhs), lhs, rhs); + ASSERT_NE (wrapped_expr, result); + ASSERT_EQ (NON_LVALUE_EXPR, TREE_CODE (result)); + ASSERT_EQ (wrapped_expr, TREE_OPERAND (result, 0)); +} + +/* Verify that various arithmetic binary operations are folded + correctly. */ + +static void +test_arithmetic_folding () +{ + tree type = integer_type_node; + tree x = create_tmp_var_raw (type, "x"); + tree zero = build_zero_cst (type); + tree one = build_int_cst (type, 1); + + /* Addition. */ + /* 1 <-- (0 + 1) */ + assert_binop_folds_to_const (zero, PLUS_EXPR, one, + one); + assert_binop_folds_to_const (one, PLUS_EXPR, zero, + one); + + /* (nonlvalue)x <-- (x + 0) */ + assert_binop_folds_to_nonlvalue (x, PLUS_EXPR, zero, + x); + + /* Subtraction. */ + /* 0 <-- (x - x) */ + assert_binop_folds_to_const (x, MINUS_EXPR, x, + zero); + assert_binop_folds_to_nonlvalue (x, MINUS_EXPR, zero, + x); + + /* Multiplication. */ + /* 0 <-- (x * 0) */ + assert_binop_folds_to_const (x, MULT_EXPR, zero, + zero); + + /* (nonlvalue)x <-- (x * 1) */ + assert_binop_folds_to_nonlvalue (x, MULT_EXPR, one, + x); +} + +/* Run all of the selftests within this file. */ + +void +fold_const_c_tests () +{ + test_arithmetic_folding (); +} + +} // namespace selftest + +#endif /* CHECKING_P */ diff --git a/gcc/function-tests.c b/gcc/function-tests.c new file mode 100644 index 00000000000..c8188e769fa --- /dev/null +++ b/gcc/function-tests.c @@ -0,0 +1,658 @@ +/* Unit tests for function-handling. + Copyright (C) 2015-2016 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "opts.h" +#include "signop.h" +#include "hash-set.h" +#include "fixed-value.h" +#include "alias.h" +#include "flags.h" +#include "symtab.h" +#include "tree-core.h" +#include "stor-layout.h" +#include "tree.h" +#include "stringpool.h" +#include "stor-layout.h" +#include "rtl.h" +#include "predict.h" +#include "vec.h" +#include "hashtab.h" +#include "hash-set.h" +#include "machmode.h" +#include "hard-reg-set.h" +#include "input.h" +#include "function.h" +#include "dominance.h" +#include "cfg.h" +#include "cfganal.h" +#include "basic-block.h" +#include "tree-ssa-alias.h" +#include "internal-fn.h" +#include "gimple-fold.h" +#include "gimple-expr.h" +#include "toplev.h" +#include "print-tree.h" +#include "tree-iterator.h" +#include "gimplify.h" +#include "tree-cfg.h" +#include "basic-block.h" +#include "double-int.h" +#include "alias.h" +#include "symtab.h" +#include "wide-int.h" +#include "inchash.h" +#include "tree.h" +#include "fold-const.h" +#include "stor-layout.h" +#include "stmt.h" +#include "hash-table.h" +#include "tree-ssa-alias.h" +#include "internal-fn.h" +#include "gimple-expr.h" +#include "is-a.h" +#include "gimple.h" +#include "tree-pass.h" +#include "context.h" +#include "hash-map.h" +#include "plugin-api.h" +#include "ipa-ref.h" +#include "cgraph.h" +#include "selftest.h" + +#if CHECKING_P + +namespace selftest { + +/* Helper function for selftests of function-creation. */ + +static tree +make_fndecl (tree return_type, + const char *name, + vec ¶m_types, + bool is_variadic = false) +{ + tree fn_type; + if (is_variadic) + fn_type = build_varargs_function_type_array (return_type, + param_types.length (), + param_types.address ()); + else + fn_type = build_function_type_array (return_type, + param_types.length (), + param_types.address ()); + /* FIXME: this uses input_location: */ + tree fndecl = build_fn_decl (name, fn_type); + + return fndecl; +} + +/* Verify creating a function declaration equivalent to the following + int test_fndecl_int_void (void); + C declaration. */ + +static void +test_fndecl_int_void () +{ + auto_vec param_types; + const char *name = "test_fndecl_int_void"; + tree fndecl = make_fndecl (integer_type_node, + name, + param_types); + ASSERT_TRUE (fndecl != NULL); + + /* Verify name of decl. */ + tree declname = DECL_NAME (fndecl); + ASSERT_TRUE (declname != NULL); + ASSERT_EQ (IDENTIFIER_NODE, TREE_CODE (declname)); + /* We expect it to use a *copy* of the string we passed in. */ + const char *identifier_ptr = IDENTIFIER_POINTER (declname); + ASSERT_NE (name, identifier_ptr); + ASSERT_EQ (0, strcmp ("test_fndecl_int_void", identifier_ptr)); + + /* Verify type of fndecl. */ + ASSERT_EQ (FUNCTION_DECL, TREE_CODE (fndecl)); + tree fntype = TREE_TYPE (fndecl); + ASSERT_EQ (FUNCTION_TYPE, TREE_CODE (fntype)); + + /* Verify return type. */ + ASSERT_EQ (integer_type_node, TREE_TYPE (fntype)); + + /* Verify "void" args. */ + tree argtypes = TYPE_ARG_TYPES (fntype); + ASSERT_EQ (TREE_LIST, TREE_CODE (argtypes)); + ASSERT_EQ (void_type_node, TREE_VALUE (argtypes)); + ASSERT_EQ (NULL, TREE_CHAIN (argtypes)); +} + +/* Verify creating a function declaration equivalent to the following + float test_fndecl_float_intchar (int, char); + C declaration. */ + +static void +test_fndecl_float_intchar () +{ + auto_vec param_types; + param_types.safe_push (integer_type_node); + param_types.safe_push (char_type_node); + const char *name = "test_fndecl_float_intchar"; + tree fndecl = make_fndecl (float_type_node, + name, + param_types); + ASSERT_TRUE (fndecl != NULL); + + /* Verify name of decl. */ + tree declname = DECL_NAME (fndecl); + ASSERT_TRUE (declname != NULL); + ASSERT_EQ (IDENTIFIER_NODE, TREE_CODE (declname)); + /* We expect it to use a *copy* of the string we passed in. */ + const char *identifier_ptr = IDENTIFIER_POINTER (declname); + ASSERT_NE (name, identifier_ptr); + ASSERT_EQ (0, strcmp (name, identifier_ptr)); + + /* Verify type of fndecl. */ + ASSERT_EQ (FUNCTION_DECL, TREE_CODE (fndecl)); + tree fntype = TREE_TYPE (fndecl); + ASSERT_EQ (FUNCTION_TYPE, TREE_CODE (fntype)); + + /* Verify return type. */ + ASSERT_EQ (float_type_node, TREE_TYPE (fntype)); + + /* Verify "(int, char)" args. */ + tree arg0 = TYPE_ARG_TYPES (fntype); + ASSERT_EQ (TREE_LIST, TREE_CODE (arg0)); + ASSERT_EQ (integer_type_node, TREE_VALUE (arg0)); + tree arg1 = TREE_CHAIN (arg0); + ASSERT_TRUE (arg1 != NULL); + ASSERT_EQ (TREE_LIST, TREE_CODE (arg1)); + ASSERT_EQ (char_type_node, TREE_VALUE (arg1)); + tree argterm = TREE_CHAIN (arg1); + ASSERT_TRUE (argterm != NULL); + ASSERT_EQ (TREE_LIST, TREE_CODE (argterm)); + ASSERT_EQ (void_type_node, TREE_VALUE (argterm)); + ASSERT_EQ (NULL, TREE_CHAIN (argterm)); +} + +/* The test cases using these helper functions take a trivial function: + + int test_fn (void) { return 42; } + + and test various conversions done to it: + + - gimplification + - construction of the CFG + - conversion to SSA form + - expansion to RTL form + + In avoid having one overlong test case, this is broken + up into separate test cases for each stage, with helper functions + to minimize code duplication. + + Another approach would be to attempt to directly construct a function + in the appropriate representation at each stage, though presumably + that would exhibit different kinds of failure compared to this + approach. */ + +/* Construct this function: + int test_fn (void) { return 42; } + in generic tree form. Return the fndecl. */ + +static tree +build_trivial_generic_function () +{ + auto_vec param_types; + tree fndecl = make_fndecl (integer_type_node, + "test_fn", + param_types); + ASSERT_TRUE (fndecl != NULL); + + /* Populate the function. */ + tree retval = build_decl (UNKNOWN_LOCATION, RESULT_DECL, + NULL_TREE, integer_type_node); + DECL_ARTIFICIAL (retval) = 1; + DECL_IGNORED_P (retval) = 1; + DECL_RESULT (fndecl) = retval; + + /* Create a BIND_EXPR, and within it, a statement list. */ + tree stmt_list = alloc_stmt_list (); + tree_stmt_iterator stmt_iter = tsi_start (stmt_list); + tree block = make_node (BLOCK); + tree bind_expr + = build3 (BIND_EXPR, void_type_node, NULL, stmt_list, block); + + tree modify_retval = build2 (MODIFY_EXPR, + integer_type_node, + retval, + build_int_cst (integer_type_node, 42)); + tree return_stmt = build1 (RETURN_EXPR, + integer_type_node, + modify_retval); + tsi_link_after (&stmt_iter, return_stmt, TSI_CONTINUE_LINKING); + + DECL_INITIAL (fndecl) = block; + + /* how to add to function? the following appears to be how to + set the body of a fndecl: */ + DECL_SAVED_TREE(fndecl) = bind_expr; + + /* Ensure that locals appear in the debuginfo. */ + BLOCK_VARS (block) = BIND_EXPR_VARS (bind_expr); + + return fndecl; +} + +/* Construct this function: + int test_fn (void) { return 42; } + in "high gimple" form. Return the fndecl. */ + +static tree +build_trivial_high_gimple_function () +{ + /* Construct a trivial function, and gimplify it: */ + tree fndecl = build_trivial_generic_function (); + gimplify_function_tree (fndecl); + return fndecl; +} + +/* Build a CFG for a function in gimple form. */ + +static void +build_cfg (tree fndecl) +{ + function *fun = DECL_STRUCT_FUNCTION (fndecl); + ASSERT_TRUE (fun != NULL); + ASSERT_EQ (fndecl, fun->decl); + + /* We first have to lower control flow; for our trivial test function + this gives us: + test_fn () + { + D.56 = 42; + goto ; + : + return D.56; + } + */ + gimple_opt_pass *lower_cf_pass = make_pass_lower_cf (g); + push_cfun (fun); + lower_cf_pass->execute (fun); + pop_cfun (); + + /* We can now convert to CFG form; for our trivial test function this + gives us: + test_fn () + { + : + D.56 = 42; + return D.56; + } + */ + gimple_opt_pass *build_cfg_pass = make_pass_build_cfg (g); + push_cfun (fun); + build_cfg_pass->execute (fun); + pop_cfun (); +} + +/* Convert a gimple+CFG function to SSA form. */ + +static void +convert_to_ssa (tree fndecl) +{ + function *fun = DECL_STRUCT_FUNCTION (fndecl); + ASSERT_TRUE (fun != NULL); + ASSERT_EQ (fndecl, fun->decl); + + gimple_opt_pass *build_ssa_pass = make_pass_build_ssa (g); + push_cfun (fun); + build_ssa_pass->execute (fun); + pop_cfun (); +} + +/* Assuming we have a simple 3-block CFG like this: + [ENTRY] -> [block2] -> [EXIT] + get the "real" basic block (block 2). */ + +static basic_block +get_real_block (function *fun) +{ + ASSERT_TRUE (fun->cfg != NULL); + ASSERT_EQ (3, n_basic_blocks_for_fn (fun)); + basic_block bb2 = (*fun->cfg->x_basic_block_info)[2]; + ASSERT_TRUE (bb2 != NULL); + return bb2; +} + +/* Verify that we have a simple 3-block CFG: the two "fake" ones, and + a "real" one: + [ENTRY] -> [block2] -> [EXIT]. */ + +static void +verify_three_block_cfg (function *fun) +{ + ASSERT_TRUE (fun->cfg != NULL); + ASSERT_EQ (3, n_basic_blocks_for_fn (fun)); + ASSERT_EQ (2, n_edges_for_fn (fun)); + + /* The "fake" basic blocks. */ + basic_block entry = ENTRY_BLOCK_PTR_FOR_FN (fun); + ASSERT_TRUE (entry != NULL); + ASSERT_EQ (ENTRY_BLOCK, entry->index); + + basic_block exit = EXIT_BLOCK_PTR_FOR_FN (fun); + ASSERT_TRUE (exit != NULL); + ASSERT_EQ (EXIT_BLOCK, exit->index); + + /* The "real" basic block. */ + basic_block bb2 = get_real_block (fun); + ASSERT_TRUE (bb2 != NULL); + ASSERT_EQ (2, bb2->index); + + /* Verify connectivity. */ + ASSERT_EQ (NULL, entry->preds); + ASSERT_EQ (1, entry->succs->length ()); + + edge from_entry_to_bb2 = (*entry->succs)[0]; + ASSERT_EQ (entry, from_entry_to_bb2->src); + ASSERT_EQ (bb2, from_entry_to_bb2->dest); + + ASSERT_EQ (1, bb2->preds->length ()); + ASSERT_EQ (from_entry_to_bb2, (*bb2->preds)[0]); + ASSERT_EQ (1, bb2->succs->length ()); + + edge from_bb2_to_exit = (*bb2->succs)[0]; + ASSERT_EQ (bb2, from_bb2_to_exit->src); + ASSERT_EQ (exit, from_bb2_to_exit->dest); + + ASSERT_EQ (1, exit->preds->length ()); + ASSERT_EQ (from_bb2_to_exit, (*exit->preds)[0]); + ASSERT_EQ (NULL, exit->succs); +} + +/* As above, but additionally verify the gimple statements are sane. */ + +static void +verify_three_block_gimple_cfg (function *fun) +{ + verify_three_block_cfg (fun); + + /* The "fake" basic blocks should be flagged as gimple, but with have no + statements. */ + basic_block entry = ENTRY_BLOCK_PTR_FOR_FN (fun); + ASSERT_TRUE (entry != NULL); + ASSERT_EQ (0, entry->flags & BB_RTL); + ASSERT_EQ (NULL, bb_seq (entry)); + + basic_block exit = EXIT_BLOCK_PTR_FOR_FN (fun); + ASSERT_TRUE (exit != NULL); + ASSERT_EQ (0, entry->flags & BB_RTL); + ASSERT_EQ (NULL, bb_seq (exit)); + + /* The "real" basic block should be flagged as gimple, and have one + or more statements. */ + basic_block bb2 = get_real_block (fun); + ASSERT_TRUE (bb2 != NULL); + ASSERT_EQ (0, entry->flags & BB_RTL); + ASSERT_TRUE (bb_seq (bb2) != NULL); +} + +/* As above, but additionally verify the RTL insns are sane. */ + +static void +verify_three_block_rtl_cfg (function *fun) +{ + verify_three_block_cfg (fun); + + /* The "fake" basic blocks should be flagged as RTL, but with no + insns. */ + basic_block entry = ENTRY_BLOCK_PTR_FOR_FN (fun); + ASSERT_TRUE (entry != NULL); + ASSERT_EQ (BB_RTL, entry->flags & BB_RTL); + ASSERT_EQ (NULL, BB_HEAD (entry)); + + basic_block exit = EXIT_BLOCK_PTR_FOR_FN (fun); + ASSERT_TRUE (exit != NULL); + ASSERT_EQ (BB_RTL, entry->flags & BB_RTL); + ASSERT_EQ (NULL, BB_HEAD (exit)); + + /* The "real" basic block should be flagged as RTL, and have one + or more insns. */ + basic_block bb2 = get_real_block (fun); + ASSERT_TRUE (bb2 != NULL); + ASSERT_EQ (BB_RTL, entry->flags & BB_RTL); + ASSERT_TRUE (BB_HEAD (bb2) != NULL); +} + +/* Test converting our trivial function: + int test_fn (void) { return 42; } + to gimple form. */ + +static void +test_gimplification () +{ + tree fndecl = build_trivial_generic_function (); + + /* Convert to gimple: */ + gimplify_function_tree (fndecl); + + /* Verify that we got gimple out of it. */ + + /* The function is now in GIMPLE form but the CFG has not been + built yet. */ + + /* We should have a struct function for the decl. */ + function *fun = DECL_STRUCT_FUNCTION (fndecl); + ASSERT_TRUE (fun != NULL); + ASSERT_EQ (fndecl, fun->decl); + + /* We expect a GIMPLE_BIND, with two gimple statements within it: + tmp = 42; + return tmp; */ + + gimple_seq seq_fn_body = gimple_body (fndecl); + ASSERT_TRUE (seq_fn_body != NULL); + gimple *bind_stmt = gimple_seq_first_stmt (seq_fn_body); + ASSERT_EQ (GIMPLE_BIND, gimple_code (bind_stmt)); + ASSERT_EQ (NULL, bind_stmt->next); + + gimple_seq seq_bind_body = gimple_bind_body (as_a (bind_stmt)); + + /* Verify that we have the 2 statements we expect. */ + ASSERT_TRUE (seq_bind_body != NULL); + gimple *stmt1 = gimple_seq_first_stmt (seq_bind_body); + ASSERT_TRUE (stmt1 != NULL); + ASSERT_EQ (GIMPLE_ASSIGN, gimple_code (stmt1)); + gimple *stmt2 = stmt1->next; + ASSERT_TRUE (stmt2 != NULL); + ASSERT_EQ (stmt1, stmt2->prev); + ASSERT_EQ (GIMPLE_RETURN, gimple_code (stmt2)); +} + +/* Test of building a CFG for a function in high gimple form. */ + +static void +test_building_cfg () +{ + /* Construct a trivial function, and gimplify it: */ + tree fndecl = build_trivial_high_gimple_function (); + function *fun = DECL_STRUCT_FUNCTION (fndecl); + ASSERT_TRUE (fun != NULL); + + /* Build a CFG. */ + build_cfg (fndecl); + + /* The CFG-building code constructs a 4-block cfg (with + ENTRY and EXIT): + test_fn () + { + : + D.65 = 42; + + : + return D.65; + } + and then ought to merge blocks 2 and 3 in cleanup_tree_cfg. + + Hence we should end up with a simple 3-block cfg, the two "fake" ones, + and a "real" one: + [ENTRY] -> [block2] -> [EXIT] + with code like this: + test_fn () + { + : + D.56 = 42; + return D.56; + } + */ + verify_three_block_gimple_cfg (fun); + + /* Verify the statements within the "real" block. */ + basic_block bb2 = get_real_block (fun); + gimple *stmt_a = gimple_seq_first_stmt (bb_seq (bb2)); + ASSERT_EQ (GIMPLE_ASSIGN, gimple_code (stmt_a)); + gimple *stmt_b = stmt_a->next; + ASSERT_EQ (GIMPLE_RETURN, gimple_code (stmt_b)); + ASSERT_EQ (NULL, stmt_b->next); +} + +/* Test of conversion of gimple to SSA form. */ + +static void +test_conversion_to_ssa () +{ + /* As above, construct a trivial function, gimplify it, and build a CFG: */ + tree fndecl = build_trivial_high_gimple_function (); + function *fun = DECL_STRUCT_FUNCTION (fndecl); + ASSERT_TRUE (fun != NULL); + build_cfg (fndecl); + + convert_to_ssa (fndecl); + + verify_three_block_gimple_cfg (fun); + + /* For out trivial test function we should now have something like + this: + test_fn () + { + : + _1 = 42; + return _1; + } + */ + basic_block bb2 = get_real_block (fun); + gimple *stmt_a = gimple_seq_first_stmt (bb_seq (bb2)); + ASSERT_EQ (GIMPLE_ASSIGN, gimple_code (stmt_a)); + + gimple *stmt_b = stmt_a->next; + ASSERT_EQ (GIMPLE_RETURN, gimple_code (stmt_b)); + ASSERT_EQ (NULL, stmt_b->next); + + greturn *return_stmt = as_a (stmt_b); + ASSERT_EQ (SSA_NAME, TREE_CODE (gimple_return_retval (return_stmt))); +} + +/* Test of expansion from gimple-ssa to RTL. */ + +static void +test_expansion_to_rtl () +{ + /* As above, construct a trivial function, gimplify it, build a CFG, + and convert to SSA: */ + tree fndecl = build_trivial_high_gimple_function (); + function *fun = DECL_STRUCT_FUNCTION (fndecl); + ASSERT_TRUE (fun != NULL); + build_cfg (fndecl); + convert_to_ssa (fndecl); + + /* We need a cgraph_node for it. */ + cgraph_node::get_create (fndecl); + /* Normally, cgraph_node::expand () would call + init_function_start (and a bunch of other stuff), + and invoke the expand pass, but it also runs + all of the other passes. So just do the minimum + needed to get from gimple-SSA to RTL. */ + rtl_opt_pass *expand_pass = make_pass_expand (g); + push_cfun (fun); + init_function_start (fndecl); + expand_pass->execute (fun); + pop_cfun (); + + /* On x86_64, I get this: + (note 3 1 2 2 [bb 2] NOTE_INSN_BASIC_BLOCK) + (note 2 3 5 2 NOTE_INSN_FUNCTION_BEG) + (insn 5 2 6 2 (set (reg:SI 87 [ D.59 ]) + (const_int 42 [0x2a])) -1 (nil)) + (insn 6 5 10 2 (set (reg:SI 88 [ ]) + (reg:SI 87 [ D.59 ])) -1 (nil)) + (insn 10 6 11 2 (set (reg/i:SI 0 ax) + (reg:SI 88 [ ])) -1 (nil)) + (insn 11 10 0 2 (use (reg/i:SI 0 ax)) -1 (nil)) + + On cr16-elf I get this: + (note 4 1 2 2 [bb 2] NOTE_INSN_BASIC_BLOCK) + (insn 2 4 3 2 (set (reg:SI 24) + (reg/f:SI 16 virtual-incoming-args)) -1 + (nil)) + (note 3 2 6 2 NOTE_INSN_FUNCTION_BEG) + (insn 6 3 7 2 (set (reg:HI 22 [ _1 ]) + (const_int 42 [0x2a])) -1 + (nil)) + (insn 7 6 11 2 (set (reg:HI 23 [ ]) + (reg:HI 22 [ _1 ])) -1 + (nil)) + (insn 11 7 12 2 (set (reg/i:HI 0 r0) + (reg:HI 23 [ ])) -1 + (nil)) + (insn 12 11 0 2 (use (reg/i:HI 0 r0)) -1 + (nil)). */ + verify_three_block_rtl_cfg (fun); + + /* Verify as much of the RTL as we can whilst avoiding + target-specific behavior. */ + basic_block bb2 = get_real_block (fun); + + /* Expect a NOTE_INSN_BASIC_BLOCK... */ + rtx_insn *insn = BB_HEAD (bb2); + ASSERT_TRUE (insn != NULL); + ASSERT_EQ (NOTE, insn->code); + ASSERT_EQ (NOTE_INSN_BASIC_BLOCK, NOTE_KIND (insn)); + ASSERT_EQ (bb2, NOTE_BASIC_BLOCK (insn)); + + /* ...etc; any further checks are likely to over-specify things + and run us into target dependencies. */ +} + +/* Run all of the selftests within this file. */ + +void +function_tests_c_tests () +{ + test_fndecl_int_void (); + test_fndecl_float_intchar (); + test_gimplification (); + test_building_cfg (); + test_conversion_to_ssa (); + test_expansion_to_rtl (); +} + +} // namespace selftest + +#endif /* #if CHECKING_P */ diff --git a/gcc/gimple.c b/gcc/gimple.c index 75a1ed8ee0b..178c1d3a517 100644 --- a/gcc/gimple.c +++ b/gcc/gimple.c @@ -38,6 +38,8 @@ along with GCC; see the file COPYING3. If not see #include "gimple-walk.h" #include "gimplify.h" #include "target.h" +#include "selftest.h" +#include "gimple-pretty-print.h" /* All the tuples have their operand vector (if present) at the very bottom @@ -3022,3 +3024,138 @@ maybe_remove_unused_call_args (struct function *fn, gimple *stmt) update_stmt_fn (fn, stmt); } } + +#if CHECKING_P + +namespace selftest { + +/* Selftests for core gimple structures. */ + +/* Verify that STMT is pretty-printed as EXPECTED. + Helper function for selftests. */ + +static void +verify_gimple_pp (const char *expected, gimple *stmt) +{ + pretty_printer pp; + pp_gimple_stmt_1 (&pp, stmt, 0 /* spc */, 0 /* flags */); + ASSERT_STREQ (expected, pp_formatted_text (&pp)); +} + +/* Build a GIMPLE_ASSIGN equivalent to + tmp = 5; + and verify various properties of it. */ + +static void +test_assign_single () +{ + tree type = integer_type_node; + tree lhs = build_decl (UNKNOWN_LOCATION, VAR_DECL, + get_identifier ("tmp"), + type); + tree rhs = build_int_cst (type, 5); + gassign *stmt = gimple_build_assign (lhs, rhs); + verify_gimple_pp ("tmp = 5;", stmt); + + ASSERT_TRUE (is_gimple_assign (stmt)); + ASSERT_EQ (lhs, gimple_assign_lhs (stmt)); + ASSERT_EQ (lhs, gimple_get_lhs (stmt)); + ASSERT_EQ (rhs, gimple_assign_rhs1 (stmt)); + ASSERT_EQ (NULL, gimple_assign_rhs2 (stmt)); + ASSERT_EQ (NULL, gimple_assign_rhs3 (stmt)); + ASSERT_TRUE (gimple_assign_single_p (stmt)); + ASSERT_EQ (INTEGER_CST, gimple_assign_rhs_code (stmt)); +} + +/* Build a GIMPLE_ASSIGN equivalent to + tmp = a * b; + and verify various properties of it. */ + +static void +test_assign_binop () +{ + tree type = integer_type_node; + tree lhs = build_decl (UNKNOWN_LOCATION, VAR_DECL, + get_identifier ("tmp"), + type); + tree a = build_decl (UNKNOWN_LOCATION, VAR_DECL, + get_identifier ("a"), + type); + tree b = build_decl (UNKNOWN_LOCATION, VAR_DECL, + get_identifier ("b"), + type); + gassign *stmt = gimple_build_assign (lhs, MULT_EXPR, a, b); + verify_gimple_pp ("tmp = a * b;", stmt); + + ASSERT_TRUE (is_gimple_assign (stmt)); + ASSERT_EQ (lhs, gimple_assign_lhs (stmt)); + ASSERT_EQ (lhs, gimple_get_lhs (stmt)); + ASSERT_EQ (a, gimple_assign_rhs1 (stmt)); + ASSERT_EQ (b, gimple_assign_rhs2 (stmt)); + ASSERT_EQ (NULL, gimple_assign_rhs3 (stmt)); + ASSERT_FALSE (gimple_assign_single_p (stmt)); + ASSERT_EQ (MULT_EXPR, gimple_assign_rhs_code (stmt)); +} + +/* Build a GIMPLE_NOP and verify various properties of it. */ + +static void +test_nop_stmt () +{ + gimple *stmt = gimple_build_nop (); + verify_gimple_pp ("GIMPLE_NOP", stmt); + ASSERT_EQ (GIMPLE_NOP, gimple_code (stmt)); + ASSERT_EQ (NULL, gimple_get_lhs (stmt)); + ASSERT_FALSE (gimple_assign_single_p (stmt)); +} + +/* Build a GIMPLE_RETURN equivalent to + return 7; + and verify various properties of it. */ + +static void +test_return_stmt () +{ + tree type = integer_type_node; + tree val = build_int_cst (type, 7); + greturn *stmt = gimple_build_return (val); + verify_gimple_pp ("return 7;", stmt); + + ASSERT_EQ (GIMPLE_RETURN, gimple_code (stmt)); + ASSERT_EQ (NULL, gimple_get_lhs (stmt)); + ASSERT_EQ (val, gimple_return_retval (stmt)); + ASSERT_FALSE (gimple_assign_single_p (stmt)); +} + +/* Build a GIMPLE_RETURN equivalent to + return; + and verify various properties of it. */ + +static void +test_return_without_value () +{ + greturn *stmt = gimple_build_return (NULL); + verify_gimple_pp ("return;", stmt); + + ASSERT_EQ (GIMPLE_RETURN, gimple_code (stmt)); + ASSERT_EQ (NULL, gimple_get_lhs (stmt)); + ASSERT_EQ (NULL, gimple_return_retval (stmt)); + ASSERT_FALSE (gimple_assign_single_p (stmt)); +} + +/* Run all of the selftests within this file. */ + +void +gimple_c_tests () +{ + test_assign_single (); + test_assign_binop (); + test_nop_stmt (); + test_return_stmt (); + test_return_without_value (); +} + +} // namespace selftest + + +#endif /* CHECKING_P */ diff --git a/gcc/hash-map-tests.c b/gcc/hash-map-tests.c new file mode 100644 index 00000000000..fd1bb952fcd --- /dev/null +++ b/gcc/hash-map-tests.c @@ -0,0 +1,93 @@ +/* Unit tests for hash-map.h. + Copyright (C) 2015-2016 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "opts.h" +#include "signop.h" +#include "hash-set.h" +#include "fixed-value.h" +#include "alias.h" +#include "flags.h" +#include "symtab.h" +#include "tree-core.h" +#include "stor-layout.h" +#include "tree.h" +#include "stringpool.h" +#include "selftest.h" + +#if CHECKING_P + +namespace selftest { + +/* Construct a hash_map and verify that + various operations work correctly. */ + +static void +test_map_of_strings_to_int () +{ + hash_map m; + + const char *ostrich = "ostrich"; + const char *elephant = "elephant"; + const char *ant = "ant"; + const char *spider = "spider"; + const char *millipede = "Illacme plenipes"; + const char *eric = "half a bee"; + + /* A fresh hash_map should be empty. */ + ASSERT_EQ (0, m.elements ()); + ASSERT_EQ (NULL, m.get (ostrich)); + + /* Populate the hash_map. */ + ASSERT_EQ (false, m.put (ostrich, 2)); + ASSERT_EQ (false, m.put (elephant, 4)); + ASSERT_EQ (false, m.put (ant, 6)); + ASSERT_EQ (false, m.put (spider, 8)); + ASSERT_EQ (false, m.put (millipede, 750)); + ASSERT_EQ (false, m.put (eric, 3)); + + /* Verify that we can recover the stored values. */ + ASSERT_EQ (6, m.elements ()); + ASSERT_EQ (2, *m.get (ostrich)); + ASSERT_EQ (4, *m.get (elephant)); + ASSERT_EQ (6, *m.get (ant)); + ASSERT_EQ (8, *m.get (spider)); + ASSERT_EQ (750, *m.get (millipede)); + ASSERT_EQ (3, *m.get (eric)); + + /* Verify removing an item. */ + m.remove (eric); + ASSERT_EQ (5, m.elements ()); + ASSERT_EQ (NULL, m.get (eric)); +} + +/* Run all of the selftests within this file. */ + +void +hash_map_tests_c_tests () +{ + test_map_of_strings_to_int (); +} + +} // namespace selftest + +#endif /* CHECKING_P */ diff --git a/gcc/hash-set-tests.c b/gcc/hash-set-tests.c new file mode 100644 index 00000000000..db408f293e2 --- /dev/null +++ b/gcc/hash-set-tests.c @@ -0,0 +1,69 @@ +/* Unit tests for hash-set.h. + Copyright (C) 2015-2016 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "opts.h" +#include "signop.h" +#include "hash-set.h" +#include "selftest.h" + +#if CHECKING_P + +namespace selftest { + +/* Construct a hash_set and verify that various operations + work correctly. */ + +static void +test_set_of_strings () +{ + hash_set s; + ASSERT_EQ (0, s.elements ()); + + const char *red = "red"; + const char *green = "green"; + const char *blue = "blue"; + + ASSERT_EQ (false, s.contains (red)); + + /* Populate the hash_set. */ + ASSERT_EQ (false, s.add (red)); + ASSERT_EQ (false, s.add (green)); + ASSERT_EQ (false, s.add (blue)); + + /* Verify that the values are now within the set. */ + ASSERT_EQ (true, s.contains (red)); + ASSERT_EQ (true, s.contains (green)); + ASSERT_EQ (true, s.contains (blue)); +} + +/* Run all of the selftests within this file. */ + +void +hash_set_tests_c_tests () +{ + test_set_of_strings (); +} + +} // namespace selftest + +#endif /* #if CHECKING_P */ diff --git a/gcc/input.c b/gcc/input.c index 61b1e4477fc..0b340a8ed9d 100644 --- a/gcc/input.c +++ b/gcc/input.c @@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see #include "coretypes.h" #include "intl.h" #include "diagnostic-core.h" +#include "selftest.h" /* This is a cache used by get_next_line to store the content of a file to be searched for file lines. */ @@ -1136,3 +1137,118 @@ dump_location_info (FILE *stream) dump_labelled_location_range (stream, "AD-HOC LOCATIONS", MAX_SOURCE_LOCATION + 1, UINT_MAX); } + +#if CHECKING_P + +namespace selftest { + +/* Selftests of location handling. */ + +/* Verify the result of LOCATION_FILE/LOCATION_LINE/LOCATION_COLUMN + on LOC. */ + +static void +assert_loceq (const char *exp_filename, int exp_linenum, int exp_colnum, + location_t loc) +{ + ASSERT_STREQ (exp_filename, LOCATION_FILE (loc)); + ASSERT_EQ (exp_linenum, LOCATION_LINE (loc)); + ASSERT_EQ (exp_colnum, LOCATION_COLUMN (loc)); +} + +/* Verify basic operation of ordinary linemaps. */ + +static void +test_accessing_ordinary_linemaps () +{ + /* Build a simple linemap describing some locations. */ + linemap_add (line_table, LC_ENTER, false, "foo.c", 0); + + linemap_line_start (line_table, 1, 100); + location_t loc_a = linemap_position_for_column (line_table, 1); + location_t loc_b = linemap_position_for_column (line_table, 23); + + linemap_line_start (line_table, 2, 100); + location_t loc_c = linemap_position_for_column (line_table, 1); + location_t loc_d = linemap_position_for_column (line_table, 17); + + /* Example of a very long line. */ + linemap_line_start (line_table, 3, 2000); + location_t loc_e = linemap_position_for_column (line_table, 700); + + linemap_add (line_table, LC_LEAVE, false, NULL, 0); + + /* Multiple files. */ + linemap_add (line_table, LC_ENTER, false, "bar.c", 0); + linemap_line_start (line_table, 1, 200); + location_t loc_f = linemap_position_for_column (line_table, 150); + linemap_add (line_table, LC_LEAVE, false, NULL, 0); + + /* Verify that we can recover the location info. */ + assert_loceq ("foo.c", 1, 1, loc_a); + assert_loceq ("foo.c", 1, 23, loc_b); + assert_loceq ("foo.c", 2, 1, loc_c); + assert_loceq ("foo.c", 2, 17, loc_d); + assert_loceq ("foo.c", 3, 700, loc_e); + assert_loceq ("bar.c", 1, 150, loc_f); + + ASSERT_FALSE (is_location_from_builtin_token (loc_a)); +} + +/* Verify various properties of UNKNOWN_LOCATION. */ + +static void +test_unknown_location () +{ + ASSERT_EQ (NULL, LOCATION_FILE (UNKNOWN_LOCATION)); + ASSERT_EQ (0, LOCATION_LINE (UNKNOWN_LOCATION)); + ASSERT_EQ (0, LOCATION_COLUMN (UNKNOWN_LOCATION)); +} + +/* Verify various properties of BUILTINS_LOCATION. */ + +static void +test_builtins () +{ + assert_loceq ("", 0, 0, BUILTINS_LOCATION); + ASSERT_PRED1 (is_location_from_builtin_token, BUILTINS_LOCATION); +} + +/* Verify reading of input files (e.g. for caret-based diagnostics). */ + +static void +test_reading_source_line () +{ + /* We will read *this* source file, using __FILE__. + Here is some specific text to read and test for: + The quick brown fox jumps over the lazy dog. */ + const int linenum_after_test_message = __LINE__; + const int linenum = linenum_after_test_message - 1; + + int line_size; + const char *source_line = location_get_source_line (__FILE__, linenum, &line_size); + ASSERT_TRUE (source_line != NULL); + ASSERT_EQ (53, line_size); + if (!strncmp (" The quick brown fox jumps over the lazy dog. */", + source_line, line_size)) + ::selftest::pass (__FILE__, __LINE__, + "source_line matched expected value"); + else + ::selftest::fail (__FILE__, __LINE__, + "source_line did not match expected value"); +} + +/* Run all of the selftests within this file. */ + +void +input_c_tests () +{ + test_accessing_ordinary_linemaps (); + test_unknown_location (); + test_builtins (); + test_reading_source_line (); +} + +} // namespace selftest + +#endif /* CHECKING_P */ diff --git a/gcc/rtl-tests.c b/gcc/rtl-tests.c new file mode 100644 index 00000000000..3e9ebae02bb --- /dev/null +++ b/gcc/rtl-tests.c @@ -0,0 +1,117 @@ +/* Unit tests for RTL-handling. + Copyright (C) 2015-2016 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "opts.h" +#include "signop.h" +#include "hash-set.h" +#include "fixed-value.h" +#include "alias.h" +#include "flags.h" +#include "symtab.h" +#include "tree-core.h" +#include "stor-layout.h" +#include "tree.h" +#include "stringpool.h" +#include "stor-layout.h" +#include "rtl.h" +#include "pretty-print.h" +#include "cfgbuild.h" +#include "print-rtl.h" +#include "selftest.h" +#include "function.h" +#include "emit-rtl.h" + +#if CHECKING_P + +namespace selftest { + +/* Verify that PAT is printed as EXPECTED. Helper function for + selftests. */ + +static void +verify_print_pattern (const char *expected, rtx pat) +{ + pretty_printer pp; + print_pattern (&pp, pat, 1); + ASSERT_STREQ (expected, pp_formatted_text (&pp)); +} + +/* Unit testing of "single_set". */ + +static void +test_single_set () +{ + /* A label is not a SET. */ + ASSERT_EQ (NULL_RTX, single_set (gen_label_rtx ())); + + /* An unconditional jump insn is a single SET. */ + rtx set_pc = gen_rtx_SET (pc_rtx, + gen_rtx_LABEL_REF (VOIDmode, + gen_label_rtx ())); + rtx_insn *jump_insn = emit_jump_insn (set_pc); + ASSERT_EQ (set_pc, single_set (jump_insn)); + + /* etc */ +} + +/* Construct an unconditional jump to a label, and verify that + various properties of it are sane. */ + +static void +test_uncond_jump () +{ + rtx_insn *label = gen_label_rtx (); + rtx jump_pat = gen_rtx_SET (pc_rtx, + gen_rtx_LABEL_REF (VOIDmode, + label)); + ASSERT_EQ (SET, jump_pat->code); + ASSERT_EQ (LABEL_REF, SET_SRC (jump_pat)->code); + ASSERT_EQ (label, LABEL_REF_LABEL (SET_SRC (jump_pat))); + ASSERT_EQ (PC, SET_DEST (jump_pat)->code); + + verify_print_pattern ("pc=L0", jump_pat); + + rtx_insn *jump_insn = emit_jump_insn (jump_pat); + ASSERT_FALSE (any_condjump_p (jump_insn)); + ASSERT_TRUE (any_uncondjump_p (jump_insn)); + ASSERT_TRUE (pc_set (jump_insn)); + ASSERT_TRUE (simplejump_p (jump_insn)); + ASSERT_TRUE (onlyjump_p (jump_insn)); + ASSERT_TRUE (control_flow_insn_p (jump_insn)); +} + +/* Run all of the selftests within this file. */ + +void +rtl_tests_c_tests () +{ + test_single_set (); + test_uncond_jump (); + + /* Purge state. */ + set_first_insn (NULL); + set_last_insn (NULL); +} + +} // namespace selftest +#endif /* #if CHECKING_P */ diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c new file mode 100644 index 00000000000..ab334aac640 --- /dev/null +++ b/gcc/selftest-run-tests.c @@ -0,0 +1,77 @@ +/* Implementation of selftests. + Copyright (C) 2015-2016 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "selftest.h" + +/* This function needed to be split out from selftest.c as it references + tests from the whole source tree, and so is within + OBJS in Makefile.in, whereas selftest.o is within OBJS-libcommon. + This allows us to embed tests within files in OBJS-libcommon without + introducing a dependency on objects within OBJS. */ + +#if CHECKING_P + +/* Run all tests, aborting if any fail. */ + +void +selftest::run_tests () +{ + long start_time = get_run_time (); + + /* Run all the tests, in hand-coded order of (approximate) dependencies: + run the tests for lowest-level code first. */ + + /* Low-level data structures. */ + bitmap_c_tests (); + et_forest_c_tests (); + hash_map_tests_c_tests (); + hash_set_tests_c_tests (); + vec_c_tests (); + wide_int_cc_tests (); + + /* Mid-level data structures. */ + input_c_tests (); + tree_c_tests (); + gimple_c_tests (); + rtl_tests_c_tests (); + + /* Higher-level tests, or for components that other selftests don't + rely on. */ + diagnostic_show_locus_c_tests (); + fold_const_c_tests (); + spellcheck_c_tests (); + tree_cfg_c_tests (); + + /* This one relies on most of the above. */ + function_tests_c_tests (); + + /* Finished running tests. */ + long finish_time = get_run_time (); + long elapsed_time = finish_time - start_time; + + fprintf (stderr, + "-fself-test: %i pass(es) in %ld.%06ld seconds\n", + num_passes, + elapsed_time / 1000000, elapsed_time % 1000000); +} + +#endif /* #if CHECKING_P */ diff --git a/gcc/selftest.c b/gcc/selftest.c new file mode 100644 index 00000000000..de804df9f3e --- /dev/null +++ b/gcc/selftest.c @@ -0,0 +1,47 @@ +/* A self-testing framework, for use by -fself-test. + Copyright (C) 2015-2016 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "selftest.h" + +#if CHECKING_P + +int selftest::num_passes; + +/* Record the successful outcome of some aspect of a test. */ + +void +selftest::pass (const char */*file*/, int /*line*/, const char */*msg*/) +{ + num_passes++; +} + +/* Report the failed outcome of some aspect of a test and abort. */ + +void +selftest::fail (const char *file, int line, const char *msg) +{ + fprintf (stderr,"%s:%i: FAIL: %s\n", file, line, msg); + /* TODO: add calling function name as well? */ + abort (); +} + +#endif /* #if CHECKING_P */ diff --git a/gcc/selftest.h b/gcc/selftest.h new file mode 100644 index 00000000000..a1d3074b7de --- /dev/null +++ b/gcc/selftest.h @@ -0,0 +1,153 @@ +/* A self-testing framework, for use by -fself-test. + Copyright (C) 2015-2016 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#ifndef GCC_SELFTEST_H +#define GCC_SELFTEST_H + +/* The selftest code should entirely disappear in a production + configuration, hence we guard all of it with #if CHECKING_P. */ + +#if CHECKING_P + +namespace selftest { + +/* The entrypoint for running all tests. */ + +extern void run_tests (); + +/* Record the successful outcome of some aspect of the test. */ + +extern void pass (const char *file, int line, const char *msg); + +/* Report the failed outcome of some aspect of the test and abort. */ + +extern void fail (const char *file, int line, const char *msg); + +/* Declarations for specific families of tests (by source file), in + alphabetical order. */ +extern void bitmap_c_tests (); +extern void diagnostic_show_locus_c_tests (); +extern void et_forest_c_tests (); +extern void fold_const_c_tests (); +extern void function_tests_c_tests (); +extern void gimple_c_tests (); +extern void hash_map_tests_c_tests (); +extern void hash_set_tests_c_tests (); +extern void input_c_tests (); +extern void rtl_tests_c_tests (); +extern void spellcheck_c_tests (); +extern void tree_c_tests (); +extern void tree_cfg_c_tests (); +extern void vec_c_tests (); +extern void wide_int_cc_tests (); + +extern int num_passes; + +} /* end of namespace selftest. */ + +/* Macros for writing tests. */ + +/* Evaluate EXPR and coerce to bool, calling + ::selftest::pass if it is true, + ::selftest::fail if it false. */ + +#define ASSERT_TRUE(EXPR) \ + SELFTEST_BEGIN_STMT \ + const char *desc = "ASSERT_TRUE (" #EXPR ")"; \ + bool actual = (EXPR); \ + if (actual) \ + ::selftest::pass (__FILE__, __LINE__, desc); \ + else \ + ::selftest::fail (__FILE__, __LINE__, desc); \ + SELFTEST_END_STMT + +/* Evaluate EXPR and coerce to bool, calling + ::selftest::pass if it is false, + ::selftest::fail if it true. */ + +#define ASSERT_FALSE(EXPR) \ + SELFTEST_BEGIN_STMT \ + const char *desc = "ASSERT_FALSE (" #EXPR ")"; \ + bool actual = (EXPR); \ + if (actual) \ + ::selftest::fail (__FILE__, __LINE__, desc); \ + else \ + ::selftest::pass (__FILE__, __LINE__, desc); \ + SELFTEST_END_STMT + +/* Evaluate EXPECTED and ACTUAL and compare them with ==, calling + ::selftest::pass if they are equal, + ::selftest::fail if they are non-equal. */ + +#define ASSERT_EQ(EXPECTED, ACTUAL) \ + SELFTEST_BEGIN_STMT \ + const char *desc = "ASSERT_EQ (" #EXPECTED ", " #ACTUAL ")"; \ + if ((EXPECTED) == (ACTUAL)) \ + ::selftest::pass (__FILE__, __LINE__, desc); \ + else \ + ::selftest::fail (__FILE__, __LINE__, desc); \ + SELFTEST_END_STMT + +/* Evaluate EXPECTED and ACTUAL and compare them with !=, calling + ::selftest::pass if they are non-equal, + ::selftest::fail if they are equal. */ + +#define ASSERT_NE(EXPECTED, ACTUAL) \ + SELFTEST_BEGIN_STMT \ + const char *desc = "ASSERT_NE (" #EXPECTED ", " #ACTUAL ")"; \ + if ((EXPECTED) != (ACTUAL)) \ + ::selftest::pass (__FILE__, __LINE__, desc); \ + else \ + ::selftest::fail (__FILE__, __LINE__, desc); \ + SELFTEST_END_STMT + +/* Evaluate EXPECTED and ACTUAL and compare them with strcmp, calling + ::selftest::pass if they are equal, + ::selftest::fail if they are non-equal. */ + +#define ASSERT_STREQ(EXPECTED, ACTUAL) \ + SELFTEST_BEGIN_STMT \ + const char *desc = "ASSERT_STREQ (" #EXPECTED ", " #ACTUAL ")"; \ + const char *expected_ = (EXPECTED); \ + const char *actual_ = (ACTUAL); \ + if (0 == strcmp (expected_, actual_)) \ + ::selftest::pass (__FILE__, __LINE__, desc); \ + else \ + ::selftest::fail (__FILE__, __LINE__, desc); \ + SELFTEST_END_STMT + +/* Evaluate PRED1 (VAL1), calling ::selftest::pass if it is true, + ::selftest::fail if it is false. */ + +#define ASSERT_PRED1(PRED1, VAL1) \ + SELFTEST_BEGIN_STMT \ + const char *desc = "ASSERT_PRED1 (" #PRED1 ", " #VAL1 ")"; \ + bool actual = (PRED1) (VAL1); \ + if (actual) \ + ::selftest::pass (__FILE__, __LINE__, desc); \ + else \ + ::selftest::fail (__FILE__, __LINE__, desc); \ + SELFTEST_END_STMT + +#define SELFTEST_BEGIN_STMT do { +#define SELFTEST_END_STMT } while (0) + +#endif /* #if CHECKING_P */ + +#endif /* GCC_SELFTEST_H */ diff --git a/gcc/spellcheck.c b/gcc/spellcheck.c index e4e83a5ae16..ceb60168bf1 100644 --- a/gcc/spellcheck.c +++ b/gcc/spellcheck.c @@ -23,6 +23,7 @@ along with GCC; see the file COPYING3. If not see #include "tm.h" #include "tree.h" #include "spellcheck.h" +#include "selftest.h" /* The Levenshtein distance is an "edit-distance": the minimal number of one-character insertions, removals or substitutions @@ -165,3 +166,60 @@ find_closest_string (const char *target, return best_candidate; } + +#if CHECKING_P + +namespace selftest { + +/* Selftests. */ + +/* Verify that the levenshtein_distance (A, B) equals the expected + value. */ + +static void +levenshtein_distance_unit_test_oneway (const char *a, const char *b, + edit_distance_t expected) +{ + edit_distance_t actual = levenshtein_distance (a, b); + ASSERT_EQ (actual, expected); +} + +/* Verify that both + levenshtein_distance (A, B) + and + levenshtein_distance (B, A) + equal the expected value, to ensure that the function is symmetric. */ + +static void +levenshtein_distance_unit_test (const char *a, const char *b, + edit_distance_t expected) +{ + levenshtein_distance_unit_test_oneway (a, b, expected); + levenshtein_distance_unit_test_oneway (b, a, expected); +} + +/* Verify levenshtein_distance for a variety of pairs of pre-canned + inputs, comparing against known-good values. */ + +void +spellcheck_c_tests () +{ + levenshtein_distance_unit_test ("", "nonempty", strlen ("nonempty")); + levenshtein_distance_unit_test ("saturday", "sunday", 3); + levenshtein_distance_unit_test ("foo", "m_foo", 2); + levenshtein_distance_unit_test ("hello_world", "HelloWorld", 3); + levenshtein_distance_unit_test + ("the quick brown fox jumps over the lazy dog", "dog", 40); + levenshtein_distance_unit_test + ("the quick brown fox jumps over the lazy dog", + "the quick brown dog jumps over the lazy fox", + 4); + levenshtein_distance_unit_test + ("Lorem ipsum dolor sit amet, consectetur adipiscing elit,", + "All your base are belong to us", + 44); +} + +} // namespace selftest + +#endif /* #if CHECKING_P */ diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index b68744b92f3..15a77167943 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2016-06-06 David Malcolm + + * gcc.dg/plugin/levenshtein-test-1.c: Delete. + * gcc.dg/plugin/levenshtein_plugin.c: Delete. + * gcc.dg/plugin/plugin.exp (plugin_test_list): Remove the + above. + 2016-06-06 Kyrylo Tkachov PR middle-end/37780 diff --git a/gcc/testsuite/gcc.dg/plugin/levenshtein-test-1.c b/gcc/testsuite/gcc.dg/plugin/levenshtein-test-1.c deleted file mode 100644 index ac49992780d..00000000000 --- a/gcc/testsuite/gcc.dg/plugin/levenshtein-test-1.c +++ /dev/null @@ -1,9 +0,0 @@ -/* Placeholder C source file for unit-testing gcc/spellcheck.c. */ -/* { dg-do compile } */ -/* { dg-options "-O" } */ - -int -main (int argc, char **argv) -{ - return 0; -} diff --git a/gcc/testsuite/gcc.dg/plugin/levenshtein_plugin.c b/gcc/testsuite/gcc.dg/plugin/levenshtein_plugin.c deleted file mode 100644 index 3e7dc788930..00000000000 --- a/gcc/testsuite/gcc.dg/plugin/levenshtein_plugin.c +++ /dev/null @@ -1,64 +0,0 @@ -/* Plugin for unittesting gcc/spellcheck.h. */ - -#include "config.h" -#include "gcc-plugin.h" -#include "system.h" -#include "coretypes.h" -#include "spellcheck.h" -#include "diagnostic.h" - -int plugin_is_GPL_compatible; - -static void -levenshtein_distance_unit_test_oneway (const char *a, const char *b, - edit_distance_t expected) -{ - edit_distance_t actual = levenshtein_distance (a, b); - if (actual != expected) - error ("levenshtein_distance (\"%s\", \"%s\") : expected: %i got %i", - a, b, expected, actual); -} - - -static void -levenshtein_distance_unit_test (const char *a, const char *b, - edit_distance_t expected) -{ - /* Run every test both ways to ensure it's symmetric. */ - levenshtein_distance_unit_test_oneway (a, b, expected); - levenshtein_distance_unit_test_oneway (b, a, expected); -} - -/* Callback handler for the PLUGIN_FINISH event; run - levenshtein_distance unit tests here. */ - -static void -on_finish (void */*gcc_data*/, void */*user_data*/) -{ - levenshtein_distance_unit_test ("", "nonempty", strlen ("nonempty")); - levenshtein_distance_unit_test ("saturday", "sunday", 3); - levenshtein_distance_unit_test ("foo", "m_foo", 2); - levenshtein_distance_unit_test ("hello_world", "HelloWorld", 3); - levenshtein_distance_unit_test - ("the quick brown fox jumps over the lazy dog", "dog", 40); - levenshtein_distance_unit_test - ("the quick brown fox jumps over the lazy dog", - "the quick brown dog jumps over the lazy fox", - 4); - levenshtein_distance_unit_test - ("Lorem ipsum dolor sit amet, consectetur adipiscing elit,", - "All your base are belong to us", - 44); -} - -int -plugin_init (struct plugin_name_args *plugin_info, - struct plugin_gcc_version */*version*/) -{ - register_callback (plugin_info->base_name, - PLUGIN_FINISH, - on_finish, - NULL); /* void *user_data */ - - return 0; -} diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.dg/plugin/plugin.exp index 321b4baafe0..be2ac8dcfcb 100644 --- a/gcc/testsuite/gcc.dg/plugin/plugin.exp +++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp @@ -70,7 +70,6 @@ set plugin_test_list [list \ diagnostic-test-expressions-1.c } \ { diagnostic_plugin_show_trees.c \ diagnostic-test-show-trees-1.c } \ - { levenshtein_plugin.c levenshtein-test-1.c } \ { location_overflow_plugin.c \ location-overflow-test-1.c \ location-overflow-test-2.c } \ diff --git a/gcc/toplev.c b/gcc/toplev.c index 230878eeff1..543b8a3dc72 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -87,6 +87,8 @@ along with GCC; see the file COPYING3. If not see #include "xcoffout.h" /* Needed for external data declarations. */ #endif +#include "selftest.h" + static void general_init (const char *, bool); static void do_compile (); static void process_options (void); @@ -2031,6 +2033,27 @@ toplev::start_timevars () timevar_start (TV_TOTAL); } +/* Handle -fself-test. */ + +void +toplev::run_self_tests () +{ +#if CHECKING_P + /* Reset some state. */ + input_location = UNKNOWN_LOCATION; + bitmap_obstack_initialize (NULL); + + /* Run the tests; any failures will lead to an abort of the process. + Use "make selftests-gdb" to run under the debugger. */ + ::selftest::run_tests (); + + /* Cleanup. */ + bitmap_obstack_release (NULL); +#else + inform (UNKNOWN_LOCATION, "self-tests are not enabled in this build"); +#endif /* #if CHECKING_P */ +} + /* Entry point of cc1, cc1plus, jc1, f771, etc. Exit code is FATAL_EXIT_CODE if can't open files or if there were any errors, or SUCCESS_EXIT_CODE if compilation succeeded. @@ -2098,6 +2121,9 @@ toplev::main (int argc, char **argv) if (warningcount || errorcount || werrorcount) print_ignored_options (); + if (flag_self_test) + run_self_tests (); + /* Invoke registered plugin callbacks if any. Some plugins could emit some diagnostics here. */ invoke_plugin_callbacks (PLUGIN_FINISH, NULL); diff --git a/gcc/toplev.h b/gcc/toplev.h index 0beb06e8571..06923cf0c94 100644 --- a/gcc/toplev.h +++ b/gcc/toplev.h @@ -42,6 +42,8 @@ private: void start_timevars (); + void run_self_tests (); + bool m_use_TV_TOTAL; bool m_init_signals; }; diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c index 7fc24ba7173..40e524bba9c 100644 --- a/gcc/tree-cfg.c +++ b/gcc/tree-cfg.c @@ -58,6 +58,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-cfgcleanup.h" #include "gimplify.h" #include "attribs.h" +#include "selftest.h" /* This file contains functions for building the Control Flow Graph (CFG) for a function tree. */ @@ -9195,3 +9196,280 @@ gt_pch_nx (edge_def *e, gt_pointer_operator op, void *cookie) op (&(e->insns.r), cookie); op (&(block), cookie); } + +#if CHECKING_P + +namespace selftest { + +/* Helper function for CFG selftests: create a dummy function decl + and push it as cfun. */ + +static tree +push_fndecl (const char *name) +{ + tree fn_type = build_function_type_array (integer_type_node, 0, NULL); + /* FIXME: this uses input_location: */ + tree fndecl = build_fn_decl (name, fn_type); + tree retval = build_decl (UNKNOWN_LOCATION, RESULT_DECL, + NULL_TREE, integer_type_node); + DECL_RESULT (fndecl) = retval; + push_struct_function (fndecl); + function *fun = DECL_STRUCT_FUNCTION (fndecl); + ASSERT_TRUE (fun != NULL); + init_empty_tree_cfg_for_function (fun); + ASSERT_EQ (2, n_basic_blocks_for_fn (fun)); + ASSERT_EQ (0, n_edges_for_fn (fun)); + return fndecl; +} + +/* These tests directly create CFGs. + Compare with the static fns within tree-cfg.c: + - build_gimple_cfg + - make_blocks: calls create_basic_block (seq, bb); + - make_edges. */ + +/* Verify a simple cfg of the form: + ENTRY -> A -> B -> C -> EXIT. */ + +static void +test_linear_chain () +{ + gimple_register_cfg_hooks (); + + tree fndecl = push_fndecl ("cfg_test_linear_chain"); + function *fun = DECL_STRUCT_FUNCTION (fndecl); + + /* Create some empty blocks. */ + basic_block bb_a = create_empty_bb (ENTRY_BLOCK_PTR_FOR_FN (fun)); + basic_block bb_b = create_empty_bb (bb_a); + basic_block bb_c = create_empty_bb (bb_b); + + ASSERT_EQ (5, n_basic_blocks_for_fn (fun)); + ASSERT_EQ (0, n_edges_for_fn (fun)); + + /* Create some edges: a simple linear chain of BBs. */ + make_edge (ENTRY_BLOCK_PTR_FOR_FN (fun), bb_a, EDGE_FALLTHRU); + make_edge (bb_a, bb_b, 0); + make_edge (bb_b, bb_c, 0); + make_edge (bb_c, EXIT_BLOCK_PTR_FOR_FN (fun), 0); + + /* Verify the edges. */ + ASSERT_EQ (4, n_edges_for_fn (fun)); + ASSERT_EQ (NULL, ENTRY_BLOCK_PTR_FOR_FN (fun)->preds); + ASSERT_EQ (1, ENTRY_BLOCK_PTR_FOR_FN (fun)->succs->length ()); + ASSERT_EQ (1, bb_a->preds->length ()); + ASSERT_EQ (1, bb_a->succs->length ()); + ASSERT_EQ (1, bb_b->preds->length ()); + ASSERT_EQ (1, bb_b->succs->length ()); + ASSERT_EQ (1, bb_c->preds->length ()); + ASSERT_EQ (1, bb_c->succs->length ()); + ASSERT_EQ (1, EXIT_BLOCK_PTR_FOR_FN (fun)->preds->length ()); + ASSERT_EQ (NULL, EXIT_BLOCK_PTR_FOR_FN (fun)->succs); + + /* Verify the dominance information + Each BB in our simple chain should be dominated by the one before + it. */ + calculate_dominance_info (CDI_DOMINATORS); + ASSERT_EQ (bb_a, get_immediate_dominator (CDI_DOMINATORS, bb_b)); + ASSERT_EQ (bb_b, get_immediate_dominator (CDI_DOMINATORS, bb_c)); + vec dom_by_b = get_dominated_by (CDI_DOMINATORS, bb_b); + ASSERT_EQ (1, dom_by_b.length ()); + ASSERT_EQ (bb_c, dom_by_b[0]); + free_dominance_info (CDI_DOMINATORS); + + /* Similarly for post-dominance: each BB in our chain is post-dominated + by the one after it. */ + calculate_dominance_info (CDI_POST_DOMINATORS); + ASSERT_EQ (bb_b, get_immediate_dominator (CDI_POST_DOMINATORS, bb_a)); + ASSERT_EQ (bb_c, get_immediate_dominator (CDI_POST_DOMINATORS, bb_b)); + vec postdom_by_b = get_dominated_by (CDI_POST_DOMINATORS, bb_b); + ASSERT_EQ (1, postdom_by_b.length ()); + ASSERT_EQ (bb_a, postdom_by_b[0]); + free_dominance_info (CDI_POST_DOMINATORS); + + pop_cfun (); +} + +/* Verify a simple CFG of the form: + ENTRY + | + A + / \ + /t \f + B C + \ / + \ / + D + | + EXIT. */ + +static void +test_diamond () +{ + gimple_register_cfg_hooks (); + + tree fndecl = push_fndecl ("cfg_test_diamond"); + function *fun = DECL_STRUCT_FUNCTION (fndecl); + + /* Create some empty blocks. */ + basic_block bb_a = create_empty_bb (ENTRY_BLOCK_PTR_FOR_FN (fun)); + basic_block bb_b = create_empty_bb (bb_a); + basic_block bb_c = create_empty_bb (bb_a); + basic_block bb_d = create_empty_bb (bb_b); + + ASSERT_EQ (6, n_basic_blocks_for_fn (fun)); + ASSERT_EQ (0, n_edges_for_fn (fun)); + + /* Create the edges. */ + make_edge (ENTRY_BLOCK_PTR_FOR_FN (fun), bb_a, EDGE_FALLTHRU); + make_edge (bb_a, bb_b, EDGE_TRUE_VALUE); + make_edge (bb_a, bb_c, EDGE_FALSE_VALUE); + make_edge (bb_b, bb_d, 0); + make_edge (bb_c, bb_d, 0); + make_edge (bb_d, EXIT_BLOCK_PTR_FOR_FN (fun), 0); + + /* Verify the edges. */ + ASSERT_EQ (6, n_edges_for_fn (fun)); + ASSERT_EQ (1, bb_a->preds->length ()); + ASSERT_EQ (2, bb_a->succs->length ()); + ASSERT_EQ (1, bb_b->preds->length ()); + ASSERT_EQ (1, bb_b->succs->length ()); + ASSERT_EQ (1, bb_c->preds->length ()); + ASSERT_EQ (1, bb_c->succs->length ()); + ASSERT_EQ (2, bb_d->preds->length ()); + ASSERT_EQ (1, bb_d->succs->length ()); + + /* Verify the dominance information. */ + calculate_dominance_info (CDI_DOMINATORS); + ASSERT_EQ (bb_a, get_immediate_dominator (CDI_DOMINATORS, bb_b)); + ASSERT_EQ (bb_a, get_immediate_dominator (CDI_DOMINATORS, bb_c)); + ASSERT_EQ (bb_a, get_immediate_dominator (CDI_DOMINATORS, bb_d)); + vec dom_by_a = get_dominated_by (CDI_DOMINATORS, bb_a); + ASSERT_EQ (3, dom_by_a.length ()); /* B, C, D, in some order. */ + vec dom_by_b = get_dominated_by (CDI_DOMINATORS, bb_b); + ASSERT_EQ (0, dom_by_b.length ()); + free_dominance_info (CDI_DOMINATORS); + + /* Similarly for post-dominance. */ + calculate_dominance_info (CDI_POST_DOMINATORS); + ASSERT_EQ (bb_d, get_immediate_dominator (CDI_POST_DOMINATORS, bb_a)); + ASSERT_EQ (bb_d, get_immediate_dominator (CDI_POST_DOMINATORS, bb_b)); + ASSERT_EQ (bb_d, get_immediate_dominator (CDI_POST_DOMINATORS, bb_c)); + vec postdom_by_d = get_dominated_by (CDI_POST_DOMINATORS, bb_d); + ASSERT_EQ (3, postdom_by_d.length ()); /* A, B, C in some order. */ + vec postdom_by_b = get_dominated_by (CDI_POST_DOMINATORS, bb_b); + ASSERT_EQ (0, postdom_by_b.length ()); + free_dominance_info (CDI_POST_DOMINATORS); + + pop_cfun (); +} + +/* Verify that we can handle a CFG containing a "complete" aka + fully-connected subgraph (where A B C D below all have edges + pointing to each other node, also to themselves). + e.g.: + ENTRY EXIT + | ^ + | / + | / + | / + V/ + A<--->B + ^^ ^^ + | \ / | + | X | + | / \ | + VV VV + C<--->D +*/ + +static void +test_fully_connected () +{ + gimple_register_cfg_hooks (); + + tree fndecl = push_fndecl ("cfg_fully_connected"); + function *fun = DECL_STRUCT_FUNCTION (fndecl); + + const int n = 4; + + /* Create some empty blocks. */ + auto_vec subgraph_nodes; + for (int i = 0; i < n; i++) + subgraph_nodes.safe_push (create_empty_bb (ENTRY_BLOCK_PTR_FOR_FN (fun))); + + ASSERT_EQ (n + 2, n_basic_blocks_for_fn (fun)); + ASSERT_EQ (0, n_edges_for_fn (fun)); + + /* Create the edges. */ + make_edge (ENTRY_BLOCK_PTR_FOR_FN (fun), subgraph_nodes[0], EDGE_FALLTHRU); + make_edge (subgraph_nodes[0], EXIT_BLOCK_PTR_FOR_FN (fun), 0); + for (int i = 0; i < n; i++) + for (int j = 0; j < n; j++) + make_edge (subgraph_nodes[i], subgraph_nodes[j], 0); + + /* Verify the edges. */ + ASSERT_EQ (2 + (n * n), n_edges_for_fn (fun)); + /* The first one is linked to ENTRY/EXIT as well as itself and + everything else. */ + ASSERT_EQ (n + 1, subgraph_nodes[0]->preds->length ()); + ASSERT_EQ (n + 1, subgraph_nodes[0]->succs->length ()); + /* The other ones in the subgraph are linked to everything in + the subgraph (including themselves). */ + for (int i = 1; i < n; i++) + { + ASSERT_EQ (n, subgraph_nodes[i]->preds->length ()); + ASSERT_EQ (n, subgraph_nodes[i]->succs->length ()); + } + + /* Verify the dominance information. */ + calculate_dominance_info (CDI_DOMINATORS); + /* The initial block in the subgraph should be dominated by ENTRY. */ + ASSERT_EQ (ENTRY_BLOCK_PTR_FOR_FN (fun), + get_immediate_dominator (CDI_DOMINATORS, + subgraph_nodes[0])); + /* Every other block in the subgraph should be dominated by the + initial block. */ + for (int i = 1; i < n; i++) + ASSERT_EQ (subgraph_nodes[0], + get_immediate_dominator (CDI_DOMINATORS, + subgraph_nodes[i])); + free_dominance_info (CDI_DOMINATORS); + + /* Similarly for post-dominance. */ + calculate_dominance_info (CDI_POST_DOMINATORS); + /* The initial block in the subgraph should be postdominated by EXIT. */ + ASSERT_EQ (EXIT_BLOCK_PTR_FOR_FN (fun), + get_immediate_dominator (CDI_POST_DOMINATORS, + subgraph_nodes[0])); + /* Every other block in the subgraph should be postdominated by the + initial block, since that leads to EXIT. */ + for (int i = 1; i < n; i++) + ASSERT_EQ (subgraph_nodes[0], + get_immediate_dominator (CDI_POST_DOMINATORS, + subgraph_nodes[i])); + free_dominance_info (CDI_POST_DOMINATORS); + + pop_cfun (); +} + +/* Run all of the selftests within this file. */ + +void +tree_cfg_c_tests () +{ + test_linear_chain (); + test_diamond (); + test_fully_connected (); +} + +} // namespace selftest + +/* TODO: test the dominator/postdominator logic with various graphs/nodes: + - loop + - nested loops + - switch statement (a block with many out-edges) + - something that jumps to itself + - etc */ + +#endif /* CHECKING_P */ diff --git a/gcc/tree.c b/gcc/tree.c index 2e01eac6aca..fd0e6921bfd 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -61,6 +61,7 @@ along with GCC; see the file COPYING3. If not see #include "builtins.h" #include "print-tree.h" #include "ipa-utils.h" +#include "selftest.h" /* Tree code classes. */ @@ -14203,4 +14204,65 @@ combined_fn_name (combined_fn fn) return internal_fn_name (as_internal_fn (fn)); } +#if CHECKING_P + +namespace selftest { + +/* Selftests for tree. */ + +/* Verify that integer constants are sane. */ + +static void +test_integer_constants () +{ + ASSERT_TRUE (integer_type_node != NULL); + ASSERT_TRUE (build_int_cst (integer_type_node, 0) != NULL); + + tree type = integer_type_node; + + tree zero = build_zero_cst (type); + ASSERT_EQ (INTEGER_CST, TREE_CODE (zero)); + ASSERT_EQ (type, TREE_TYPE (zero)); + + tree one = build_int_cst (type, 1); + ASSERT_EQ (INTEGER_CST, TREE_CODE (one)); + ASSERT_EQ (type, TREE_TYPE (zero)); +} + +/* Verify identifiers. */ + +static void +test_identifiers () +{ + tree identifier = get_identifier ("foo"); + ASSERT_EQ (3, IDENTIFIER_LENGTH (identifier)); + ASSERT_STREQ ("foo", IDENTIFIER_POINTER (identifier)); +} + +/* Verify LABEL_DECL. */ + +static void +test_labels () +{ + tree identifier = get_identifier ("err"); + tree label_decl = build_decl (UNKNOWN_LOCATION, LABEL_DECL, + identifier, void_type_node); + ASSERT_EQ (-1, LABEL_DECL_UID (label_decl)); + ASSERT_FALSE (FORCED_LABEL (label_decl)); +} + +/* Run all of the selftests within this file. */ + +void +tree_c_tests () +{ + test_integer_constants (); + test_identifiers (); + test_labels (); +} + +} // namespace selftest + +#endif /* CHECKING_P */ + #include "gt-tree.h" diff --git a/gcc/vec.c b/gcc/vec.c index a483d5b5451..fd200ea904b 100644 --- a/gcc/vec.c +++ b/gcc/vec.c @@ -30,6 +30,7 @@ along with GCC; see the file COPYING3. If not see #include "system.h" #include "coretypes.h" #include "hash-table.h" +#include "selftest.h" /* vNULL is an empty type with a template cast operation that returns a zero-initialized vec instance. Use this when you want @@ -188,3 +189,194 @@ dump_vec_loc_statistics (void) { vec_mem_desc.dump (VEC_ORIGIN); } + +#ifndef GENERATOR_FILE +#if CHECKING_P + +namespace selftest { + +/* Selftests. */ + +/* Call V.safe_push for all ints from START up to, but not including LIMIT. + Helper function for selftests. */ + +static void +safe_push_range (vec &v, int start, int limit) +{ + for (int i = start; i < limit; i++) + v.safe_push (i); +} + +/* Verify that vec::quick_push works correctly. */ + +static void +test_quick_push () +{ + auto_vec v; + ASSERT_EQ (0, v.length ()); + v.reserve (3); + ASSERT_EQ (0, v.length ()); + ASSERT_TRUE (v.space (3)); + v.quick_push (5); + v.quick_push (6); + v.quick_push (7); + ASSERT_EQ (3, v.length ()); + ASSERT_EQ (5, v[0]); + ASSERT_EQ (6, v[1]); + ASSERT_EQ (7, v[2]); +} + +/* Verify that vec::safe_push works correctly. */ + +static void +test_safe_push () +{ + auto_vec v; + ASSERT_EQ (0, v.length ()); + v.safe_push (5); + v.safe_push (6); + v.safe_push (7); + ASSERT_EQ (3, v.length ()); + ASSERT_EQ (5, v[0]); + ASSERT_EQ (6, v[1]); + ASSERT_EQ (7, v[2]); +} + +/* Verify that vec::truncate works correctly. */ + +static void +test_truncate () +{ + auto_vec v; + ASSERT_EQ (0, v.length ()); + safe_push_range (v, 0, 10); + ASSERT_EQ (10, v.length ()); + + v.truncate (5); + ASSERT_EQ (5, v.length ()); +} + +/* Verify that vec::safe_grow_cleared works correctly. */ + +static void +test_safe_grow_cleared () +{ + auto_vec v; + ASSERT_EQ (0, v.length ()); + v.safe_grow_cleared (50); + ASSERT_EQ (50, v.length ()); + ASSERT_EQ (0, v[0]); + ASSERT_EQ (0, v[49]); +} + +/* Verify that vec::pop works correctly. */ + +static void +test_pop () +{ + auto_vec v; + safe_push_range (v, 5, 20); + ASSERT_EQ (15, v.length ()); + + int last = v.pop (); + ASSERT_EQ (19, last); + ASSERT_EQ (14, v.length ()); +} + +/* Verify that vec::safe_insert works correctly. */ + +static void +test_safe_insert () +{ + auto_vec v; + safe_push_range (v, 0, 10); + v.safe_insert (5, 42); + ASSERT_EQ (4, v[4]); + ASSERT_EQ (42, v[5]); + ASSERT_EQ (5, v[6]); + ASSERT_EQ (11, v.length ()); +} + +/* Verify that vec::ordered_remove works correctly. */ + +static void +test_ordered_remove () +{ + auto_vec v; + safe_push_range (v, 0, 10); + v.ordered_remove (5); + ASSERT_EQ (4, v[4]); + ASSERT_EQ (6, v[5]); + ASSERT_EQ (9, v.length ()); +} + +/* Verify that vec::unordered_remove works correctly. */ + +static void +test_unordered_remove () +{ + auto_vec v; + safe_push_range (v, 0, 10); + v.unordered_remove (5); + ASSERT_EQ (9, v.length ()); +} + +/* Verify that vec::block_remove works correctly. */ + +static void +test_block_remove () +{ + auto_vec v; + safe_push_range (v, 0, 10); + v.block_remove (5, 3); + ASSERT_EQ (3, v[3]); + ASSERT_EQ (4, v[4]); + ASSERT_EQ (8, v[5]); + ASSERT_EQ (9, v[6]); + ASSERT_EQ (7, v.length ()); +} + +/* Comparator for use by test_qsort. */ + +static int +reverse_cmp (const void *p_i, const void *p_j) +{ + return *(const int *)p_j - *(const int *)p_i; +} + +/* Verify that vec::qsort works correctly. */ + +static void +test_qsort () +{ + auto_vec v; + safe_push_range (v, 0, 10); + v.qsort (reverse_cmp); + ASSERT_EQ (9, v[0]); + ASSERT_EQ (8, v[1]); + ASSERT_EQ (1, v[8]); + ASSERT_EQ (0, v[9]); + ASSERT_EQ (10, v.length ()); +} + +/* Run all of the selftests within this file. */ + +void +vec_c_tests () +{ + test_quick_push (); + test_safe_push (); + test_truncate (); + test_safe_grow_cleared (); + test_pop (); + test_safe_insert (); + test_ordered_remove (); + test_unordered_remove (); + test_block_remove (); + test_qsort (); +} + +} // namespace selftest + +#endif /* #if CHECKING_P */ +#endif /* #ifndef GENERATOR_FILE */ diff --git a/gcc/wide-int.cc b/gcc/wide-int.cc index 8648e7dc286..30d355fdb89 100644 --- a/gcc/wide-int.cc +++ b/gcc/wide-int.cc @@ -23,6 +23,8 @@ along with GCC; see the file COPYING3. If not see #include "coretypes.h" #include "tm.h" #include "tree.h" +#include "selftest.h" +#include "wide-int-print.h" #define HOST_BITS_PER_HALF_WIDE_INT 32 @@ -2144,3 +2146,171 @@ template void generic_wide_int >::dump () const; template void generic_wide_int >::dump () const; template void offset_int::dump () const; template void widest_int::dump () const; + + +#if CHECKING_P + +namespace selftest { + +/* Selftests for wide ints. We run these multiple times, once per type. */ + +/* Helper function for building a test value. */ + +template +static VALUE_TYPE +from_int (int i); + +/* Specializations of the fixture for each wide-int type. */ + +/* Specialization for VALUE_TYPE == wide_int. */ + +template <> +wide_int +from_int (int i) +{ + return wi::shwi (i, 32); +} + +/* Specialization for VALUE_TYPE == offset_int. */ + +template <> +offset_int +from_int (int i) +{ + return offset_int (i); +} + +/* Specialization for VALUE_TYPE == widest_int. */ + +template <> +widest_int +from_int (int i) +{ + return widest_int (i); +} + +/* Verify that print_dec (WI, ..., SGN) gives the expected string + representation (using base 10). */ + +static void +assert_deceq (const char *expected, const wide_int_ref &wi, signop sgn) +{ + char buf[WIDE_INT_PRINT_BUFFER_SIZE]; + print_dec (wi, buf, sgn); + ASSERT_STREQ (expected, buf); +} + +/* Likewise for base 16. */ + +static void +assert_hexeq (const char *expected, const wide_int_ref &wi) +{ + char buf[WIDE_INT_PRINT_BUFFER_SIZE]; + print_hex (wi, buf); + ASSERT_STREQ (expected, buf); +} + +/* Test cases. */ + +/* Verify that print_dec and print_hex work for VALUE_TYPE. */ + +template +static void +test_printing () +{ + VALUE_TYPE a = from_int (42); + assert_deceq ("42", a, SIGNED); + assert_hexeq ("0x2a", a); +} + +/* Verify that various operations work correctly for VALUE_TYPE, + unary and binary, using both function syntax, and + overloaded-operators. */ + +template +static void +test_ops () +{ + VALUE_TYPE a = from_int (7); + VALUE_TYPE b = from_int (3); + + /* Using functions. */ + assert_deceq ("-7", wi::neg (a), SIGNED); + assert_deceq ("10", wi::add (a, b), SIGNED); + assert_deceq ("4", wi::sub (a, b), SIGNED); + assert_deceq ("-4", wi::sub (b, a), SIGNED); + assert_deceq ("21", wi::mul (a, b), SIGNED); + + /* Using operators. */ + assert_deceq ("-7", -a, SIGNED); + assert_deceq ("10", a + b, SIGNED); + assert_deceq ("4", a - b, SIGNED); + assert_deceq ("-4", b - a, SIGNED); + assert_deceq ("21", a * b, SIGNED); +} + +/* Verify that various comparisons work correctly for VALUE_TYPE. */ + +template +static void +test_comparisons () +{ + VALUE_TYPE a = from_int (7); + VALUE_TYPE b = from_int (3); + + /* == */ + ASSERT_TRUE (wi::eq_p (a, a)); + ASSERT_FALSE (wi::eq_p (a, b)); + + /* != */ + ASSERT_TRUE (wi::ne_p (a, b)); + ASSERT_FALSE (wi::ne_p (a, a)); + + /* < */ + ASSERT_FALSE (wi::lts_p (a, a)); + ASSERT_FALSE (wi::lts_p (a, b)); + ASSERT_TRUE (wi::lts_p (b, a)); + + /* <= */ + ASSERT_TRUE (wi::les_p (a, a)); + ASSERT_FALSE (wi::les_p (a, b)); + ASSERT_TRUE (wi::les_p (b, a)); + + /* > */ + ASSERT_FALSE (wi::gts_p (a, a)); + ASSERT_TRUE (wi::gts_p (a, b)); + ASSERT_FALSE (wi::gts_p (b, a)); + + /* >= */ + ASSERT_TRUE (wi::ges_p (a, a)); + ASSERT_TRUE (wi::ges_p (a, b)); + ASSERT_FALSE (wi::ges_p (b, a)); + + /* comparison */ + ASSERT_EQ (-1, wi::cmps (b, a)); + ASSERT_EQ (0, wi::cmps (a, a)); + ASSERT_EQ (1, wi::cmps (a, b)); +} + +/* Run all of the selftests, using the given VALUE_TYPE. */ + +template +static void run_all_wide_int_tests () +{ + test_printing (); + test_ops (); + test_comparisons (); +} + +/* Run all of the selftests within this file, for all value types. */ + +void +wide_int_cc_tests () +{ + run_all_wide_int_tests (); + run_all_wide_int_tests (); + run_all_wide_int_tests (); +} + +} // namespace selftest +#endif /* CHECKING_P */ -- 2.30.2