diff mbox series

[COMMITTED,v2] pru: Named address space for R30/R31 I/O access

Message ID 20210925143823.1535798-1-dimitar@dinux.eu
State Committed
Commit 8bafc9640f353ff11c0535a03096fb4db9a5bb95
Headers show
Series [COMMITTED,v2] pru: Named address space for R30/R31 I/O access | expand

Commit Message

Dimitar Dimitrov Sept. 25, 2021, 2:38 p.m. UTC
I have committed the below patch. I've made only cosmetic updates since
version 1 (https://gcc.gnu.org/pipermail/gcc-patches/2021-September/579420.html).

The PRU architecture provides single-cycle access to GPIO pins via
special designated CPU registers - R30 and R31. These two registers can
of course be accessed in C code using inline assembly, but that can be
intimidating to users.

The TI proprietary compiler [1] can expose these I/O registers as global
volatile registers:
  volatile register unsigned int __R31;

Consequently, accessing them in user programs is as straightforward as
using a regular global variable:
  __R31 |= (1 << 2);

Unfortunately, global volatile registers are not supported by GCC [2].
I decided to implement convenient access to __R30 and __R31 using a new
named address space:
  extern volatile __regio_symbol unsigned int __R30;
Unlike global registers, volatile global memory variables are well
supported in GCC.  Memory writes and reads to the __regio_symbol address
space are converted to writes and reads to R30 and R31 CPU registers.
The declared variable name determines which of the two registers it is
representing.

With an ifdef for the __R30/__R31 declarations, user programs can now
be source-compatible with both TI and GCC toolchains.

[1] https://www.ti.com/lit/ug/spruhv7c/spruhv7c.pdf , "Global Register Variables"
[2] https://gcc.gnu.org/ml/gcc-patches/2015-01/msg02241.html

gcc/ChangeLog:

	* config/pru/constraints.md (Rrio): New constraint.
	* config/pru/predicates.md (regio_operand): New predicate.
	* config/pru/pru-pragma.c (pru_register_pragmas): Register
	the __regio_symbol address space.
	* config/pru/pru-protos.h (pru_symref2ioregno): Declaration.
	* config/pru/pru.c (pru_symref2ioregno): New helper function.
	(pru_legitimate_address_p): Remove.
	(pru_addr_space_legitimate_address_p): Use the address space
	aware hook variant.
	(pru_nongeneric_pointer_addrspace): New helper function.
	(pru_insert_attributes): New function to validate __regio_symbol
	usage.
	(TARGET_INSERT_ATTRIBUTES): New macro.
	(TARGET_LEGITIMATE_ADDRESS_P): Remove.
	(TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P): New macro.
	* config/pru/pru.h (enum reg_class): Add REGIO_REGS class.
	* config/pru/pru.md (*regio_readsi): New pattern to read I/O
	registers.
	(*regio_nozext_writesi): New pattern to write to I/O registers.
	(*regio_zext_write_r30<EQS0:mode>): Ditto.
	* doc/extend.texi: Document the new PRU Named Address Space.

gcc/testsuite/ChangeLog:

	* gcc.target/pru/regio-as-pointer.c: New negative test.
	* gcc.target/pru/regio-as-pointer-2.c: New negative test.
	* gcc.target/pru/regio-decl-2.c: New negative test.
	* gcc.target/pru/regio-decl-3.c: New negative test.
	* gcc.target/pru/regio-decl-4.c: New negative test.
	* gcc.target/pru/regio-decl.c: New negative test.
	* gcc.target/pru/regio-di.c: New negative test.
	* gcc.target/pru/regio-hi.c: New negative test.
	* gcc.target/pru/regio-qi.c: New negative test.
	* gcc.target/pru/regio.c: New test.
	* gcc.target/pru/regio.h: New helper header.

Signed-off-by: Dimitar Dimitrov <dimitar@dinux.eu>
---
 gcc/config/pru/constraints.md                 |   5 +
 gcc/config/pru/predicates.md                  |  19 +++
 gcc/config/pru/pru-pragma.c                   |   2 +
 gcc/config/pru/pru-protos.h                   |   3 +
 gcc/config/pru/pru.c                          | 156 +++++++++++++++++-
 gcc/config/pru/pru.h                          |   5 +
 gcc/config/pru/pru.md                         | 102 +++++++++++-
 gcc/doc/extend.texi                           |  19 ++-
 .../gcc.target/pru/regio-as-pointer-2.c       |  11 ++
 .../gcc.target/pru/regio-as-pointer.c         |  11 ++
 gcc/testsuite/gcc.target/pru/regio-decl-2.c   |  13 ++
 gcc/testsuite/gcc.target/pru/regio-decl-3.c   |  19 +++
 gcc/testsuite/gcc.target/pru/regio-decl-4.c   |  17 ++
 gcc/testsuite/gcc.target/pru/regio-decl.c     |  15 ++
 gcc/testsuite/gcc.target/pru/regio-di.c       |   9 +
 gcc/testsuite/gcc.target/pru/regio-hi.c       |   9 +
 gcc/testsuite/gcc.target/pru/regio-qi.c       |   9 +
 gcc/testsuite/gcc.target/pru/regio.c          |  58 +++++++
 gcc/testsuite/gcc.target/pru/regio.h          |   7 +
 19 files changed, 478 insertions(+), 11 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/pru/regio-as-pointer-2.c
 create mode 100644 gcc/testsuite/gcc.target/pru/regio-as-pointer.c
 create mode 100644 gcc/testsuite/gcc.target/pru/regio-decl-2.c
 create mode 100644 gcc/testsuite/gcc.target/pru/regio-decl-3.c
 create mode 100644 gcc/testsuite/gcc.target/pru/regio-decl-4.c
 create mode 100644 gcc/testsuite/gcc.target/pru/regio-decl.c
 create mode 100644 gcc/testsuite/gcc.target/pru/regio-di.c
 create mode 100644 gcc/testsuite/gcc.target/pru/regio-hi.c
 create mode 100644 gcc/testsuite/gcc.target/pru/regio-qi.c
 create mode 100644 gcc/testsuite/gcc.target/pru/regio.c
 create mode 100644 gcc/testsuite/gcc.target/pru/regio.h
diff mbox series

Patch

diff --git a/gcc/config/pru/constraints.md b/gcc/config/pru/constraints.md
index a31ae9318b8..1e0e703c394 100644
--- a/gcc/config/pru/constraints.md
+++ b/gcc/config/pru/constraints.md
@@ -34,6 +34,7 @@ 
 ;; The following constraints are intended for internal use only:
 ;;  Rmd0, Rms0, Rms1: Registers for MUL instruction operands.
 ;;  Rsib: Jump address register suitable for sibling calls.
+;;  Rrio: The R30 and R31 I/O registers.
 ;;  M: -255 to 0 (for converting ADD to SUB with suitable UBYTE OP2).
 ;;  N: -32768 to 32767 (16-bit signed integer).
 ;;  O: -128 to 127 (8-bit signed integer).
@@ -57,6 +58,10 @@  (define_register_constraint "Rms1" "MULSRC1_REGS"
   "@internal
   The multiply source 1 register.")
 
+(define_register_constraint "Rrio" "REGIO_REGS"
+  "@internal
+  The R30 and R31 I/O registers.")
+
 ;; Integer constraints.
 
 (define_constraint "I"
diff --git a/gcc/config/pru/predicates.md b/gcc/config/pru/predicates.md
index 469002f0567..1a4b98eb216 100644
--- a/gcc/config/pru/predicates.md
+++ b/gcc/config/pru/predicates.md
@@ -121,6 +121,25 @@  (define_predicate "pru_mulsrc1_operand"
   return 0;
 })
 
+(define_predicate "regio_operand"
+  (match_code "subreg,reg")
+{
+  if (register_operand (op, mode))
+    {
+      int regno;
+
+      if (REG_P (op))
+	regno = REGNO (op);
+      else if (GET_CODE (op) == SUBREG && REG_P (SUBREG_REG (op)))
+	regno = REGNO (SUBREG_REG (op));
+      else
+	return 0;
+
+      return REGNO_REG_CLASS (regno) == REGIO_REGS;
+    }
+  return 0;
+})
+
 (define_predicate "reg_or_const_int_operand"
   (ior (match_operand 0 "const_int_operand")
        (match_operand 0 "register_operand")))
diff --git a/gcc/config/pru/pru-pragma.c b/gcc/config/pru/pru-pragma.c
index 01d076101f1..3beec236be1 100644
--- a/gcc/config/pru/pru-pragma.c
+++ b/gcc/config/pru/pru-pragma.c
@@ -83,4 +83,6 @@  pru_register_pragmas (void)
 {
   c_register_pragma (NULL, "ctable_entry", pru_pragma_ctable_entry);
   c_register_pragma (NULL, "CTABLE_ENTRY", pru_pragma_ctable_entry);
+
+  c_register_addr_space ("__regio_symbol", ADDR_SPACE_REGIO);
 }
diff --git a/gcc/config/pru/pru-protos.h b/gcc/config/pru/pru-protos.h
index 74129e9b9ed..031ea9e2fab 100644
--- a/gcc/config/pru/pru-protos.h
+++ b/gcc/config/pru/pru-protos.h
@@ -62,7 +62,10 @@  extern int pru_get_ctable_exact_base_index (unsigned HOST_WIDE_INT caddr);
 extern int pru_get_ctable_base_index (unsigned HOST_WIDE_INT caddr);
 extern int pru_get_ctable_base_offset (unsigned HOST_WIDE_INT caddr);
 
+extern int pru_symref2ioregno (rtx op);
+
 extern void pru_register_abicheck_pass (void);
+
 #endif /* RTX_CODE */
 
 #ifdef TREE_CODE
diff --git a/gcc/config/pru/pru.c b/gcc/config/pru/pru.c
index 30d0da194ce..9f264b4698d 100644
--- a/gcc/config/pru/pru.c
+++ b/gcc/config/pru/pru.c
@@ -1403,11 +1403,42 @@  pru_valid_addr_expr_p (machine_mode mode, rtx base, rtx offset, bool strict_p)
     return false;
 }
 
-/* Implement TARGET_LEGITIMATE_ADDRESS_P.  */
+/* Return register number (either for r30 or r31) which maps to the
+   corresponding symbol OP's name in the __regio_symbol address namespace.
+
+   If no mapping can be established (i.e. symbol name is invalid), then
+   return -1.  */
+int pru_symref2ioregno (rtx op)
+{
+  if (!SYMBOL_REF_P (op))
+    return -1;
+
+  const char *name = XSTR (op, 0);
+  if (!strcmp (name, "__R30"))
+    return R30_REGNUM;
+  else if (!strcmp (name, "__R31"))
+    return R31_REGNUM;
+  else
+    return -1;
+}
+
+/* Implement TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P.  */
 static bool
-pru_legitimate_address_p (machine_mode mode,
-			    rtx operand, bool strict_p)
+pru_addr_space_legitimate_address_p (machine_mode mode, rtx operand,
+				     bool strict_p, addr_space_t as)
 {
+  if (as == ADDR_SPACE_REGIO)
+    {
+      /*  Address space constraints for __regio_symbol have been checked in
+	  TARGET_INSERT_ATTRIBUTES, and some more checks will be done
+	  during RTL expansion of "mov<mode>".  */
+      return true;
+    }
+  else if (as != ADDR_SPACE_GENERIC)
+    {
+      gcc_unreachable ();
+    }
+
   switch (GET_CODE (operand))
     {
     /* Direct.  */
@@ -2002,6 +2033,117 @@  pru_file_start (void)
      need to confuse users with this warning.  */
   fprintf (asm_out_file, "\t.set no_warn_regname_label\n");
 }
+
+/* Scan type TYP for pointer references to address space other than
+   ADDR_SPACE_GENERIC.  Return true if such reference is found.
+   Much of this code was taken from the avr port.  */
+
+static bool
+pru_nongeneric_pointer_addrspace (tree typ)
+{
+  while (ARRAY_TYPE == TREE_CODE (typ))
+    typ = TREE_TYPE (typ);
+
+  if (POINTER_TYPE_P (typ))
+    {
+      addr_space_t as;
+      tree target = TREE_TYPE (typ);
+
+      /* Pointer to function: Test the function's return type.  */
+      if (FUNCTION_TYPE == TREE_CODE (target))
+	return pru_nongeneric_pointer_addrspace (TREE_TYPE (target));
+
+      /* "Ordinary" pointers... */
+
+      while (TREE_CODE (target) == ARRAY_TYPE)
+	target = TREE_TYPE (target);
+
+      as = TYPE_ADDR_SPACE (target);
+
+      if (!ADDR_SPACE_GENERIC_P (as))
+	return true;
+
+      /* Scan pointer's target type.  */
+      return pru_nongeneric_pointer_addrspace (target);
+    }
+
+  return false;
+}
+
+/* Implement `TARGET_INSERT_ATTRIBUTES'.  For PRU it's used as a hook to
+   provide better diagnostics for some invalid usages of the __regio_symbol
+   address space.
+
+   Any escapes of the following checks are supposed to be caught
+   during the "mov<mode>" pattern expansion.  */
+
+static void
+pru_insert_attributes (tree node, tree *attributes ATTRIBUTE_UNUSED)
+{
+
+  /* Validate __regio_symbol variable declarations.  */
+  if (VAR_P (node))
+    {
+      const char *name = DECL_NAME (node)
+			  ? IDENTIFIER_POINTER (DECL_NAME (node))
+			  : "<unknown>";
+      tree typ = TREE_TYPE (node);
+      addr_space_t as = TYPE_ADDR_SPACE (typ);
+
+      if (as == ADDR_SPACE_GENERIC)
+	return;
+
+      if (AGGREGATE_TYPE_P (typ))
+	{
+	  error ("aggregate types are prohibited in "
+		 "%<__regio_symbol%> address space");
+	  /* Don't bother anymore.  Below checks would pile
+	     meaningless errors, which would confuse user.  */
+	  return;
+	}
+      if (DECL_INITIAL (node) != NULL_TREE)
+	error ("variables in %<__regio_symbol%> address space "
+	       "cannot have initial value");
+      if (DECL_REGISTER (node))
+	error ("variables in %<__regio_symbol%> address space "
+	       "cannot be declared %<register%>");
+      if (!TYPE_VOLATILE (typ))
+	error ("variables in %<__regio_symbol%> address space "
+	       "must be declared %<volatile%>");
+      if (!DECL_EXTERNAL (node))
+	error ("variables in %<__regio_symbol%> address space "
+	       "must be declared %<extern%>");
+      if (TYPE_MODE (typ) != SImode)
+	error ("only 32-bit access is supported "
+	       "for %<__regio_symbol%> address space");
+      if (strcmp (name, "__R30") != 0 && strcmp (name, "__R31") != 0)
+	error ("register name %<%s%> not recognized "
+	       "in %<__regio_symbol%> address space", name);
+    }
+
+  tree typ = NULL_TREE;
+
+  switch (TREE_CODE (node))
+    {
+    case FUNCTION_DECL:
+      typ = TREE_TYPE (TREE_TYPE (node));
+      break;
+    case TYPE_DECL:
+    case RESULT_DECL:
+    case VAR_DECL:
+    case FIELD_DECL:
+    case PARM_DECL:
+      typ = TREE_TYPE (node);
+      break;
+    case POINTER_TYPE:
+      typ = node;
+      break;
+    default:
+      break;
+    }
+  if (typ != NULL_TREE && pru_nongeneric_pointer_addrspace (typ))
+    error ("pointers to %<__regio_symbol%> address space are prohibited");
+}
 
 /* Function argument related.  */
 
@@ -2933,6 +3075,9 @@  pru_unwind_word_mode (void)
 #undef TARGET_ASM_FILE_START
 #define TARGET_ASM_FILE_START pru_file_start
 
+#undef  TARGET_INSERT_ATTRIBUTES
+#define TARGET_INSERT_ATTRIBUTES pru_insert_attributes
+
 #undef TARGET_INIT_BUILTINS
 #define TARGET_INIT_BUILTINS pru_init_builtins
 #undef TARGET_EXPAND_BUILTIN
@@ -2979,8 +3124,9 @@  pru_unwind_word_mode (void)
 #undef TARGET_MUST_PASS_IN_STACK
 #define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size
 
-#undef TARGET_LEGITIMATE_ADDRESS_P
-#define TARGET_LEGITIMATE_ADDRESS_P pru_legitimate_address_p
+#undef TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P
+#define TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P \
+  pru_addr_space_legitimate_address_p
 
 #undef TARGET_INIT_LIBFUNCS
 #define TARGET_INIT_LIBFUNCS pru_init_libfuncs
diff --git a/gcc/config/pru/pru.h b/gcc/config/pru/pru.h
index 9b6be323e6d..03f08b1720f 100644
--- a/gcc/config/pru/pru.h
+++ b/gcc/config/pru/pru.h
@@ -215,6 +215,7 @@  enum reg_class
   MULDST_REGS,
   MULSRC0_REGS,
   MULSRC1_REGS,
+  REGIO_REGS,
   GP_REGS,
   ALL_REGS,
   LIM_REG_CLASSES
@@ -229,6 +230,7 @@  enum reg_class
      "MULDST_REGS",	  \
      "MULSRC0_REGS",	  \
      "MULSRC1_REGS",	  \
+     "REGIO_REGS",	  \
      "GP_REGS",		  \
      "ALL_REGS" }
 
@@ -242,6 +244,7 @@  enum reg_class
     /* MULDST_REGS    */ { 0, 0, 0, 0x00000f00, 0},		\
     /* MULSRC0_REGS   */ { 0, 0, 0, 0x000f0000, 0},		\
     /* MULSRC1_REGS   */ { 0, 0, 0, 0x00f00000, 0},		\
+    /* REGIO_REGS     */ { 0, 0, 0, 0xff000000, 0},		\
     /* GP_REGS	      */ { ~0, ~0, ~0, ~0, 0},			\
     /* ALL_REGS	      */ { ~0,~0, ~0, ~0, ~0}			\
   }
@@ -252,6 +255,8 @@  enum reg_class
 	((REGNO) == MULDST_REGNUM ? MULDST_REGS				    \
 	 : (REGNO) == MULSRC0_REGNUM ? MULSRC0_REGS			    \
 	 : (REGNO) == MULSRC1_REGNUM ? MULSRC1_REGS			    \
+	 : (REGNO) == R30_REGNUM ? REGIO_REGS				    \
+	 : (REGNO) == R31_REGNUM ? REGIO_REGS				    \
 	 : (REGNO) >= FIRST_ARG_REGNUM					    \
 	    && (REGNO) <= LAST_ARG_REGNUM ? SIB_REGS			    \
 	 : (REGNO) == STATIC_CHAIN_REGNUM ? SIB_REGS			    \
diff --git a/gcc/config/pru/pru.md b/gcc/config/pru/pru.md
index e6cfa8ec3bf..c0ded8ea4e5 100644
--- a/gcc/config/pru/pru.md
+++ b/gcc/config/pru/pru.md
@@ -36,6 +36,8 @@  (define_constants
    (MULSRC0_REGNUM		112) ; Multiply source register.
    (MULSRC1_REGNUM		116) ; Multiply source register.
    (LAST_NONIO_GP_REGNUM	119) ; Last non-I/O general purpose register.
+   (R30_REGNUM			120) ; R30 I/O register.
+   (R31_REGNUM			124) ; R31 I/O register.
    (LOOPCNTR_REGNUM		128) ; internal LOOP counter register
    (LAST_GP_REGNUM		132) ; Last general purpose register.
 
@@ -49,6 +51,13 @@  (define_constants
   ]
 )
 
+;; Enumerate address spaces.
+(define_constants
+  [
+   (ADDR_SPACE_REGIO		1) ; Access to R30 and R31 I/O registers.
+  ]
+)
+
 ;; Enumeration of UNSPECs.
 
 (define_c_enum "unspec" [
@@ -68,6 +77,9 @@  (define_c_enum "unspecv" [
   UNSPECV_HALT
 
   UNSPECV_BLOCKAGE
+
+  UNSPECV_REGIO_READ
+  UNSPECV_REGIO_WRITE
 ])
 
 ; Length of an instruction (in bytes).
@@ -129,11 +141,62 @@  (define_expand "mov<mode>"
 	(match_operand:MOV8_16_32 1 "general_operand"))]
   ""
 {
-  /* It helps to split constant loading and memory access
-     early, so that the LDI/LDI32 instructions can be hoisted
-     outside a loop body.  */
-  if (MEM_P (operands[0]))
-    operands[1] = force_reg (<MODE>mode, operands[1]);
+  if (MEM_P (operands[0])
+      && MEM_ADDR_SPACE (operands[0]) == ADDR_SPACE_REGIO)
+
+    {
+      /* Intercept writes to the SImode register I/O "address space".  */
+      gcc_assert (<MODE>mode == SImode);
+
+      if (!SYMBOL_REF_P (XEXP (operands[0], 0)))
+	{
+	  error ("invalid access to %<__regio_symbol%> address space");
+	  FAIL;
+	}
+
+      if (!REG_P (operands[1]))
+	operands[1] = force_reg (<MODE>mode, operands[1]);
+
+      int regiono = pru_symref2ioregno (XEXP (operands[0], 0));
+      gcc_assert (regiono >= 0);
+      rtx regio = gen_rtx_REG (<MODE>mode, regiono);
+      rtx unspecv = gen_rtx_UNSPEC_VOLATILE (<MODE>mode,
+					     gen_rtvec (1, operands[1]),
+					     UNSPECV_REGIO_WRITE);
+      emit_insn (gen_rtx_SET (regio, unspecv));
+      DONE;
+    }
+  else if (MEM_P (operands[1])
+	   && MEM_ADDR_SPACE (operands[1]) == ADDR_SPACE_REGIO)
+    {
+      /* Intercept reads from the SImode register I/O "address space".  */
+      gcc_assert (<MODE>mode == SImode);
+
+      if (!SYMBOL_REF_P (XEXP (operands[1], 0)))
+	{
+	  error ("invalid access to %<__regio_symbol%> address space");
+	  FAIL;
+	}
+
+      if (MEM_P (operands[0]))
+	operands[0] = force_reg (<MODE>mode, operands[0]);
+
+      int regiono = pru_symref2ioregno (XEXP (operands[1], 0));
+      gcc_assert (regiono >= 0);
+      rtx regio = gen_rtx_REG (<MODE>mode, regiono);
+      rtx unspecv = gen_rtx_UNSPEC_VOLATILE (<MODE>mode,
+					     gen_rtvec (1, regio),
+					     UNSPECV_REGIO_READ);
+      emit_insn (gen_rtx_SET (operands[0], unspecv));
+      DONE;
+    }
+  else if (MEM_P (operands[0]))
+    {
+    /* It helps to split constant loading and memory access
+       early, so that the LDI/LDI32 instructions can be hoisted
+       outside a loop body.  */
+      operands[1] = force_reg (<MODE>mode, operands[1]);
+    }
 })
 
 ;; Keep a single pattern for 32 bit MOV operations.  LRA requires that the
@@ -546,6 +609,35 @@  (define_insn "ashr<mode>3_single"
 
 (include "alu-zext.md")
 
+;; Patterns for accessing the R30/R31 I/O registers.
+
+(define_insn "*regio_readsi"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+    (unspec_volatile:SI
+      [(match_operand:SI 1 "regio_operand" "Rrio")]
+      UNSPECV_REGIO_READ))]
+  ""
+  "mov\\t%0, %1"
+  [(set_attr "type"     "alu")])
+
+(define_insn "*regio_nozext_writesi"
+  [(set (match_operand:SI 0 "regio_operand" "=Rrio")
+    (unspec_volatile:SI
+      [(match_operand:SI 1 "register_operand" "r")]
+      UNSPECV_REGIO_WRITE))]
+  ""
+  "mov\\t%0, %1"
+  [(set_attr "type"     "alu")])
+
+(define_insn "*regio_zext_write_r30<EQS0:mode>"
+  [(set (match_operand:SI 0 "regio_operand" "=Rrio")
+    (unspec_volatile:SI
+      [(zero_extend:SI (match_operand:EQS0 1 "register_operand" "r"))]
+      UNSPECV_REGIO_WRITE))]
+  ""
+  "mov\\t%0, %1"
+  [(set_attr "type"     "alu")])
+
 ;; DI logical ops could be automatically split into WORD-mode ops in
 ;; expand_binop().  But then we'll miss an opportunity to use SI mode
 ;; operations, since WORD mode for PRU is QI.
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 9501a60f20e..63be63f091a 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -1406,7 +1406,7 @@  As an extension, GNU C supports named address spaces as
 defined in the N1275 draft of ISO/IEC DTR 18037.  Support for named
 address spaces in GCC will evolve as the draft technical report
 changes.  Calling conventions for any target might also change.  At
-present, only the AVR, M32C, RL78, and x86 targets support
+present, only the AVR, M32C, PRU, RL78, and x86 targets support
 address spaces other than the generic address space.
 
 Address space identifiers may be used exactly like any other C type
@@ -1586,6 +1586,23 @@  order to access memory beyond the first 64@tie{}Ki bytes.  If
 @code{__far} is used with the M32CM or M32C CPU variants, it has no
 effect.
 
+@subsection PRU Named Address Spaces
+@cindex @code{__regio_symbol} PRU Named Address Spaces
+
+On the PRU target, variables qualified with @code{__regio_symbol} are
+aliases used to access the special I/O CPU registers.  They must be
+declared as @code{extern} because such variables will not be allocated in
+any data memory.  They must also be marked as @code{volatile}, and can
+only be 32-bit integer types.  The only names those variables can have
+are @code{__R30} and @code{__R31}, representing respectively the
+@code{R30} and @code{R31} special I/O CPU registers.  Hence the following
+example is the only valid usage of @code{__regio_symbol}:
+
+@smallexample
+extern volatile __regio_symbol uint32_t __R30;
+extern volatile __regio_symbol uint32_t __R31;
+@end smallexample
+
 @subsection RL78 Named Address Spaces
 @cindex @code{__far} RL78 Named Address Spaces
 
diff --git a/gcc/testsuite/gcc.target/pru/regio-as-pointer-2.c b/gcc/testsuite/gcc.target/pru/regio-as-pointer-2.c
new file mode 100644
index 00000000000..06d9473e995
--- /dev/null
+++ b/gcc/testsuite/gcc.target/pru/regio-as-pointer-2.c
@@ -0,0 +1,11 @@ 
+/* Test __regio_symbol invalid attempt to get regio variable address.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+#include "regio.h"
+
+uint32_t test(void)
+{
+  return *(&__R30+1); /* { dg-error "invalid access to '__regio_symbol' address space" } */
+}
diff --git a/gcc/testsuite/gcc.target/pru/regio-as-pointer.c b/gcc/testsuite/gcc.target/pru/regio-as-pointer.c
new file mode 100644
index 00000000000..885464f498d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/pru/regio-as-pointer.c
@@ -0,0 +1,11 @@ 
+/* Test __regio_symbol invalid attempt to get regio variable address.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+#include "regio.h"
+
+uint32_t *test(void)
+{
+  return &__R31; /* { dg-error "return from pointer to non-enclosed address space" } */
+}
diff --git a/gcc/testsuite/gcc.target/pru/regio-decl-2.c b/gcc/testsuite/gcc.target/pru/regio-decl-2.c
new file mode 100644
index 00000000000..9a9338e4b42
--- /dev/null
+++ b/gcc/testsuite/gcc.target/pru/regio-decl-2.c
@@ -0,0 +1,13 @@ 
+/* Test __regio_symbol diagnostics for unsupported declarations.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O1" } */
+
+#include <stdint.h>
+
+extern volatile __regio_symbol
+uint32_t __R30[10]; /* { dg-error "aggregate types are prohibited in '__regio_symbol' address space" } */
+
+/* { dg-warning "'__R31' initialized and declared 'extern'" "" { target *-*-* } 0 } */
+extern volatile __regio_symbol
+uint32_t __R31 = 2; /* { dg-error "variables in '__regio_symbol' address space cannot have initial value" } */
diff --git a/gcc/testsuite/gcc.target/pru/regio-decl-3.c b/gcc/testsuite/gcc.target/pru/regio-decl-3.c
new file mode 100644
index 00000000000..36fcd8ac730
--- /dev/null
+++ b/gcc/testsuite/gcc.target/pru/regio-decl-3.c
@@ -0,0 +1,19 @@ 
+/* Test __regio_symbol diagnostics for unsupported declarations.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O1" } */
+
+#include <stdint.h>
+
+uint32_t __regio_symbol *test1(void); /* { dg-error "pointers to '__regio_symbol' address space are prohibited" } */
+
+void test2(uint32_t __regio_symbol __R30); /* { dg-error "'__regio_symbol' specified for parameter '__R30'" } */
+
+void test3(uint32_t __regio_symbol *__R30); /* { dg-error "pointers to '__regio_symbol' address space are prohibited" } */
+
+typedef volatile uint32_t __regio_symbol * regio_type1_t; /* { dg-error "pointers to '__regio_symbol' address space are prohibited" } */
+
+struct A {
+  uint32_t __regio_symbol *__R30; /* { dg-error "pointers to '__regio_symbol' address space are prohibited" } */
+  uint32_t __regio_symbol __R31; /* { dg-error "__regio_symbol' specified for structure field '__R31'" } */
+};
diff --git a/gcc/testsuite/gcc.target/pru/regio-decl-4.c b/gcc/testsuite/gcc.target/pru/regio-decl-4.c
new file mode 100644
index 00000000000..48c45a7c918
--- /dev/null
+++ b/gcc/testsuite/gcc.target/pru/regio-decl-4.c
@@ -0,0 +1,17 @@ 
+/* Test __regio_symbol diagnostics for unsupported access.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O1" } */
+
+#include <stdint.h>
+
+extern volatile uint32_t __regio_symbol *__R30;
+uint32_t test_r(void)
+{
+  return *__R30; /* { dg-error "invalid access to '__regio_symbol' address space" } */
+}
+
+void test_w(uint32_t a)
+{
+  *__R30 = a; /* { dg-error "invalid access to '__regio_symbol' address space" } */
+}
diff --git a/gcc/testsuite/gcc.target/pru/regio-decl.c b/gcc/testsuite/gcc.target/pru/regio-decl.c
new file mode 100644
index 00000000000..a4962aa4495
--- /dev/null
+++ b/gcc/testsuite/gcc.target/pru/regio-decl.c
@@ -0,0 +1,15 @@ 
+/* Test __regio_symbol diagnostics for unsupported declarations.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O1" } */
+
+#include <stdint.h>
+
+volatile __regio_symbol
+uint32_t __R30; /* { dg-error "variables in '__regio_symbol' address space must be declared 'extern'" } */
+
+extern __regio_symbol
+uint32_t __R31; /* { dg-error "variables in '__regio_symbol' address space must be declared 'volatile'" } */
+
+extern volatile
+__regio_symbol uint32_t __R32; /* { dg-error "register name '__R32' not recognized in '__regio_symbol' address space" } */
diff --git a/gcc/testsuite/gcc.target/pru/regio-di.c b/gcc/testsuite/gcc.target/pru/regio-di.c
new file mode 100644
index 00000000000..a4226274fc1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/pru/regio-di.c
@@ -0,0 +1,9 @@ 
+/* Test __regio_symbol invalid access diagnostic for DImode.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O1" } */
+
+#include <stdint.h>
+
+extern volatile
+__regio_symbol uint64_t __R31; /* { dg-error "only 32-bit access is supported for '__regio_symbol' address space" } */
diff --git a/gcc/testsuite/gcc.target/pru/regio-hi.c b/gcc/testsuite/gcc.target/pru/regio-hi.c
new file mode 100644
index 00000000000..5b89e8cea96
--- /dev/null
+++ b/gcc/testsuite/gcc.target/pru/regio-hi.c
@@ -0,0 +1,9 @@ 
+/* Test __regio_symbol invalid access diagnostic for HImode.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O1" } */
+
+#include <stdint.h>
+
+extern volatile __regio_symbol
+uint16_t __R31; /* { dg-error "only 32-bit access is supported for '__regio_symbol' address space" } */
diff --git a/gcc/testsuite/gcc.target/pru/regio-qi.c b/gcc/testsuite/gcc.target/pru/regio-qi.c
new file mode 100644
index 00000000000..a3f63062b06
--- /dev/null
+++ b/gcc/testsuite/gcc.target/pru/regio-qi.c
@@ -0,0 +1,9 @@ 
+/* Test __regio_symbol invalid access diagnostic for QImode.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O1" } */
+
+#include <stdint.h>
+
+extern volatile __regio_symbol
+uint8_t __R31; /* { dg-error "only 32-bit access is supported for '__regio_symbol' address space" } */
diff --git a/gcc/testsuite/gcc.target/pru/regio.c b/gcc/testsuite/gcc.target/pru/regio.c
new file mode 100644
index 00000000000..2f01263b902
--- /dev/null
+++ b/gcc/testsuite/gcc.target/pru/regio.c
@@ -0,0 +1,58 @@ 
+/* __regio_symbol operations. */
+
+/* { dg-do compile } */
+/* { dg-options "-Os" } */
+
+#include "regio.h"
+
+void
+test_r30_w_const (void)
+{
+  /* { dg-final { scan-assembler "mov\\tr30, r\[012\]\[0-9\]?" } } */
+  __R30 = 1;
+}
+
+void
+test_r31_w_zext_qi (unsigned char val1)
+{
+  /* { dg-final { scan-assembler "mov\\tr31, r14.b0" } } */
+  __R31 = val1;
+}
+
+void
+test_r31_w_zext_hi (unsigned short val1)
+{
+  /* { dg-final { scan-assembler "mov\\tr31, r14.w0" } } */
+  __R31 = val1;
+}
+
+void
+test_r31_w (unsigned int val1)
+{
+  /* { dg-final { scan-assembler "mov\\tr31, r14" } } */
+  __R31 = val1;
+}
+
+uint32_t
+test_r30_r (void)
+{
+  /* { dg-final { scan-assembler "mov\\tr14, r30" } } */
+  return __R30;
+}
+
+void
+test_r30_rw (void)
+{
+  /* { dg-final { scan-assembler "mov\\tr\[012\]\[0-9\]?, r30" } } */
+  /* { dg-final { scan-assembler "mov\\tr30, r\[012\]\[0-9\]?" } } */
+  __R30 = __R30;
+}
+
+void
+test_r31_rw (void)
+{
+  /* { dg-final { scan-assembler "mov\\tr\[012\]\[0-9\]?, r31" } } */
+  /* { dg-final { scan-assembler "mov\\tr31, r\[012\]\[0-9\]?" } } */
+  __R31 |= 101;
+}
+
diff --git a/gcc/testsuite/gcc.target/pru/regio.h b/gcc/testsuite/gcc.target/pru/regio.h
new file mode 100644
index 00000000000..3a120c1d2d1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/pru/regio.h
@@ -0,0 +1,7 @@ 
+
+#include <stdint.h>
+
+/* Declare the I/O registers.  */
+extern volatile __regio_symbol uint32_t __R30;
+extern volatile __regio_symbol uint32_t __R31;
+