;; 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).
"@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"
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")))
{
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);
}
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
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. */
need to confuse users with this warning. */
fprintf (asm_out_file, "\t.set no_warn_regname_label\n");
}
+
+/* Scan type TYP for pointer references to address space other than
+ ADDR_SPACE_GENERIC. Return true if such reference is found.
+ Much of this code was taken from the avr port. */
+
+static bool
+pru_nongeneric_pointer_addrspace (tree typ)
+{
+ while (ARRAY_TYPE == TREE_CODE (typ))
+ typ = TREE_TYPE (typ);
+
+ if (POINTER_TYPE_P (typ))
+ {
+ addr_space_t as;
+ tree target = TREE_TYPE (typ);
+
+ /* Pointer to function: Test the function's return type. */
+ if (FUNCTION_TYPE == TREE_CODE (target))
+ return pru_nongeneric_pointer_addrspace (TREE_TYPE (target));
+
+ /* "Ordinary" pointers... */
+
+ while (TREE_CODE (target) == ARRAY_TYPE)
+ target = TREE_TYPE (target);
+
+ as = TYPE_ADDR_SPACE (target);
+
+ if (!ADDR_SPACE_GENERIC_P (as))
+ return true;
+
+ /* Scan pointer's target type. */
+ return pru_nongeneric_pointer_addrspace (target);
+ }
+
+ return false;
+}
+
+/* Implement `TARGET_INSERT_ATTRIBUTES'. For PRU it's used as a hook to
+ provide better diagnostics for some invalid usages of the __regio_symbol
+ address space.
+
+ Any escapes of the following checks are supposed to be caught
+ during the "mov<mode>" pattern expansion. */
+
+static void
+pru_insert_attributes (tree node, tree *attributes ATTRIBUTE_UNUSED)
+{
+
+ /* Validate __regio_symbol variable declarations. */
+ if (VAR_P (node))
+ {
+ const char *name = DECL_NAME (node)
+ ? IDENTIFIER_POINTER (DECL_NAME (node))
+ : "<unknown>";
+ tree typ = TREE_TYPE (node);
+ addr_space_t as = TYPE_ADDR_SPACE (typ);
+
+ if (as == ADDR_SPACE_GENERIC)
+ return;
+
+ if (AGGREGATE_TYPE_P (typ))
+ {
+ error ("aggregate types are prohibited in "
+ "%<__regio_symbol%> address space");
+ /* Don't bother anymore. Below checks would pile
+ meaningless errors, which would confuse user. */
+ return;
+ }
+ if (DECL_INITIAL (node) != NULL_TREE)
+ error ("variables in %<__regio_symbol%> address space "
+ "cannot have initial value");
+ if (DECL_REGISTER (node))
+ error ("variables in %<__regio_symbol%> address space "
+ "cannot be declared %<register%>");
+ if (!TYPE_VOLATILE (typ))
+ error ("variables in %<__regio_symbol%> address space "
+ "must be declared %<volatile%>");
+ if (!DECL_EXTERNAL (node))
+ error ("variables in %<__regio_symbol%> address space "
+ "must be declared %<extern%>");
+ if (TYPE_MODE (typ) != SImode)
+ error ("only 32-bit access is supported "
+ "for %<__regio_symbol%> address space");
+ if (strcmp (name, "__R30") != 0 && strcmp (name, "__R31") != 0)
+ error ("register name %<%s%> not recognized "
+ "in %<__regio_symbol%> address space", name);
+ }
+
+ tree typ = NULL_TREE;
+
+ switch (TREE_CODE (node))
+ {
+ case FUNCTION_DECL:
+ typ = TREE_TYPE (TREE_TYPE (node));
+ break;
+ case TYPE_DECL:
+ case RESULT_DECL:
+ case VAR_DECL:
+ case FIELD_DECL:
+ case PARM_DECL:
+ typ = TREE_TYPE (node);
+ break;
+ case POINTER_TYPE:
+ typ = node;
+ break;
+ default:
+ break;
+ }
+ if (typ != NULL_TREE && pru_nongeneric_pointer_addrspace (typ))
+ error ("pointers to %<__regio_symbol%> address space are prohibited");
+}
\f
/* Function argument related. */
#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
#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
MULDST_REGS,
MULSRC0_REGS,
MULSRC1_REGS,
+ REGIO_REGS,
GP_REGS,
ALL_REGS,
LIM_REG_CLASSES
"MULDST_REGS", \
"MULSRC0_REGS", \
"MULSRC1_REGS", \
+ "REGIO_REGS", \
"GP_REGS", \
"ALL_REGS" }
/* 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} \
}
((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 \
(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.
]
)
+;; Enumerate address spaces.
+(define_constants
+ [
+ (ADDR_SPACE_REGIO 1) ; Access to R30 and R31 I/O registers.
+ ]
+)
+
;; Enumeration of UNSPECs.
(define_c_enum "unspec" [
UNSPECV_HALT
UNSPECV_BLOCKAGE
+
+ UNSPECV_REGIO_READ
+ UNSPECV_REGIO_WRITE
])
\f
; Length of an instruction (in bytes).
(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
(include "alu-zext.md")
\f
+;; 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")])
+\f
;; 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.
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
@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
--- /dev/null
+/* Test __regio_symbol invalid attempt to get regio variable address. */
+
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+#include "regio.h"
+
+uint32_t test(void)
+{
+ return *(&__R30+1); /* { dg-error "invalid access to '__regio_symbol' address space" } */
+}
--- /dev/null
+/* 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" } */
+}
--- /dev/null
+/* Test __regio_symbol diagnostics for unsupported declarations. */
+
+/* { dg-do compile } */
+/* { dg-options "-O1" } */
+
+#include <stdint.h>
+
+extern volatile __regio_symbol
+uint32_t __R30[10]; /* { dg-error "aggregate types are prohibited in '__regio_symbol' address space" } */
+
+/* { dg-warning "'__R31' initialized and declared 'extern'" "" { target *-*-* } 0 } */
+extern volatile __regio_symbol
+uint32_t __R31 = 2; /* { dg-error "variables in '__regio_symbol' address space cannot have initial value" } */
--- /dev/null
+/* Test __regio_symbol diagnostics for unsupported declarations. */
+
+/* { dg-do compile } */
+/* { dg-options "-O1" } */
+
+#include <stdint.h>
+
+uint32_t __regio_symbol *test1(void); /* { dg-error "pointers to '__regio_symbol' address space are prohibited" } */
+
+void test2(uint32_t __regio_symbol __R30); /* { dg-error "'__regio_symbol' specified for parameter '__R30'" } */
+
+void test3(uint32_t __regio_symbol *__R30); /* { dg-error "pointers to '__regio_symbol' address space are prohibited" } */
+
+typedef volatile uint32_t __regio_symbol * regio_type1_t; /* { dg-error "pointers to '__regio_symbol' address space are prohibited" } */
+
+struct A {
+ uint32_t __regio_symbol *__R30; /* { dg-error "pointers to '__regio_symbol' address space are prohibited" } */
+ uint32_t __regio_symbol __R31; /* { dg-error "__regio_symbol' specified for structure field '__R31'" } */
+};
--- /dev/null
+/* Test __regio_symbol diagnostics for unsupported access. */
+
+/* { dg-do compile } */
+/* { dg-options "-O1" } */
+
+#include <stdint.h>
+
+extern volatile uint32_t __regio_symbol *__R30;
+uint32_t test_r(void)
+{
+ return *__R30; /* { dg-error "invalid access to '__regio_symbol' address space" } */
+}
+
+void test_w(uint32_t a)
+{
+ *__R30 = a; /* { dg-error "invalid access to '__regio_symbol' address space" } */
+}
--- /dev/null
+/* Test __regio_symbol diagnostics for unsupported declarations. */
+
+/* { dg-do compile } */
+/* { dg-options "-O1" } */
+
+#include <stdint.h>
+
+volatile __regio_symbol
+uint32_t __R30; /* { dg-error "variables in '__regio_symbol' address space must be declared 'extern'" } */
+
+extern __regio_symbol
+uint32_t __R31; /* { dg-error "variables in '__regio_symbol' address space must be declared 'volatile'" } */
+
+extern volatile
+__regio_symbol uint32_t __R32; /* { dg-error "register name '__R32' not recognized in '__regio_symbol' address space" } */
--- /dev/null
+/* 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" } */
--- /dev/null
+/* 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" } */
--- /dev/null
+/* 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" } */
--- /dev/null
+/* __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;
+}
+
--- /dev/null
+
+#include <stdint.h>
+
+/* Declare the I/O registers. */
+extern volatile __regio_symbol uint32_t __R30;
+extern volatile __regio_symbol uint32_t __R31;
+