From patchwork Fri Jun 3 19:12:46 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Malcolm X-Patchwork-Id: 629971 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3rLtPW1g4lz9t6X for ; Sat, 4 Jun 2016 04:47:26 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b=AUZ6gmUb; dkim-atps=neutral DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:in-reply-to:references; q=dns; s= default; b=Qz9iV3+3ndWhFDbN9hB2gqhgb0MZLvOd2zXD9nCyUQvLTQ4JW8zLQ X7mYwAunar4JAeKD7SpbToAI9jEDhrA61dSK/Tjz6Li06eYf/rvA0eeMdL2WOMqY gNe2MChOUsVWx7awU0kdTyjFPa2ARTGvfZJUJ2hFZcfjIKLxfLAK3Y= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:in-reply-to:references; s= default; bh=NNch6f2te+BRgUbj4uwH7QU21ZA=; b=AUZ6gmUbnoV2SeSal3MB Q9CwP/a4+++sI92M4i9+vptlgAKdbbC7RP7QbkAiTsV7aE9bYg1GCBscCcl0Qklp 65xdOep2bNf9X8d08m7gw9Y/B3QcmDQVJuNuDxSGw9xJKfJ1GarUP+wEkjh9QYUS 4xKR8e4shCZshN+2p1xGYpA= Received: (qmail 6313 invoked by alias); 3 Jun 2016 18:47:10 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 6232 invoked by uid 89); 3 Jun 2016 18:47:09 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-3.3 required=5.0 tests=BAYES_00, RP_MATCHES_RCVD, SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=gained, 18th, sk:test_ar, WRAPPED_EXPR X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Fri, 03 Jun 2016 18:46:57 +0000 Received: from int-mx14.intmail.prod.int.phx2.redhat.com (int-mx14.intmail.prod.int.phx2.redhat.com [10.5.11.27]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 38049C06C9D1 for ; Fri, 3 Jun 2016 18:46:56 +0000 (UTC) Received: from c64.redhat.com (vpn-224-30.phx2.redhat.com [10.3.224.30]) by int-mx14.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u53Iksjv002695; Fri, 3 Jun 2016 14:46:55 -0400 From: David Malcolm To: gcc-patches@gcc.gnu.org Cc: Bernd Schmidt , Jeff Law , David Malcolm Subject: [PATCH] Selftest framework (v7) Date: Fri, 3 Jun 2016 15:12:46 -0400 Message-Id: <1464981166-25871-1-git-send-email-dmalcolm@redhat.com> In-Reply-To: <5750BF79.20201@redhat.com> References: <5750BF79.20201@redhat.com> X-IsSubscribed: yes On Fri, 2016-06-03 at 01:21 +0200, Bernd Schmidt wrote: > On 06/02/2016 11:06 PM, David Malcolm wrote: > > 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 "selftests". > > (all.cross): Likewise. > > (selftests): New phony target. > > (s-selftests): New target. > > (selftests-gdb): New phony target. > > (COLLECT2_OBJS): Add selftest.o. > > * common.opt (fself-test): New. > > * selftest-run-tests.c: New file. > > * selftest.c: New file. > > * selftest.h: New file. > > * toplev.c: Include selftest.h. > > (toplev::run_self_tests): New. > > (toplev::main): Handle -fself-test. > > * toplev.h (toplev::run_self_tests): New. > > This one looks good to me. I kind of liked the auto-registration, but > I > guess manually calling functions is preferrable to including C files > and > similar in effort required. So it's probably better this way. Thanks. > > + fprintf (stderr, > > + "%s:%i: FAIL: %s\n", > > + file, line, msg); > > + /* TODO: add calling function name as well? */ > > + abort (); > > +} > > That'll fit on one line. Fixed. > Otherwise OK. Likewise for anything Jeff has > already approved in a different form - but please make another pass > and > add brief function comments for new functions, Done. > and please ensure every > step you commit actually compiles (this patch alone won't). Given that this would all be committed atomically, here's a merged version of the patch. I've also rebased the code against today's trunk (r237076). > Let me know which patches still need approval after that. I believe I can self-approve the changes to diagnostic-show-locus.c You've approved the new selftests.* files (I fixed the linewrap issue you identified) and the changes to Makefile.in, common.opt, and toplev.c. Remaining approvals needed: The spellcheck.c changes (moving from a plugin) still need approval. Jeff approved older versions of the rest of this patch with this message: > OK if/when prereqs are approved. Minor twiddling if we end up moving it > elsewhere or standardizing/reducing header files is pre-approved. Since those reviews, the tests have been moved around, gained comments, and various tweaking. I've also ported them to the new API. It's not clear to me if these approvals still hold. In particular, the wide-int.cc tests required a substantial rewrite; otherwise the tweaking could reasonably be described as "minor". Specifically, Jeff's reviews were: bitmap.c changes (as unittests/test-bitmap.c): https://gcc.gnu.org/ml/gcc-patches/2015-10/msg03284.html et-forest.c additions: https://gcc.gnu.org/ml/gcc-patches/2015-10/msg03295.html fold-const.c additions (as unittests/test-folding.c): https://gcc.gnu.org/ml/gcc-patches/2015-10/msg03305.html function-tests.c (as unittests/test-functions.c): https://gcc.gnu.org/ml/gcc-patches/2015-10/msg03310.html gimple.c additions (as unittests/test-gimple.c): https://gcc.gnu.org/ml/gcc-patches/2015-10/msg03304.html hash-map-tests.c (as unittests/test-hash-map.c): https://gcc.gnu.org/ml/gcc-patches/2015-10/msg03301.html hash-set-tests.c (as unittests/test-hash-set.c): https://gcc.gnu.org/ml/gcc-patches/2015-10/msg03300.html input.c additions (as unittests/test-locations.c): https://gcc.gnu.org/ml/gcc-patches/2015-10/msg03307.html rtl-tests.c (as unittests/test-rtl.c): https://gcc.gnu.org/ml/gcc-patches/2015-10/msg03302.html tree.c additions (as unittests/test-tree.c): https://gcc.gnu.org/ml/gcc-patches/2015-10/msg03303.html tree-cfg.c: add selftests https://gcc.gnu.org/ml/gcc-patches/2015-10/msg03285.html > Unless there's a good reason, drop the presumably redundant tests > and this is OK. Save preapprovald for these changes as the bitmap > patch. This version does remove the redundant tests. vec.c: add selftests (as unittests/test-vec.c): https://gcc.gnu.org/ml/gcc-patches/2015-10/msg03308.html wide-int.cc: add selftests: https://gcc.gnu.org/ml/gcc-patches/2015-10/msg03309.html The wide-int.cc tests required a substantial rewrite, since they are type-parametrized. I believe the only changes since my last round of testing have been: * the tweaks mentioned above * rebasing from r236397 (May 18th) to r237076 (today) * the addition of comments * squashing it into one patch I'm re-testing now to be sure (checked and release builds, bootstrap & regrtest, multi-config build for all in config-list.mk). Assuming the testing is OK, is this OK for trunk? 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 "selftests". (all.cross): Likewise. (selftests): New phony target. (s-selftests): New target. (selftests-gdb): New phony target. (COLLECT2_OBJS): Add selftest.o. * bitmap.c: Include "selftest.h". (test_gc_alloc): New function. (test_set_range): New function. (test_clear_bit_in_middle): New function. (test_copying): New function. (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_test_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". (test_single_node): New function. (test_simple_tree): New function. (test_disconnected_nodes): New function. (selftest::et_forest_c_tests): New function. * fold-const.c: Include "selftest.h". (assert_binop_folds_to_const): New function. (assert_binop_folds_to_nonlvalue): New function. (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". (verify_gimple_pp): New function. (test_assign_single): New function. (test_assign_binop): New function. (test_nop_stmt): New function. (test_return_stmt): New function. (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". (assert_loceq): New function. (test_accessing_ordinary_linemaps): New function. (test_unknown_location): New function. (test_builtins): New function. (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". (levenshtein_distance_unit_test_oneway): New function, adapted from testsuite/gcc.dg/plugin/levenshtein_plugin.c. (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". (test_integer_constants): New function. (test_identifiers): New function. (test_labels): New function. (selftest::tree_c_tests): New function. * tree-cfg.c: Include "selftest.h". (push_fndecl): New function. (test_linear_chain): New function. (test_diamond): New function. (test_fully_connected): New function. (selftest::tree_cfg_c_tests): New function. * vec.c: Include "selftest.h". (safe_push_range): New function. (test_quick_push): New function. (test_safe_push): New function. (test_truncate): New function. (test_safe_grow_cleared): New function. (test_pop): New function. (test_safe_insert): New function. (test_ordered_remove): New function. (test_unordered_remove): New function. (test_block_remove): New function. (reverse_cmp): New function. (test_qsort): New function. (selftest::vec_c_tests): New function.c. * wide-int.cc: Include selftest.h and wide-int-print.h. (from_int ): New function. (from_int ): New function. (from_int ): New function. (assert_deceq): New function. (assert_hexeq): New function. (test_printing ): New function template. (test_ops ): New function template. (test_comparisons ): New function template. (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. --- gcc/Makefile.in | 31 +- gcc/bitmap.c | 113 ++++ gcc/common.opt | 4 + gcc/diagnostic-show-locus.c | 167 ++++++ 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 | 118 ++++ 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/gcc.dg/plugin/levenshtein-test-1.c | 9 - gcc/testsuite/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 | 279 ++++++++++ gcc/tree.c | 62 +++ gcc/vec.c | 192 +++++++ gcc/wide-int.cc | 170 ++++++ 25 files changed, 2766 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/Makefile.in b/gcc/Makefile.in index 2d6f1e8..78a268f 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 selftests # 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 selftests @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-selftests" to ensure that we only run the selftests if the +# driver or cc1 change. +.PHONY: selftests +selftests: s-selftests +s-selftests: $(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: selftests-gdb +selftests-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 010cf75..7efd036 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 + +/* 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)); +} + +namespace selftest { + +/* 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 2bb576c..632dd31 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 eeccee5..0142e1f 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,124 @@ 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_test_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 +584,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 +1318,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_test_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 cd36752..67dbf35 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 + +/* 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); +} + +namespace selftest { + +/* 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 5058746..25af7ed 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 + +/* 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); +} + +namespace selftest { + +/* 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 0000000..e8bfb5c --- /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 + +/* 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. */ +} + +namespace selftest { + +/* 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 226b080..0a15628 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 @@ -3018,3 +3020,138 @@ maybe_remove_unused_call_args (struct function *fn, gimple *stmt) update_stmt_fn (fn, stmt); } } + +#if CHECKING_P + +/* 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)); +} + +namespace selftest { + +/* 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 0000000..663ec82 --- /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 + +/* 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)); +} + +namespace selftest { + +/* 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 0000000..1c7b7a1 --- /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 + +/* 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)); +} + +namespace selftest { + +/* 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 61b1e44..367901e 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,120 @@ dump_location_info (FILE *stream) dump_labelled_location_range (stream, "AD-HOC LOCATIONS", MAX_SOURCE_LOCATION + 1, UINT_MAX); } + +#if CHECKING_P + +/* 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"); +} + +namespace selftest { + +/* 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 0000000..a94ec5a --- /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 + +/* 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)); +} + +namespace selftest { + +/* 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 0000000..ab334aa --- /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 0000000..de804df --- /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 0000000..a1d3074 --- /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 e4e83a5..5ac5c60 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 + +/* 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); +} + +namespace selftest { + +/* 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/gcc.dg/plugin/levenshtein-test-1.c b/gcc/testsuite/gcc.dg/plugin/levenshtein-test-1.c deleted file mode 100644 index ac49992..0000000 --- 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 3e7dc78..0000000 --- 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 321b4ba..be2ac8d 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 230878e..543b8a3 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 0beb06e..06923cf 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 7fc24ba..ddf594d 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,281 @@ gt_pch_nx (edge_def *e, gt_pointer_operator op, void *cookie) op (&(e->insns.r), cookie); op (&(block), cookie); } + +#if CHECKING_P + +/* 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, /* return_type */ + 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 (); +} + +namespace selftest { + +/* 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 2e01eac..4a45276 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 + +/* 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)); +} + +namespace selftest { + +/* 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 a483d5b..ef38f6ab 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 + +/* 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 ()); +} + +namespace selftest { + +/* 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 8648e7d..310b3df 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 + +/* 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 (); +} + +namespace selftest { + +/* 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 */