diff mbox

[01/16] Core of selftest framework (v6)

Message ID 1464901625-54547-2-git-send-email-dmalcolm@redhat.com
State New
Headers show

Commit Message

David Malcolm June 2, 2016, 9:06 p.m. UTC
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

Comments

Bernd Schmidt June 2, 2016, 11:21 p.m. UTC | #1
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.

> +  fprintf (stderr,
> +	   "%s:%i: FAIL: %s\n",
> +	   file, line, msg);
> +  /* TODO: add calling function name as well?  */
> +  abort ();
> +}

That'll fit on one line. 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, and please ensure every 
step you commit actually compiles (this patch alone won't). Let me know 
which patches still need approval after that.


Bernd
diff mbox

Patch

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;
 };