]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
re PR target/54760 ([SH] Add __builtin_thread_pointer, __builtin_set_thread_pointer)
authorOleg Endo <olegendo@gcc.gnu.org>
Mon, 8 Oct 2012 02:00:46 +0000 (02:00 +0000)
committerOleg Endo <olegendo@gcc.gnu.org>
Mon, 8 Oct 2012 02:00:46 +0000 (02:00 +0000)
PR target/54760
* config/sh/sh.md (*mov<mode>_gbr_load, *mov<mode>_gbr_store): New
insns and accompanying unnamed splits.
* config/sh/predicates.md (general_movsrc_operand,
general_movdst_operand): Reject GBR addresses.
* config/sh/sh-protos.h (sh_find_equiv_gbr_addr): New declaration.
* config/sh/sh.c (sh_address_cost, sh_legitimate_address_p,
sh_secondary_reload): Handle GBR addresses.
(base_reg_disp): New class.
(sh_find_base_reg_disp, sh_find_equiv_gbr_addr): New functions.

PR target/54760
* gcc.target/sh/pr54760-2.c: New.
* gcc.target/sh/pr54760-3.c: New.

From-SVN: r192193

gcc/ChangeLog
gcc/config/sh/predicates.md
gcc/config/sh/sh-protos.h
gcc/config/sh/sh.c
gcc/config/sh/sh.md
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.target/sh/pr54760-2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/sh/pr54760-3.c [new file with mode: 0644]

index 8f944d7fd58167b9274c785dfecb3e9ae14a6355..7fcb16744b25ec54b51d72064a51e9af57bf8b37 100644 (file)
@@ -1,3 +1,16 @@
+2012-10-08  Oleg Endo  <olegendo@gcc.gnu.org>
+
+       PR target/54760
+       * config/sh/sh.md (*mov<mode>_gbr_load, *mov<mode>_gbr_store): New
+       insns and accompanying unnamed splits.
+       * config/sh/predicates.md (general_movsrc_operand,
+       general_movdst_operand): Reject GBR addresses.
+       * config/sh/sh-protos.h (sh_find_equiv_gbr_addr): New declaration.
+       * config/sh/sh.c (sh_address_cost, sh_legitimate_address_p,
+       sh_secondary_reload): Handle GBR addresses.
+       (base_reg_disp): New class.
+       (sh_find_base_reg_disp, sh_find_equiv_gbr_addr): New functions.
+
 2012-10-08  Hans-Peter Nilsson  <hp@bitrange.com>
 
        * config/mmix/mmix.c (mmix_output_octa): Don't assume
index 05ad85808bbf3fca92573f6a3625c981083808df..89b4d0fc5a3f4f51b03e4a2bbfc7746bce427eb9 100644 (file)
   if (MEM_P (op))
     {
       rtx inside = XEXP (op, 0);
+
+      /* Disallow mems with GBR address here.  They have to go through
+        separate special patterns.  */
+      if ((REG_P (inside) && REGNO (inside) == GBR_REG)
+         || (GET_CODE (inside) == PLUS && REG_P (XEXP (inside, 0))
+             && REGNO (XEXP (inside, 0)) == GBR_REG))
+       return 0;
+
       if (GET_CODE (inside) == CONST)
        inside = XEXP (inside, 0);
 
   if (t_reg_operand (op, mode))
     return 0;
 
+  if (MEM_P (op))
+    {
+      rtx inside = XEXP (op, 0);
+      /* Disallow mems with GBR address here.  They have to go through
+        separate special patterns.  */
+      if ((REG_P (inside) && REGNO (inside) == GBR_REG)
+         || (GET_CODE (inside) == PLUS && REG_P (XEXP (inside, 0))
+             && REGNO (XEXP (inside, 0)) == GBR_REG))
+       return 0;
+    }
+
   /* Only pre dec allowed.  */
   if (MEM_P (op) && GET_CODE (XEXP (op, 0)) == POST_INC)
     return 0;
index d908110dc1b072f6e1facc5b87941e1d626f1fc2..61d4eabfde98e1122db3567b0a88d379dd8a4f7e 100644 (file)
@@ -161,7 +161,7 @@ extern bool sh_vector_mode_supported_p (enum machine_mode);
 extern bool sh_cfun_trap_exit_p (void);
 extern void sh_canonicalize_comparison (enum rtx_code&, rtx&, rtx&,
                                        enum machine_mode mode = VOIDmode);
-
+extern rtx sh_find_equiv_gbr_addr (rtx cur_insn, rtx mem);
 #endif /* RTX_CODE */
 
 extern void sh_cpu_cpp_builtins (cpp_reader* pfile);
index 3a0689d9486b1f1ae3443340cfbddcb0515a488a..d1ab28afd81a333674e8212c5c7379536b88fd47 100644 (file)
@@ -3610,6 +3610,10 @@ static int
 sh_address_cost (rtx x, enum machine_mode mode,
                 addr_space_t as ATTRIBUTE_UNUSED, bool speed ATTRIBUTE_UNUSED)
 {
+  /* 'GBR + 0'.  Account one more because of R0 restriction.  */
+  if (REG_P (x) && REGNO (x) == GBR_REG)
+    return 2;
+
   /* Simple reg, post-inc, pre-dec addressing.  */
   if (REG_P (x) || GET_CODE (x) == POST_INC || GET_CODE (x) == PRE_DEC)
     return 1;
@@ -3618,6 +3622,11 @@ sh_address_cost (rtx x, enum machine_mode mode,
   if (GET_CODE (x) == PLUS
       && REG_P (XEXP (x, 0)) && CONST_INT_P (XEXP (x, 1)))
     {
+      /* 'GBR + disp'.  Account one more because of R0 restriction.  */
+      if (REGNO (XEXP (x, 0)) == GBR_REG
+         && gbr_displacement (XEXP (x, 1), mode))
+       return 2;
+
       const HOST_WIDE_INT offset = INTVAL (XEXP (x, 1));
 
       if (offset == 0)
@@ -10185,11 +10194,16 @@ sh_legitimate_index_p (enum machine_mode mode, rtx op, bool consider_sh2a,
          REG+disp
          REG+r0
          REG++
-         --REG  */
+         --REG
+         GBR
+         GBR+disp  */
 
 static bool
 sh_legitimate_address_p (enum machine_mode mode, rtx x, bool strict)
 {
+  if (REG_P (x) && REGNO (x) == GBR_REG)
+    return true;
+
   if (MAYBE_BASE_REGISTER_RTX_P (x, strict))
     return true;
   else if ((GET_CODE (x) == POST_INC || GET_CODE (x) == PRE_DEC)
@@ -10202,6 +10216,9 @@ sh_legitimate_address_p (enum machine_mode mode, rtx x, bool strict)
       rtx xop0 = XEXP (x, 0);
       rtx xop1 = XEXP (x, 1);
 
+      if (REG_P (xop0) && REGNO (xop0) == GBR_REG)
+       return gbr_displacement (xop1, mode);
+
       if (GET_MODE_SIZE (mode) <= 8
          && MAYBE_BASE_REGISTER_RTX_P (xop0, strict)
          && sh_legitimate_index_p (mode, xop1, TARGET_SH2A, false))
@@ -13014,6 +13031,17 @@ sh_secondary_reload (bool in_p, rtx x, reg_class_t rclass_i,
 {
   enum reg_class rclass = (enum reg_class) rclass_i;
 
+  if (MEM_P (x) && GET_CODE (XEXP (x, 0)) == PLUS
+      && REG_P (XEXP (XEXP (x, 0), 0))
+      && REGNO (XEXP (XEXP (x, 0), 0)) == GBR_REG)
+    return rclass == R0_REGS ? NO_REGS : R0_REGS;
+
+  if (MEM_P (x) && REG_P (XEXP (x, 0)) && REGNO (XEXP (x, 0)) == GBR_REG)
+    return rclass == R0_REGS ? NO_REGS : R0_REGS;
+
+  if (REG_P (x) && REGNO (x) == GBR_REG)
+    return NO_REGS;
+
   if (in_p)
     {
       if (REGCLASS_HAS_FP_REG (rclass)
@@ -13248,4 +13276,150 @@ sh_can_use_simple_return_p (void)
   return true;
 }
 
+/*------------------------------------------------------------------------------
+  Address mode optimization support code
+*/
+
+typedef HOST_WIDE_INT disp_t;
+static const disp_t MIN_DISP = HOST_WIDE_INT_MIN;
+static const disp_t MAX_DISP = HOST_WIDE_INT_MAX;
+static const disp_t INVALID_DISP = MAX_DISP;
+
+/* A memory reference which is described by a base register and a
+   displacement.  */
+class base_reg_disp
+{
+public:
+  base_reg_disp (rtx br, disp_t d);
+
+  bool is_reg (void) const;
+  bool is_disp (void) const;
+  rtx reg (void) const;
+  disp_t disp (void) const;
+
+private:
+  rtx reg_;
+  disp_t disp_;
+};
+
+inline
+base_reg_disp::base_reg_disp (rtx br, disp_t d)
+: reg_ (br), disp_ (d)
+{
+}
+inline bool
+base_reg_disp::is_reg (void) const
+{
+  return reg_ != NULL_RTX && disp_ != INVALID_DISP;
+}
+
+inline bool
+base_reg_disp::is_disp (void) const
+{
+  return reg_ == NULL_RTX && disp_ != INVALID_DISP;
+}
+
+inline rtx
+base_reg_disp::reg (void) const
+{
+  return reg_;
+}
+
+inline disp_t
+base_reg_disp::disp (void) const
+{
+  return disp_;
+}
+
+/* Find the base register and calculate the displacement for a given
+   address rtx 'x'.
+   This is done by walking the insn list backwards and following SET insns
+   that set the value of the specified reg 'x'.  */
+static base_reg_disp
+sh_find_base_reg_disp (rtx insn, rtx x, disp_t disp = 0, rtx base_reg = NULL)
+{
+  if (REG_P (x))
+    {
+      if (REGNO (x) == GBR_REG)
+       return base_reg_disp (x, disp);
+
+      /* We've reached a hard-reg.  This is probably the point where
+        function args are copied to pseudos.  Do not go any further and
+        stick to the pseudo.  If the original mem addr was in a hard reg
+        from the beginning, it will become the base reg.  */
+      if (REGNO (x) < FIRST_PSEUDO_REGISTER)
+       return base_reg_disp (base_reg != NULL ? base_reg : x, disp);
+
+      /* Try to find the previous insn that sets the reg.  */
+      for (rtx i = prev_nonnote_insn (insn); i != NULL;
+          i = prev_nonnote_insn (i))
+       {
+         if (!NONJUMP_INSN_P (i))
+           continue;
+
+         rtx p = PATTERN (i);
+         if (p != NULL && GET_CODE (p) == SET && REG_P (XEXP (p, 0))
+             && REGNO (XEXP (p, 0)) == REGNO (x))
+           {
+             /* If the recursion can't find out any more details about the
+                source of the set, then this reg becomes our new base reg.  */
+             return sh_find_base_reg_disp (i, XEXP (p, 1), disp, XEXP (p, 0));
+           }
+       }
+
+    /* When here, no previous insn was found that sets the reg.
+       The input reg is already the base reg.  */
+    return base_reg_disp (x, disp);
+  }
+
+  else if (GET_CODE (x) == PLUS)
+    {
+      base_reg_disp left_val = sh_find_base_reg_disp (insn, XEXP (x, 0));
+      base_reg_disp right_val = sh_find_base_reg_disp (insn, XEXP (x, 1));
+
+      /* Either left or right val must be a reg.
+        We don't handle the case of 'reg + reg' here.  */
+      if (left_val.is_reg () && right_val.is_disp ())
+       return base_reg_disp (left_val.reg (), left_val.disp ()
+                                              + right_val.disp () + disp);
+      else if (right_val.is_reg () && left_val.is_disp ())
+       return base_reg_disp (right_val.reg (), right_val.disp ()
+                                               + left_val.disp () + disp);
+      else
+       return base_reg_disp (base_reg, disp);
+    }
+
+  else if (CONST_INT_P (x))
+    return base_reg_disp (NULL, disp + INTVAL (x));
+
+  /* Didn't find anything useful.  */
+  return base_reg_disp (base_reg, disp);
+}
+
+/* Given an insn and a memory operand, try to find an equivalent GBR
+   based memory address and return the corresponding new memory address.
+   Return NULL_RTX if not found.  */
+rtx
+sh_find_equiv_gbr_addr (rtx insn, rtx mem)
+{
+  if (!MEM_P (mem))
+    return NULL_RTX;
+
+  /* Leave post/pre inc/dec or any other side effect addresses alone.  */
+  if (side_effects_p (XEXP (mem, 0)))
+    return NULL_RTX;
+
+  base_reg_disp gbr_disp = sh_find_base_reg_disp (insn, XEXP (mem, 0));
+
+  if (gbr_disp.is_reg () && REGNO (gbr_disp.reg ()) == GBR_REG)
+    {
+      rtx disp = GEN_INT (gbr_disp.disp ());
+      if (gbr_displacement (disp, GET_MODE (mem)))
+       return gen_rtx_PLUS (SImode, gen_rtx_REG (SImode, GBR_REG), disp);
+    }
+
+  return NULL_RTX;
+}
+
 #include "gt-sh.h"
index eeed561906390d7f809e74519ab3e200cb4d8aa7..989d52e3ea20576667c2b147131fd99b79f0f2a5 100644 (file)
@@ -10060,6 +10060,135 @@ label:
   "ldc %0,gbr"
   [(set_attr "type" "move")])
 
+;;------------------------------------------------------------------------------
+;; Thread pointer relative memory loads and stores.
+;;
+;; On SH there are GBR displacement address modes which can be utilized to
+;; access memory behind the thread pointer.
+;; Since we do not allow using GBR for general purpose memory accesses, these
+;; GBR addressing modes are formed by the combine pass.
+;; This could be done with fewer patterns than below by using a mem predicate
+;; for the GBR mem, but then reload would try to reload addresses with a
+;; zero displacement for some strange reason.
+
+(define_insn "*mov<mode>_gbr_load"
+  [(set (match_operand:QIHISI 0 "register_operand" "=z")
+       (mem:QIHISI (plus:SI (reg:SI GBR_REG)
+                            (match_operand:QIHISI 1 "gbr_displacement"))))]
+  "TARGET_SH1"
+  "mov.<bwl>   @(%O1,gbr),%0"
+  [(set_attr "type" "load")])
+
+(define_insn "*mov<mode>_gbr_load"
+  [(set (match_operand:QIHISI 0 "register_operand" "=z")
+       (mem:QIHISI (reg:SI GBR_REG)))]
+  "TARGET_SH1"
+  "mov.<bwl>   @(0,gbr),%0"
+  [(set_attr "type" "load")])
+
+(define_insn "*mov<mode>_gbr_load"
+  [(set (match_operand:SI 0 "register_operand" "=z")
+       (sign_extend:SI
+         (mem:QIHI (plus:SI (reg:SI GBR_REG)
+                            (match_operand:QIHI 1 "gbr_displacement")))))]
+  "TARGET_SH1"
+  "mov.<bw>    @(%O1,gbr),%0"
+  [(set_attr "type" "load")])
+
+(define_insn "*mov<mode>_gbr_load"
+  [(set (match_operand:SI 0 "register_operand" "=z")
+       (sign_extend:SI (mem:QIHI (reg:SI GBR_REG))))]
+  "TARGET_SH1"
+  "mov.<bw>    @(0,gbr),%0"
+  [(set_attr "type" "load")])
+
+(define_insn "*mov<mode>_gbr_store"
+  [(set (mem:QIHISI (plus:SI (reg:SI GBR_REG)
+                            (match_operand:QIHISI 0 "gbr_displacement")))
+       (match_operand:QIHISI 1 "register_operand" "z"))]
+  "TARGET_SH1"
+  "mov.<bwl>   %1,@(%O0,gbr)"
+  [(set_attr "type" "store")])
+
+(define_insn "*mov<mode>_gbr_store"
+  [(set (mem:QIHISI (reg:SI GBR_REG))
+       (match_operand:QIHISI 0 "register_operand" "z"))]
+  "TARGET_SH1"
+  "mov.<bwl>   %0,@(0,gbr)"
+  [(set_attr "type" "store")])
+
+;; Sometimes memory accesses do not get combined with the store_gbr insn,
+;; in particular when the displacements are in the range of the regular move
+;; insns.  Thus, in the first split pass after the combine pass we search
+;; for missed opportunities and try to fix them up ourselves.
+;; If an equivalent GBR address can be determined the load / store is split
+;; into one of the GBR load / store patterns.
+;; All of that must happen before reload (GBR address modes use R0 as the
+;; other operand) and there's no point of doing it if the GBR is not
+;; referenced in a function at all.
+(define_split
+  [(set (match_operand:QIHISI 0 "register_operand")
+       (match_operand:QIHISI 1 "memory_operand"))]
+  "TARGET_SH1 && !reload_in_progress && !reload_completed
+   && df_regs_ever_live_p (GBR_REG)"
+  [(set (match_dup 0) (match_dup 1))]
+{
+  rtx gbr_mem = sh_find_equiv_gbr_addr (curr_insn, operands[1]);
+  if (gbr_mem != NULL_RTX)
+    operands[1] = change_address (operands[1], GET_MODE (operands[1]), gbr_mem);
+  else
+    FAIL;
+})
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand")
+       (sign_extend:SI (match_operand:QIHI 1 "memory_operand")))]
+  "TARGET_SH1 && !reload_in_progress && !reload_completed
+   && df_regs_ever_live_p (GBR_REG)"
+  [(set (match_dup 0) (sign_extend:SI (match_dup 1)))]
+{
+  rtx gbr_mem = sh_find_equiv_gbr_addr (curr_insn, operands[1]);
+  if (gbr_mem != NULL_RTX)
+    operands[1] = change_address (operands[1], GET_MODE (operands[1]), gbr_mem);
+  else
+    FAIL;
+})
+
+;; On SH2A we've got movu.b and movu.w for doing zero-extending mem loads.
+;; Split those so that a GBR load can be used.
+(define_split
+  [(set (match_operand:SI 0 "register_operand")
+       (zero_extend:SI (match_operand:QIHI 1 "memory_operand")))]
+  "TARGET_SH2A && !reload_in_progress && !reload_completed
+   && df_regs_ever_live_p (GBR_REG)"
+  [(set (match_dup 2) (match_dup 1))
+   (set (match_dup 0) (zero_extend:SI (match_dup 2)))]
+{
+  rtx gbr_mem = sh_find_equiv_gbr_addr (curr_insn, operands[1]);
+  if (gbr_mem != NULL_RTX)
+    {
+      operands[2] = gen_reg_rtx (GET_MODE (operands[1]));
+      operands[1] = change_address (operands[1], GET_MODE (operands[1]),
+                                   gbr_mem);
+    }
+  else
+    FAIL;
+})
+
+(define_split
+  [(set (match_operand:QIHISI 0 "memory_operand")
+       (match_operand:QIHISI 1 "register_operand"))]
+  "TARGET_SH1 && !reload_in_progress && !reload_completed
+   && df_regs_ever_live_p (GBR_REG)"
+  [(set (match_dup 0) (match_dup 1))]
+{
+  rtx gbr_mem = sh_find_equiv_gbr_addr (curr_insn, operands[0]);
+  if (gbr_mem != NULL_RTX)
+    operands[0] = change_address (operands[0], GET_MODE (operands[0]), gbr_mem);
+  else
+    FAIL;
+})
+
 ;;------------------------------------------------------------------------------
 ;; case instruction for switch statements.
 
index a1c4a3272f7a7789ecf2c5732cd6707d8211fe3c..5eb6c6d44ec0bbb7b73b97023522cd2acaf6dc90 100644 (file)
@@ -1,3 +1,9 @@
+2012-10-08  Oleg Endo  <olegendo@gcc.gnu.org>
+
+       PR target/54760
+       * gcc.target/sh/pr54760-2.c: New.
+       * gcc.target/sh/pr54760-3.c: New.
+
 2012-10-07  Paolo Carlini  <paolo.carlini@oracle.com>
 
        PR c++/51422
diff --git a/gcc/testsuite/gcc.target/sh/pr54760-2.c b/gcc/testsuite/gcc.target/sh/pr54760-2.c
new file mode 100644 (file)
index 0000000..b8a5018
--- /dev/null
@@ -0,0 +1,223 @@
+/* Check that thread pointer relative memory accesses are converted to
+   gbr displacement address modes.  If we see a gbr register store
+   instruction something is not working properly.  */
+/* { dg-do compile { target "sh*-*-*" } } */
+/* { dg-options "-O1" } */
+/* { dg-skip-if "" { "sh*-*-*" } { "-m5*"} { "" } }  */
+/* { dg-final { scan-assembler-times "stc\tgbr" 0 } } */
+
+/* ---------------------------------------------------------------------------
+  Simple GBR load.
+*/
+#define func(name, type, disp)\
+  int \
+  name ## _tp_load (void) \
+  { \
+    type* tp = (type*)__builtin_thread_pointer (); \
+    return tp[disp]; \
+  }
+
+func (test00, int, 0)
+func (test01, int, 5)
+func (test02, int, 255)
+
+func (test03, short, 0)
+func (test04, short, 5)
+func (test05, short, 255)
+
+func (test06, char, 0)
+func (test07, char, 5)
+func (test08, char, 255)
+
+func (test09, unsigned int, 0)
+func (test10, unsigned int, 5)
+func (test11, unsigned int, 255)
+
+func (test12, unsigned short, 0)
+func (test13, unsigned short, 5)
+func (test14, unsigned short, 255)
+
+func (test15, unsigned char, 0)
+func (test16, unsigned char, 5)
+func (test17, unsigned char, 255)
+
+#undef func
+
+/* ---------------------------------------------------------------------------
+  Simple GBR store.
+*/
+#define func(name, type, disp)\
+  void \
+  name ## _tp_store (int a) \
+  { \
+    type* tp = (type*)__builtin_thread_pointer (); \
+    tp[disp] = (type)a; \
+  }
+
+func (test00, int, 0)
+func (test01, int, 5)
+func (test02, int, 255)
+
+func (test03, short, 0)
+func (test04, short, 5)
+func (test05, short, 255)
+
+func (test06, char, 0)
+func (test07, char, 5)
+func (test08, char, 255)
+
+func (test09, unsigned int, 0)
+func (test10, unsigned int, 5)
+func (test11, unsigned int, 255)
+
+func (test12, unsigned short, 0)
+func (test13, unsigned short, 5)
+func (test14, unsigned short, 255)
+
+func (test15, unsigned char, 0)
+func (test16, unsigned char, 5)
+func (test17, unsigned char, 255)
+
+#undef func
+
+/* ---------------------------------------------------------------------------
+  Arithmetic on the result of a GBR load.
+*/
+#define func(name, type, disp, op, opname)\
+  int \
+  name ## _tp_load_arith_ ##opname (int a) \
+  { \
+    type* tp = (type*)__builtin_thread_pointer (); \
+    return tp[disp] op a; \
+  }
+
+#define funcs(op, opname) \
+  func (test00, int, 0, op, opname) \
+  func (test01, int, 5, op, opname) \
+  func (test02, int, 255, op, opname) \
+  func (test03, short, 0, op, opname) \
+  func (test04, short, 5, op, opname) \
+  func (test05, short, 255, op, opname) \
+  func (test06, char, 0, op, opname) \
+  func (test07, char, 5, op, opname) \
+  func (test08, char, 255, op, opname) \
+  func (test09, unsigned int, 0, op, opname) \
+  func (test10, unsigned int, 5, op, opname) \
+  func (test11, unsigned int, 255, op, opname) \
+  func (test12, unsigned short, 0, op, opname) \
+  func (test13, unsigned short, 5, op, opname) \
+  func (test14, unsigned short, 255, op, opname) \
+  func (test15, unsigned char, 0, op, opname) \
+  func (test16, unsigned char, 5, op, opname) \
+  func (test17, unsigned char, 255, op, opname) \
+
+funcs (+, plus)
+funcs (-, minus)
+funcs (*, mul)
+funcs (&, and)
+funcs (|, or)
+funcs (^, xor)
+
+#undef funcs
+#undef func
+
+/* ---------------------------------------------------------------------------
+  Arithmetic of the result of two GBR loads.
+*/
+#define func(name, type, disp0, disp1, op, opname)\
+  int \
+  name ## _tp_load_load_arith_ ##opname (void) \
+  { \
+    type* tp = (type*)__builtin_thread_pointer (); \
+    return tp[disp0] op tp[disp1]; \
+  }
+
+#define funcs(op, opname) \
+  func (test00, int, 0, 5, op, opname) \
+  func (test02, int, 1, 255, op, opname) \
+  func (test03, short, 0, 5, op, opname) \
+  func (test05, short, 1, 255, op, opname) \
+  func (test06, char, 0, 5, op, opname) \
+  func (test08, char, 1, 255, op, opname) \
+  func (test09, unsigned int, 0, 5, op, opname) \
+  func (test11, unsigned int, 1, 255, op, opname) \
+  func (test12, unsigned short, 0, 5, op, opname) \
+  func (test14, unsigned short, 1, 255, op, opname) \
+  func (test15, unsigned char, 0, 5, op, opname) \
+  func (test17, unsigned char, 1, 255, op, opname) \
+
+funcs (+, plus)
+funcs (-, minus)
+funcs (*, mul)
+funcs (&, and)
+funcs (|, or)
+funcs (^, xor)
+
+#undef funcs
+#undef func
+
+/* ---------------------------------------------------------------------------
+  GBR load GBR store copy.
+*/
+
+#define func(name, type, disp0, disp1)\
+  void \
+  name ## _tp_copy (void) \
+  { \
+    type* tp = (type*)__builtin_thread_pointer (); \
+    tp[disp0] = tp[disp1]; \
+  }
+
+func (test00, int, 0, 5)
+func (test02, int, 1, 255)
+func (test03, short, 0, 5)
+func (test05, short, 1, 255)
+func (test06, char, 0, 5)
+func (test08, char, 1, 255)
+func (test09, unsigned int, 0, 5)
+func (test11, unsigned int, 1, 255)
+func (test12, unsigned short, 0, 5)
+func (test14, unsigned short, 1, 255)
+func (test15, unsigned char, 0, 5)
+func (test17, unsigned char, 1, 255)
+
+#undef func
+
+/* ---------------------------------------------------------------------------
+  GBR load, arithmetic, GBR store
+*/
+
+#define func(name, type, disp, op, opname)\
+  void \
+  name ## _tp_load_arith_store_ ##opname (int a) \
+  { \
+    type* tp = (type*)__builtin_thread_pointer (); \
+    tp[disp] op a; \
+  }
+
+#define funcs(op, opname) \
+  func (test00, int, 0, op, opname) \
+  func (test01, int, 5, op, opname) \
+  func (test02, int, 255, op, opname) \
+  func (test03, short, 0, op, opname) \
+  func (test04, short, 5, op, opname) \
+  func (test05, short, 255, op, opname) \
+  func (test06, char, 0, op, opname) \
+  func (test07, char, 5, op, opname) \
+  func (test08, char, 255, op, opname) \
+  func (test09, unsigned int, 0, op, opname) \
+  func (test10, unsigned int, 5, op, opname) \
+  func (test11, unsigned int, 255, op, opname) \
+  func (test12, unsigned short, 0, op, opname) \
+  func (test13, unsigned short, 5, op, opname) \
+  func (test14, unsigned short, 255, op, opname) \
+  func (test15, unsigned char, 0, op, opname) \
+  func (test16, unsigned char, 5, op, opname) \
+  func (test17, unsigned char, 255, op, opname) \
+
+funcs (+=, plus)
+funcs (-=, minus)
+funcs (*=, mul)
+funcs (&=, and)
+funcs (|=, or)
+funcs (^=, xor)
diff --git a/gcc/testsuite/gcc.target/sh/pr54760-3.c b/gcc/testsuite/gcc.target/sh/pr54760-3.c
new file mode 100644 (file)
index 0000000..2b6f186
--- /dev/null
@@ -0,0 +1,69 @@
+/* Check that these thread relative memory accesses play along with
+   surrounding code.
+   These should be moved to C torture tests once there are target
+   independent thread_pointer built-in functions available.  */
+/* { dg-do compile { target "sh*-*-*" } } */
+/* { dg-options "-O1" } */
+/* { dg-skip-if "" { "sh*-*-*" } { "-m5*"} { "" } }  */
+
+int
+test00 (void* p, int x)
+{
+  int* tcb = (int*)__builtin_thread_pointer ();
+  int r = tcb[4];
+
+  __builtin_set_thread_pointer (p);
+
+  tcb = (int*)__builtin_thread_pointer ();
+  return tcb[255] + r;
+}
+
+int
+test01 (void)
+{
+  unsigned short* tcb = (unsigned short*)__builtin_thread_pointer ();
+  return tcb[500];
+}
+
+void
+test02 (int* x, int a, int b)
+{
+  int* tcb = (int*)__builtin_thread_pointer ();
+  tcb[50] = a;
+
+  __builtin_set_thread_pointer (x);
+  
+  tcb = (int*)__builtin_thread_pointer ();
+  tcb[40] = b;
+}
+
+int
+test03 (const int* x, int c)
+{
+  volatile int* tcb = (volatile int*)__builtin_thread_pointer ();
+
+  int s = 0;
+  int i;
+  for (i = 0; i < c; ++i)
+    s ^= x[i] + tcb[40];
+
+  return s;
+}
+
+int
+test04 (const int* x, int c, int** xx, int d)
+{
+  int s = 0;
+  int i;
+  for (i = 0; i < c; ++i)
+  {
+    volatile int* tcb = (volatile int*)__builtin_thread_pointer ();
+    tcb[20] = s;
+   __builtin_set_thread_pointer (xx[i]);
+
+    tcb = (volatile int*)__builtin_thread_pointer ();
+    s ^= x[i] + tcb[40] + d;
+  }
+  return s;
+}