Nios II has a group of load/store IO instructions that bypass the normal memory cache; they're intended to be used for accessing memory-mapped IO peripherals. In the R2 re-encoding of the Nios II instruction set, the constant offset field for these instructions has been reduced from 16 bits to 12, so GCC needs a new constraint for memory addresses for these instructions.
A "gotcha" here is that the new encodings don't play nicely with GP-relative addressing. %gprel is a 16-bit relocation, and adding a 12-bit equivalent didn't seem very useful as it would restrict the size of the small data area to only 4K. Moreover, we'd expect IO peripherals to be mapped somewhere other than the normal small data section. So, we just tell GCC not to emit GP-relative addresses for anything that might be used in a R2 load/store IO instruction. Committed as r225792. -Sandra
2015-07-14 Sandra Loosemore <san...@codesourcery.com> Cesar Philippidis <ce...@codesourcery.com> Chung-Lin Tang <clt...@codesourcery.com> gcc/ * config/nios2/nios2.h (SMALL_INT12): New macro. * config/nios2/nios2.c (nios2_valid_addr_offset_p): New function. (nios2_valid_addr_expr_p): Use it. (nios2_symbol_ref_in_small_data_p): Disallow GP-relative addressing with implicit "io" instructions on R2. * config/nios2/constraints.md (w): New constraint. * config/nios2/predicates.md (ldstio_memory_operand): New. * config/nios2/nios2.md (ld<bhw_uns>io, ld<bh>io): Update memory operand predicate and constraint. (ld<bh>io_signed, st<bhw>io>): Likewise. * doc/md.texi (Machine Constraints): Document w constraint. gcc/testsuite/ * gcc.target/nios2/r2-io-range.c: New. * gcc.target/nios2/r2-stio-1.c: New. * gcc.target/nios2/r2-stio-2.c: New. * gcc.target/nios2/nios2-ldxio.c: New. * gcc.target/nios2/nios2-stxio.c: Change to assemble test instead of just compile. Add more tests.
Index: gcc/config/nios2/nios2.h =================================================================== --- gcc/config/nios2/nios2.h (revision 225791) +++ gcc/config/nios2/nios2.h (working copy) @@ -216,6 +216,7 @@ enum reg_class /* Tests for various kinds of constants used in the Nios II port. */ #define SMALL_INT(X) ((unsigned HOST_WIDE_INT)(X) + 0x8000 < 0x10000) +#define SMALL_INT12(X) ((unsigned HOST_WIDE_INT)(X) + 0x800 < 0x1000) #define SMALL_INT_UNSIGNED(X) ((X) >= 0 && (X) < 0x10000) #define UPPER16_INT(X) (((X) & 0xffff) == 0) #define SHIFT_INT(X) ((X) >= 0 && (X) <= 31) Index: gcc/config/nios2/nios2.c =================================================================== --- gcc/config/nios2/nios2.c (revision 225791) +++ gcc/config/nios2/nios2.c (working copy) @@ -1627,6 +1627,21 @@ nios2_regno_ok_for_base_p (int regno, bo || regno == ARG_POINTER_REGNUM); } +/* Return true if OFFSET is permitted in a load/store address expression. + Normally any 16-bit value is permitted, but on R2 if we may be emitting + the IO forms of these instructions we must restrict the offset to fit + in a 12-bit field instead. */ + +static bool +nios2_valid_addr_offset_p (rtx offset) +{ + return (CONST_INT_P (offset) + && ((TARGET_ARCH_R2 && (TARGET_BYPASS_CACHE + || TARGET_BYPASS_CACHE_VOLATILE)) + ? SMALL_INT12 (INTVAL (offset)) + : SMALL_INT (INTVAL (offset)))); +} + /* Return true if the address expression formed by BASE + OFFSET is valid. */ static bool @@ -1637,7 +1652,7 @@ nios2_valid_addr_expr_p (rtx base, rtx o return (REG_P (base) && nios2_regno_ok_for_base_p (REGNO (base), strict_p) && (offset == NULL_RTX - || const_arith_operand (offset, Pmode) + || nios2_valid_addr_offset_p (offset) || nios2_unspec_reloc_p (offset))); } @@ -1739,6 +1754,13 @@ nios2_symbol_ref_in_small_data_p (rtx sy if (SYMBOL_REF_TLS_MODEL (sym) != 0) return false; + /* On Nios II R2, there is no GP-relative relocation that can be + used with "io" instructions. So, if we are implicitly generating + those instructions, we cannot emit GP-relative accesses. */ + if (TARGET_ARCH_R2 + && (TARGET_BYPASS_CACHE || TARGET_BYPASS_CACHE_VOLATILE)) + return false; + /* If the user has explicitly placed the symbol in a small data section via an attribute, generate gp-relative addressing even if the symbol is external, weak, or larger than we'd automatically put in the Index: gcc/config/nios2/constraints.md =================================================================== --- gcc/config/nios2/constraints.md (revision 225790) +++ gcc/config/nios2/constraints.md (working copy) @@ -28,6 +28,10 @@ ;; N: 0 to 255 (for custom instruction numbers) ;; O: 0 to 31 (for control register numbers) ;; +;; We use the following constraint letters for memory constraints +;; +;; w: memory operands for load/store IO and cache instructions +;; ;; We use the following built-in register classes: ;; ;; r: general purpose register (r0..r31) @@ -89,3 +93,7 @@ (define_constraint "T" "A constant unspec offset representing a relocation." (match_test "nios2_unspec_reloc_p (op)")) + +(define_memory_constraint "w" + "A memory operand suitable for load/store IO and cache instructions." + (match_operand 0 "ldstio_memory_operand")) Index: gcc/config/nios2/predicates.md =================================================================== --- gcc/config/nios2/predicates.md (revision 225790) +++ gcc/config/nios2/predicates.md (working copy) @@ -83,3 +83,20 @@ &XEXP (op, 0), &XEXP (op, 1), false)); }) + +(define_predicate "ldstio_memory_operand" + (match_code "mem") +{ + if (TARGET_ARCH_R2) + { + rtx addr = XEXP (op, 0); + if (REG_P (addr)) + return true; + else if (GET_CODE (addr) == PLUS) + return (REG_P (XEXP (addr, 0)) + && CONST_INT_P (XEXP (addr, 1)) + && SMALL_INT12 (INTVAL (XEXP (addr, 1)))); + return false; + } + return memory_operand (op, mode); +}) Index: gcc/config/nios2/nios2.md =================================================================== --- gcc/config/nios2/nios2.md (revision 225790) +++ gcc/config/nios2/nios2.md (working copy) @@ -221,14 +221,14 @@ (define_insn "ld<bhw_uns>io" [(set (match_operand:BHW 0 "register_operand" "=r") (unspec_volatile:BHW - [(match_operand:BHW 1 "memory_operand" "m")] UNSPECV_LDXIO))] + [(match_operand:BHW 1 "ldstio_memory_operand" "w")] UNSPECV_LDXIO))] "" "ld<bhw_uns>io\\t%0, %1" [(set_attr "type" "ld")]) (define_expand "ld<bh>io" [(set (match_operand:BH 0 "register_operand" "=r") - (match_operand:BH 1 "memory_operand" "m"))] + (match_operand:BH 1 "ldstio_memory_operand" "w"))] "" { rtx tmp = gen_reg_rtx (SImode); @@ -241,13 +241,13 @@ [(set (match_operand:SI 0 "register_operand" "=r") (sign_extend:SI (unspec_volatile:BH - [(match_operand:BH 1 "memory_operand" "m")] UNSPECV_LDXIO)))] + [(match_operand:BH 1 "ldstio_memory_operand" "w")] UNSPECV_LDXIO)))] "" "ld<bh>io\\t%0, %1" [(set_attr "type" "ld")]) (define_insn "st<bhw>io" - [(set (match_operand:BHW 0 "memory_operand" "=m") + [(set (match_operand:BHW 0 "ldstio_memory_operand" "=w") (unspec_volatile:BHW [(match_operand:BHW 1 "reg_or_0_operand" "rM")] UNSPECV_STXIO))] "" Index: gcc/doc/md.texi =================================================================== --- gcc/doc/md.texi (revision 225790) +++ gcc/doc/md.texi (working copy) @@ -2996,6 +2996,10 @@ Matches immediates which are addresses i data section and therefore can be added to @code{gp} as a 16-bit immediate to re-create their 32-bit value. +@item w +A memory operand suitable for load/store IO and cache +instructions. + @ifset INTERNALS @item T A @code{const} wrapped @code{UNSPEC} expression, Index: gcc/testsuite/gcc.target/nios2/r2-io-range.c =================================================================== --- gcc/testsuite/gcc.target/nios2/r2-io-range.c (revision 0) +++ gcc/testsuite/gcc.target/nios2/r2-io-range.c (revision 0) @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -march=r2 -mbypass-cache" } */ + +/* Check that the compiler is aware of the reduced offset range for ldio/stio + instructions in the Nios II R2 encoding. */ + +unsigned int too_big (unsigned int *p) +{ + return *(p + 0x400); +} + +unsigned int small_enough (unsigned int *p) +{ + return *(p + 0x100); +} + +/* { dg-final { scan-assembler-not "\tldwio\t.*, 4096\\(r.*\\)" } } */ +/* { dg-final { scan-assembler "\tldwio\t.*, 1024\\(r.*\\)" } } */ Index: gcc/testsuite/gcc.target/nios2/r2-stio-1.c =================================================================== --- gcc/testsuite/gcc.target/nios2/r2-stio-1.c (revision 0) +++ gcc/testsuite/gcc.target/nios2/r2-stio-1.c (revision 0) @@ -0,0 +1,19 @@ +/* { dg-do compile } */ +/* { dg-options "-O -mgpopt -march=r2" } */ + +/* The ldio/stio builtins must not use GP-relative addresses for + small data objects in R2. This is because the address offset field + has been reduced to 12 bits in R2, and %gprel is a 16-bit relocation. */ + +extern volatile unsigned int frob; + +volatile unsigned int frob = 0; + +void foo (unsigned int val) +{ + __builtin_stwio (&frob, val); +} + +/* { dg-final { scan-assembler "stwio\\t" } } */ +/* { dg-final { scan-assembler-not "stwio\\t.*%gprel(frob)" } } */ + Index: gcc/testsuite/gcc.target/nios2/r2-stio-2.c =================================================================== --- gcc/testsuite/gcc.target/nios2/r2-stio-2.c (revision 0) +++ gcc/testsuite/gcc.target/nios2/r2-stio-2.c (revision 0) @@ -0,0 +1,19 @@ +/* { dg-do compile } */ +/* { dg-options "-O -mgpopt -march=r2 -mbypass-cache" } */ + +/* Implicit ldio/stio operations must not use GP-relative addresses for + small data objects in R2. This is because the address offset field + has been reduced to 12 bits in R2, and %gprel is a 16-bit relocation. */ + +extern volatile unsigned int frob; + +volatile unsigned int frob = 0; + +void foo (unsigned int val) +{ + frob = val; +} + +/* { dg-final { scan-assembler "stwio\\t" } } */ +/* { dg-final { scan-assembler-not "stwio\\t.*%gprel(frob)" } } */ + Index: gcc/testsuite/gcc.target/nios2/nios2-ldxio.c =================================================================== --- gcc/testsuite/gcc.target/nios2/nios2-ldxio.c (revision 0) +++ gcc/testsuite/gcc.target/nios2/nios2-ldxio.c (revision 0) @@ -0,0 +1,52 @@ +/* { dg-do assemble } */ +/* { dg-options "-O" } */ + +void test_ldbio (unsigned char* p1, unsigned char* p2) +{ + __builtin_ldbio (p1); + __builtin_ldbio (p2); + __builtin_ldbio (p2 + 1); + __builtin_ldbio (p2 + 2); + __builtin_ldbio (p2 + 2047); + __builtin_ldbio (p2 + 2048); +} + +void test_ldbuio (unsigned char* p1, unsigned char* p2) +{ + __builtin_ldbuio (p1); + __builtin_ldbuio (p2); + __builtin_ldbuio (p2 + 1); + __builtin_ldbuio (p2 + 2); + __builtin_ldbuio (p2 + 2047); + __builtin_ldbuio (p2 + 2048); +} + +void test_ldhio (unsigned short* p1, unsigned short* p2) +{ + __builtin_ldhio (p1); + __builtin_ldhio (p2); + __builtin_ldhio (p2 + 1); + __builtin_ldhio (p2 + 2); + __builtin_ldhio (p2 + 1023); + __builtin_ldhio (p2 + 1024); +} + +void test_ldhuio (unsigned short* p1, unsigned short* p2) +{ + __builtin_ldhuio (p1); + __builtin_ldhuio (p2); + __builtin_ldhuio (p2 + 1); + __builtin_ldhuio (p2 + 2); + __builtin_ldhuio (p2 + 1023); + __builtin_ldhuio (p2 + 1024); +} + +void test_ldwio (unsigned int* p1, unsigned int* p2) +{ + __builtin_ldwio (p1); + __builtin_ldwio (p2); + __builtin_ldwio (p2 + 1); + __builtin_ldwio (p2 + 2); + __builtin_ldwio (p2 + 511); + __builtin_ldwio (p2 + 512); +} Index: gcc/testsuite/gcc.target/nios2/nios2-stxio.c =================================================================== --- gcc/testsuite/gcc.target/nios2/nios2-stxio.c (revision 225790) +++ gcc/testsuite/gcc.target/nios2/nios2-stxio.c (working copy) @@ -1,4 +1,5 @@ -/* { dg-do compile } */ +/* { dg-do assemble } */ +/* { dg-options "-O" } */ void test_stbio (unsigned char* p1, unsigned char* p2) { @@ -6,6 +7,8 @@ void test_stbio (unsigned char* p1, unsi __builtin_stbio (p2, 0); __builtin_stbio (p2 + 1, 0x80); __builtin_stbio (p2 + 2, 0x7f); + __builtin_stbio (p2 + 2047, 0x80); + __builtin_stbio (p2 + 2048, 0x7f); } void test_sthio (unsigned short* p1, unsigned short* p2) @@ -14,6 +17,8 @@ void test_sthio (unsigned short* p1, uns __builtin_sthio (p2, 0); __builtin_sthio (p2 + 1, 0x8000); __builtin_sthio (p2 + 2, 0x7fff); + __builtin_sthio (p2 + 1023, 0x8000); + __builtin_sthio (p2 + 1024, 0x7fff); } void test_stwio (unsigned int* p1, unsigned int* p2) @@ -22,4 +27,7 @@ void test_stwio (unsigned int* p1, unsig __builtin_stwio (p2, 0); __builtin_stwio (p2 + 1, 0x80000000); __builtin_stwio (p2 + 2, 0x7fffffff); + __builtin_stwio (p2 + 511, 5); + __builtin_stwio (p2 + 512, 5); } +