Index: gcc/testsuite/gcc.target/sh/pr54760-2.c
===================================================================
--- gcc/testsuite/gcc.target/sh/pr54760-2.c	(revision 0)
+++ gcc/testsuite/gcc.target/sh/pr54760-2.c	(revision 0)
@@ -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)
Index: gcc/testsuite/gcc.target/sh/pr54760-3.c
===================================================================
--- gcc/testsuite/gcc.target/sh/pr54760-3.c	(revision 0)
+++ gcc/testsuite/gcc.target/sh/pr54760-3.c	(revision 0)
@@ -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;
+}
Index: gcc/config/sh/sh.md
===================================================================
--- gcc/config/sh/sh.md	(revision 192155)
+++ gcc/config/sh/sh.md	(working copy)
@@ -10061,6 +10061,135 @@
   [(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.
 
 ;; Operand 0 is index
Index: gcc/config/sh/predicates.md
===================================================================
--- gcc/config/sh/predicates.md	(revision 192154)
+++ gcc/config/sh/predicates.md	(working copy)
@@ -409,6 +409,14 @@
   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);
 
@@ -466,6 +474,17 @@
   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: gcc/config/sh/sh-protos.h
===================================================================
--- gcc/config/sh/sh-protos.h	(revision 192154)
+++ gcc/config/sh/sh-protos.h	(working copy)
@@ -161,7 +161,7 @@
 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: gcc/config/sh/sh.c
===================================================================
--- gcc/config/sh/sh.c	(revision 192155)
+++ gcc/config/sh/sh.c	(working copy)
@@ -3610,6 +3610,10 @@
 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 @@
   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 @@
 	  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 @@
       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 @@
 {
   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 @@
   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"
