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.
---
 gcc/Makefile.in          |  31 ++++++++--
 gcc/common.opt           |   4 ++
 gcc/selftest-run-tests.c |  76 ++++++++++++++++++++++++
 gcc/selftest.c           |  49 +++++++++++++++
 gcc/selftest.h           | 152 +++++++++++++++++++++++++++++++++++++++++++++++
 gcc/toplev.c             |  26 ++++++++
 gcc/toplev.h             |   2 +
 7 files changed, 335 insertions(+), 5 deletions(-)
 create mode 100644 gcc/selftest-run-tests.c
 create mode 100644 gcc/selftest.c
 create mode 100644 gcc/selftest.h

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 673f87d..2c5faa3 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
+       hash-table.o file-find.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/common.opt b/gcc/common.opt
index 682cb41..10a10ed 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -2057,6 +2057,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/selftest-run-tests.c b/gcc/selftest-run-tests.c
new file mode 100644
index 0000000..4233351
--- /dev/null
+++ b/gcc/selftest-run-tests.c
@@ -0,0 +1,76 @@
+/* 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
+<http://www.gnu.org/licenses/>.  */
+
+#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 ();
+
+  /* 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..cc921c8
--- /dev/null
+++ b/gcc/selftest.c
@@ -0,0 +1,49 @@
+/* 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
+<http://www.gnu.org/licenses/>.  */
+
+#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..2062a8b
--- /dev/null
+++ b/gcc/selftest.h
@@ -0,0 +1,152 @@
+/* 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
+<http://www.gnu.org/licenses/>.  */
+
+#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 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/toplev.c b/gcc/toplev.c
index 580c03a..795818a 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;
 };
-- 
1.8.5.3

Reply via email to