Address spaces are target-specific, meaning that any test for address
spaces must be target-specific, meaning that they're abnormally
susceptible to bitrot.  This patch aims to help fix that, by providing a
helper that can (and, in the forthcoming C++ Named Address Spaces patch,
does) help write target-independent self-test for address-space related
logic.  It contains enough address spaces with various relationships to
one-another to exercise all cases.

The helper should revert the compiler state to how it was when it
started, i.e. its effects should not contaminate other self-tests.

gcc/c-family/ChangeLog:

        * c-common.h (c_register_addr_space): Add comment about the
        correlation with address_space_test.
        (selftest::addr_space_test): New class.  RAII-style
        helper for setting up target-independent address space support
        state.
        * c-common.cc (selftest::addr_space_test::addr_space_test):
        Implement.
        (selftest::addr_space_test::~addr_space_test): Ditto.
---
 gcc/c-family/c-common.cc | 97 ++++++++++++++++++++++++++++++++++++++++
 gcc/c-family/c-common.h  | 46 +++++++++++++++++++
 2 files changed, 143 insertions(+)

diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index e05551e51192..89d9ea75bfdc 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -20,6 +20,10 @@ along with GCC; see the file COPYING3.  If not see
 #define GCC_C_COMMON_C
 
 #include "config.h"
+#if CHECKING_P
+# define INCLUDE_ALGORITHM
+# define INCLUDE_ITERATOR
+#endif
 #include "system.h"
 #include "coretypes.h"
 #include "target.h"
@@ -2930,6 +2934,9 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT 
width, int unsignedp)
 void
 c_register_addr_space (const char *word, addr_space_t as)
 {
+  /* Note that selftest::addr_space_test below must perform the opposite of
+     this logic, and ergo it has an inverse of this function.  Keep in mind
+     when modifying.  */
   int rid = RID_FIRST_ADDR_SPACE + as;
   tree id;
 
@@ -10714,4 +10721,94 @@ c_hardbool_type_attr_1 (tree type, tree *false_value, 
tree *true_value)
   return attr;
 }
 
+#if CHECKING_P
+selftest::addr_space_test::addr_space_test ()
+{
+  /* c_register_addr_space is a no-op if this condition is met.  */
+  gcc_assert (!(c_dialect_objc () || flag_no_asm));
+
+  /* Save the old keywords.  */
+  std::copy (&ridpointers[RID_FIRST_ADDR_SPACE],
+            &ridpointers[RID_LAST_ADDR_SPACE + 1],
+            this->old_ridpointers);
+
+  /* Un-register them.  */
+  for (auto rid : this->old_ridpointers)
+    {
+      if (!rid)
+       continue;
+
+      C_SET_RID_CODE (rid, 0);
+      c_unmake_keyword (rid);
+    }
+
+  std::fill (&ridpointers[RID_FIRST_ADDR_SPACE],
+            &ridpointers[RID_LAST_ADDR_SPACE + 1],
+            nullptr);
+  c_register_addr_space ("__as0", 0);
+  c_register_addr_space ("__as1", 1);
+  c_register_addr_space ("__as2", 2);
+  c_register_addr_space ("__as3", 3);
+  c_register_addr_space ("__as4", 4);
+  c_register_addr_space ("__as5", 5);
+
+  /* We don't fully replace all the contents of the below, just enough for
+     tests.  */
+  this->old_addr_space_hooks = targetm.addr_space;
+  targetm.addr_space.subset_p = [] (addr_space_t sub, addr_space_t super)
+  {
+    /* Each set is its own (biggest) subset.  */
+    if (super == sub)
+      return true;
+
+    /* 0 is the superset of all other address spaces, except for 4 and 5.  */
+    if (super == 0 && sub != 4 && sub != 5)
+      return true;
+
+    /* ... because 4 contains 5 (but not vice-versa!).  */
+    if (super == 4 && sub == 5)
+      return true;
+
+    /* 2 and 3 overlap.  */
+    if ((super == 3 && sub == 2)
+       || (super == 2 && sub == 3))
+      return true;
+    return false;
+  };
+  targetm.addr_space.address_mode
+    = targetm.addr_space.pointer_mode
+    = [] (addr_space_t)
+    { return Pmode; };
+}
+
+selftest::addr_space_test::~addr_space_test ()
+{
+  for (size_t i = RID_FIRST_ADDR_SPACE; i <= RID_LAST_ADDR_SPACE; i++)
+    {
+      tree id = ridpointers[i];
+      if (!id)
+       continue;
+
+      /* Reverse the effects of c_register_addr_space.  */
+      C_SET_RID_CODE (id, 0);
+      c_unmake_keyword (id);
+    }
+
+  /* Restore the old keywords.  */
+  std::copy (std::begin (this->old_ridpointers), std::end 
(this->old_ridpointers),
+            &ridpointers[RID_FIRST_ADDR_SPACE]);
+  for (size_t i = RID_FIRST_ADDR_SPACE; i <= RID_LAST_ADDR_SPACE; i++)
+    {
+      tree id = ridpointers[i];
+      if (!id)
+       continue;
+
+      C_SET_RID_CODE (id, i);
+      c_make_keyword (id);
+    }
+
+  targetm.addr_space = this->old_addr_space_hooks;
+}
+#endif
+
 #include "gt-c-family-c-common.h"
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 569edc064014..e027b24d2ca2 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1763,6 +1763,52 @@ namespace selftest {
 
   /* The entrypoint for running all of the above tests.  */
   extern void c_family_tests (void);
+
+  /* Declarations for utilities.  */
+  /* A class that sets up various hooks for address-space testing.
+
+     Namely, it creates a situation where the generic address space is the
+     superset of 1, 2, and 3, address spaces 1 and 2 are disjoint, address
+     spaces 2 and 3 overlap fully, and address space 4 is distinct from all,
+     except for 5, which is a proper subset of it.
+
+     In the table below, if the column label is a superset of the row label,
+     the field is marked with X.
+
+     | /   | AS0 | AS1 | AS2 | AS3 | AS4 | AS5 |
+     |-----+-----+-----+-----+-----|-----|-----|
+     | AS0 |  X  |     |     |     |     |     |
+     | AS1 |  X  |  X  |     |     |     |     |
+     | AS2 |  X  |     |  X  |  X  |     |     |
+     | AS3 |  X  |     |  X  |  X  |     |     |
+     | AS4 |     |     |     |     |  X  |     |
+     | AS5 |     |     |     |     |  X  |  X  |
+
+     The address spaces can be visualized also as:
+
+     |---------AS0---------| |---AS4---|
+     |  |-AS1-|   |-AS2-|  | | |-AS5-| |
+     |  |     |   |-AS3-|  | | |     | |
+  */
+
+  class addr_space_test
+  {
+    /* A copy of the addr_space related RID pointers.  We'll register a few of
+       our own instead.  */
+    tree old_ridpointers[RID_LAST_ADDR_SPACE - RID_FIRST_ADDR_SPACE + 1];
+    /* Previous addr_space hooks.  */
+    decltype (targetm.addr_space) old_addr_space_hooks;
+  public:
+    addr_space_test ();
+    ~addr_space_test ();
+
+    /* Immobilize this object.  */
+    addr_space_test (const addr_space_test&) = delete;
+    addr_space_test (addr_space_test&&) = delete;
+    addr_space_test &operator= (const addr_space_test&) = delete;
+    addr_space_test &operator= (addr_space_test&&) = delete;
+  };
+
 } // namespace selftest
 #endif /* #if CHECKING_P */
 
-- 
2.55.0

Reply via email to