diff mbox

[nios2,2/7] Adjust for reduced offsets in R2 load/store IO insns

Message ID 55A58E39.5020106@codesourcery.com
State New
Headers show

Commit Message

Sandra Loosemore July 14, 2015, 10:33 p.m. UTC
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
diff mbox

Patch

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);
 }
+