diff --git a/gcc/rtl.h b/gcc/rtl.h
index 2370d6081614ba9e2f12d77e56cd7a4a876548cf..1ef6432fd9c16927b221c5f52c36a68f362c91ea 100644
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -2225,11 +2225,21 @@ struct address_info {
        reloading.
 
      - *BASE is a variable expression representing a base address.
-       It contains exactly one REG, SUBREG or MEM, pointed to by BASE_TERM.
+       It contains exactly one "term", pointed to by BASE_TERM.
+       This term can be one of the following:
+
+       (1) a REG, or a SUBREG of a REG
+       (2) an eliminated REG (a PLUS of (1) and a constant)
+       (3) a MEM, or a SUBREG of a MEM
+       (4) a SCRATCH
+
+       This term is the one that base_reg_class constrains.
 
      - *INDEX is a variable expression representing an index value.
        It may be a scaled expression, such as a MULT.  It has exactly
-       one REG, SUBREG or MEM, pointed to by INDEX_TERM.
+       one "term", pointed to by INDEX_TERM.  The possible terms are
+       the same as for BASE.  This term is the one that index_reg_class
+       constrains.
 
      - *DISP is a constant, possibly mutated.  DISP_TERM points to the
        unmutated RTX_CONST_OBJ.  */
diff --git a/gcc/rtlanal.cc b/gcc/rtlanal.cc
index 71207ee4f417ac40f8c8dea5dcda78956ef89108..8afbb32f2206096f094e92e9f7a615804a568b8e 100644
--- a/gcc/rtlanal.cc
+++ b/gcc/rtlanal.cc
@@ -6494,6 +6494,25 @@ binary_scale_code_p (enum rtx_code code)
           || code == ROTATERT);
 }
 
+/* Return true if X appears to be a valid base or index term.  */
+static bool
+valid_base_or_index_term_p (rtx x)
+{
+  if (GET_CODE (x) == SCRATCH)
+    return true;
+  /* Handle what appear to be eliminated forms of a register.  If we reach
+     here, the elimination occurs outside of the outermost PLUS tree,
+     and so the elimination offset cannot be treated as a displacement
+     of the main address.  Instead, we need to treat the whole PLUS as
+     the base or index term.  The address can only be made legitimate by
+     reloading the PLUS.  */
+  if (GET_CODE (x) == PLUS && CONST_SCALAR_INT_P (XEXP (x, 1)))
+    x = XEXP (x, 0);
+  if (GET_CODE (x) == SUBREG)
+    x = SUBREG_REG (x);
+  return REG_P (x) || MEM_P (x);
+}
+
 /* If *INNER can be interpreted as a base, return a pointer to the inner term
    (see address_info).  Return null otherwise.  */
 
@@ -6502,10 +6521,7 @@ get_base_term (rtx *inner)
 {
   if (GET_CODE (*inner) == LO_SUM)
     inner = strip_address_mutations (&XEXP (*inner, 0));
-  if (REG_P (*inner)
-      || MEM_P (*inner)
-      || GET_CODE (*inner) == SUBREG
-      || GET_CODE (*inner) == SCRATCH)
+  if (valid_base_or_index_term_p (*inner))
     return inner;
   return 0;
 }
@@ -6519,10 +6535,7 @@ get_index_term (rtx *inner)
   /* At present, only constant scales are allowed.  */
   if (binary_scale_code_p (GET_CODE (*inner)) && CONSTANT_P (XEXP (*inner, 1)))
     inner = strip_address_mutations (&XEXP (*inner, 0));
-  if (REG_P (*inner)
-      || MEM_P (*inner)
-      || GET_CODE (*inner) == SUBREG
-      || GET_CODE (*inner) == SCRATCH)
+  if (valid_base_or_index_term_p (*inner))
     return inner;
   return 0;
 }