[RFC] pru: Named address space for R30/R31 I/O access
Commit Message
Hi,
I'm sending this patch to get feedback for a new PRU CPU port feature.
My intention is to push it to master by end of September, so that it gets
included in GCC 12.
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-pointer2.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 | 155 +++++++++++++++++-
gcc/config/pru/pru.h | 5 +
gcc/config/pru/pru.md | 102 +++++++++++-
gcc/doc/extend.texi | 19 ++-
.../gcc.target/pru/regio-as-pointer.c | 11 ++
.../gcc.target/pru/regio-as-pointer2.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, 477 insertions(+), 11 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/pru/regio-as-pointer.c
create mode 100644 gcc/testsuite/gcc.target/pru/regio-as-pointer2.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
Comments
On Tue, Sep 14, 2021 at 11:13 PM Dimitar Dimitrov <dimitar@dinux.eu> wrote:
>
> Hi,
>
> I'm sending this patch to get feedback for a new PRU CPU port feature.
> My intention is to push it to master by end of September, so that it gets
> included in GCC 12.
>
> 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].
Yes, a "register" write or read does not follow volatile semantics, so
exposing those as registers isn't supported (I consider the GPIO regs
similar to MSRs on other CPUs?).
> 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.
I think that's reasonable. I do wonder whether it's possible to prevent
taking the address of __R30 though - otherwise I guess the backend
will crash or do weird things on such code?
> 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-pointer2.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 | 155 +++++++++++++++++-
> gcc/config/pru/pru.h | 5 +
> gcc/config/pru/pru.md | 102 +++++++++++-
> gcc/doc/extend.texi | 19 ++-
> .../gcc.target/pru/regio-as-pointer.c | 11 ++
> .../gcc.target/pru/regio-as-pointer2.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, 477 insertions(+), 11 deletions(-)
> create mode 100644 gcc/testsuite/gcc.target/pru/regio-as-pointer.c
> create mode 100644 gcc/testsuite/gcc.target/pru/regio-as-pointer2.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 --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..341be5a0ecd 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,116 @@ 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. */
> +
> +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 +3074,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 +3123,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 8b324a097a4..d9adb8ae2c5 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.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-as-pointer2.c b/gcc/testsuite/gcc.target/pru/regio-as-pointer2.c
> new file mode 100644
> index 00000000000..c85bb564677
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/pru/regio-as-pointer2.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+1); /* { dg-error "invalid access to '__regio_symbol' 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..a76cb4700c6
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/pru/regio-decl-2.c
> @@ -0,0 +1,13 @@
> +/* Test __regio_symbol diagnostics for wrong 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..f2b0b7fe0d5
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/pru/regio-decl-3.c
> @@ -0,0 +1,19 @@
> +/* Test __regio_symbol diagnostics for wrong 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..de2e8689c76
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/pru/regio-decl-4.c
> @@ -0,0 +1,17 @@
> +/* Test __regio_symbol diagnostics for wrong 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..a4e8822c528
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/pru/regio-decl.c
> @@ -0,0 +1,15 @@
> +/* Test __regio_symbol diagnostics for wrong 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;
> +
> --
> 2.31.1
>
On Wed, Sep 15, 2021 at 11:12:18AM +0200, Richard Biener wrote:
> On Tue, Sep 14, 2021 at 11:13 PM Dimitar Dimitrov <dimitar@dinux.eu> wrote:
> >
> > Hi,
> >
> > I'm sending this patch to get feedback for a new PRU CPU port feature.
> > My intention is to push it to master by end of September, so that it gets
> > included in GCC 12.
> >
> > 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].
>
> Yes, a "register" write or read does not follow volatile semantics, so
> exposing those as registers isn't supported (I consider the GPIO regs
> similar to MSRs on other CPUs?).
Yes, they are a lot like MSRs.
>
> > 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.
>
> I think that's reasonable. I do wonder whether it's possible to prevent
> taking the address of __R30 though - otherwise I guess the backend
> will crash or do weird things on such code?
I believe I have handled those cases, and suitable error messages are
emitted by the compiler. See the negative test cases added in
regio-as-pointer*.c and regio-decl*.c.
Thanks,
Dimitar
>
> > 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-pointer2.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 | 155 +++++++++++++++++-
> > gcc/config/pru/pru.h | 5 +
> > gcc/config/pru/pru.md | 102 +++++++++++-
> > gcc/doc/extend.texi | 19 ++-
> > .../gcc.target/pru/regio-as-pointer.c | 11 ++
> > .../gcc.target/pru/regio-as-pointer2.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, 477 insertions(+), 11 deletions(-)
> > create mode 100644 gcc/testsuite/gcc.target/pru/regio-as-pointer.c
> > create mode 100644 gcc/testsuite/gcc.target/pru/regio-as-pointer2.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 --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..341be5a0ecd 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,116 @@ 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. */
> > +
> > +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 +3074,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 +3123,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 8b324a097a4..d9adb8ae2c5 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.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-as-pointer2.c b/gcc/testsuite/gcc.target/pru/regio-as-pointer2.c
> > new file mode 100644
> > index 00000000000..c85bb564677
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/pru/regio-as-pointer2.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+1); /* { dg-error "invalid access to '__regio_symbol' 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..a76cb4700c6
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/pru/regio-decl-2.c
> > @@ -0,0 +1,13 @@
> > +/* Test __regio_symbol diagnostics for wrong 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..f2b0b7fe0d5
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/pru/regio-decl-3.c
> > @@ -0,0 +1,19 @@
> > +/* Test __regio_symbol diagnostics for wrong 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..de2e8689c76
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/pru/regio-decl-4.c
> > @@ -0,0 +1,17 @@
> > +/* Test __regio_symbol diagnostics for wrong 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..a4e8822c528
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/pru/regio-decl.c
> > @@ -0,0 +1,15 @@
> > +/* Test __regio_symbol diagnostics for wrong 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;
> > +
> > --
> > 2.31.1
> >
@@ -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"
@@ -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")))
@@ -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);
}
@@ -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
@@ -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,116 @@ 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. */
+
+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 +3074,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 +3123,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
@@ -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 \
@@ -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.
@@ -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
new file mode 100644
@@ -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" } */
+}
new file mode 100644
@@ -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+1); /* { dg-error "invalid access to '__regio_symbol' address space" } */
+}
new file mode 100644
@@ -0,0 +1,13 @@
+/* Test __regio_symbol diagnostics for wrong 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" } */
new file mode 100644
@@ -0,0 +1,19 @@
+/* Test __regio_symbol diagnostics for wrong 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'" } */
+};
new file mode 100644
@@ -0,0 +1,17 @@
+/* Test __regio_symbol diagnostics for wrong 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" } */
+}
new file mode 100644
@@ -0,0 +1,15 @@
+/* Test __regio_symbol diagnostics for wrong 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" } */
new file mode 100644
@@ -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" } */
new file mode 100644
@@ -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" } */
new file mode 100644
@@ -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" } */
new file mode 100644
@@ -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;
+}
+
new file mode 100644
@@ -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;
+