This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH 01/16] Core of selftest framework (v6)
- From: David Malcolm <dmalcolm at redhat dot com>
- To: gcc-patches at gcc dot gnu dot org
- Cc: Bernd Schmidt <bschmidt at redhat dot com>, Jeff Law <law at redhat dot com>, David Malcolm <dmalcolm at redhat dot com>
- Date: Thu, 2 Jun 2016 17:06:50 -0400
- Subject: [PATCH 01/16] Core of selftest framework (v6)
- Authentication-results: sourceware.org; auth=none
- References: <1464874868 dot 11895 dot 41 dot camel at redhat dot com> <1464901625-54547-1-git-send-email-dmalcolm at redhat dot com>
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