On Tue, Sep 14, 2021 at 11:13 PM Dimitar Dimitrov <dimi...@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 <dimi...@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
>

Reply via email to