diff --git a/gcc/gimple.c b/gcc/gimple.c
index cc7a88e822b..070304b64bf 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -1758,6 +1758,60 @@ gimple_set_bb (gimple *stmt, basic_block bb)
 }
 
 
+/* Return rhs1 of a STMT with GIMPLE_SINGLE_RHS and initialize *REF
+   to the wrapping TREE_AOREFWRAP ao_ref or if rhs1 is not wrapped,
+   wrap it.  When wrapping is not profitable initialize *REF to NULL.  */
+
+tree
+gimple_assign_rhs1_with_ao_ref (gassign *stmt, ao_ref **ref)
+{
+  tree rhs1 = stmt->op[1];
+  if (!REFERENCE_CLASS_P (rhs1))
+    {
+      *ref = NULL;
+      return rhs1;
+    }
+  if (TREE_CODE (rhs1) != TREE_AOREFWRAP)
+    {
+      rhs1 = build1 (TREE_AOREFWRAP, TREE_TYPE (rhs1), rhs1);
+      static_assert (sizeof (ao_ref) == sizeof (rhs1->aorefwrap.ref_storage),
+		     "wrong aorefwrap storage size");
+      *ref = new (&rhs1->aorefwrap.ref_storage) ao_ref;
+      ao_ref_init (*ref, TREE_OPERAND (rhs1, 0));
+      stmt->op[1] = rhs1;
+      return TREE_OPERAND (rhs1, 0);
+    }
+  *ref = reinterpret_cast <ao_ref *> (&rhs1->aorefwrap.ref_storage);
+  return TREE_OPERAND (rhs1, 0);
+}
+
+/* Return lhs of a STMT with GIMPLE_SINGLE_RHS and initialize *REF
+   to the wrapping TREE_AOREFWRAP ao_ref or if rhs1 is not wrapped,
+   wrap it.  When wrapping is not profitable initialize *REF to NULL.  */
+
+tree
+gimple_assign_lhs_with_ao_ref (gassign *stmt, ao_ref **ref)
+{
+  tree lhs = stmt->op[0];
+  if (!REFERENCE_CLASS_P (lhs))
+    {
+      *ref = NULL;
+      return lhs;
+    }
+  if (TREE_CODE (lhs) != TREE_AOREFWRAP)
+    {
+      lhs = build1 (TREE_AOREFWRAP, TREE_TYPE (lhs), lhs);
+      static_assert (sizeof (ao_ref) == sizeof (lhs->aorefwrap.ref_storage),
+		     "wrong aorefwrap storage size");
+      *ref = new (&lhs->aorefwrap.ref_storage) ao_ref;
+      ao_ref_init (*ref, TREE_OPERAND (lhs, 0));
+      stmt->op[0] = lhs;
+      return TREE_OPERAND (lhs, 0);
+    }
+  *ref = reinterpret_cast <ao_ref *> (&lhs->aorefwrap.ref_storage);
+  return TREE_OPERAND (lhs, 0);
+}
+
 /* Modify the RHS of the assignment pointed-to by GSI using the
    operands in the expression tree EXPR.
 
@@ -2094,7 +2148,13 @@ gimple_copy (gimple *stmt)
 
   /* Make copy of operands.  */
   for (i = 0; i < num_ops; i++)
-    gimple_set_op (copy, i, unshare_expr (gimple_op (stmt, i)));
+    {
+      tree op = gimple_op (stmt, i);
+      /* When we unshare OP the references in the ao_ref get stale.  */
+      if (op && TREE_CODE (op) == TREE_AOREFWRAP)
+	op = TREE_OPERAND (op, 0);
+      gimple_set_op (copy, i, unshare_expr (op));
+    }
 
   if (gimple_has_mem_ops (stmt))
     {
diff --git a/gcc/gimple.h b/gcc/gimple.h
index 303623b3ced..e3fcce5ab60 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -2593,7 +2593,10 @@ get_gimple_rhs_class (enum tree_code code)
 static inline tree
 gimple_assign_lhs (const gassign *gs)
 {
-  return gs->op[0];
+  tree lhs = gs->op[0];
+  if (TREE_CODE (lhs) == TREE_AOREFWRAP)
+    lhs = TREE_OPERAND (lhs, 0);
+  return lhs;
 }
 
 static inline tree
@@ -2644,7 +2647,10 @@ gimple_assign_set_lhs (gimple *gs, tree lhs)
 static inline tree
 gimple_assign_rhs1 (const gassign *gs)
 {
-  return gs->op[1];
+  tree rhs1 = gs->op[1];
+  if (TREE_CODE (rhs1) == TREE_AOREFWRAP)
+    rhs1 = TREE_OPERAND (rhs1, 0);
+  return rhs1;
 }
 
 static inline tree
@@ -2654,6 +2660,8 @@ gimple_assign_rhs1 (const gimple *gs)
   return gimple_assign_rhs1 (ass);
 }
 
+extern tree gimple_assign_lhs_with_ao_ref (gassign *, ao_ref **);
+extern tree gimple_assign_rhs1_with_ao_ref (gassign *, ao_ref **);
 
 /* Return a pointer to the first operand on the RHS of assignment
    statement GS.  */
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index d3d2a8d812f..437888181a5 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -1551,6 +1551,13 @@ struct GTY(()) tree_exp {
     operands[1];
 };
 
+struct GTY(()) tree_aorefwrap {
+  /* We rely on exp.operands having one declared element.  */
+  struct tree_exp exp;
+  /* We could move the definition of ao_ref here or to coretypes.h.  */
+  unsigned char ref_storage[/* sizeof (ao_ref) */ 2 * sizeof (tree) + 3 * sizeof (poly_int64) + 2 * sizeof (alias_set_type) + sizeof (tree)/*bool*/];
+};
+
 /* Immediate use linking structure.  This structure is used for maintaining
    a doubly linked list of uses of an SSA_NAME.  */
 struct GTY(()) ssa_use_operand_t {
@@ -2058,6 +2065,7 @@ union GTY ((ptr_alias (union lang_tree_node),
   struct tree_list GTY ((tag ("TS_LIST"))) list;
   struct tree_vec GTY ((tag ("TS_VEC"))) vec;
   struct tree_exp GTY ((tag ("TS_EXP"))) exp;
+  struct tree_aorefwrap GTY ((tag ("TS_AOREFWRAP"))) aorefwrap;
   struct tree_ssa_name GTY ((tag ("TS_SSA_NAME"))) ssa_name;
   struct tree_block GTY ((tag ("TS_BLOCK"))) block;
   struct tree_binfo GTY ((tag ("TS_BINFO"))) binfo;
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index e292a144967..7b4e701b2d5 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -1029,6 +1029,10 @@ remap_gimple_op_r (tree *tp, int *walk_subtrees, void *data)
   bool is_lhs = wi_p->is_lhs;
   wi_p->is_lhs = false;
 
+  /* Instead of possibly re-mapping ao_ref ref/base simply drop the cache.  */
+  if (TREE_CODE (*tp) == TREE_AOREFWRAP)
+    *tp = TREE_OPERAND (*tp, 0);
+
   if (TREE_CODE (*tp) == SSA_NAME)
     {
       *tp = remap_ssa_name (*tp, id);
diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
index ce667ff32b9..670d03748a8 100644
--- a/gcc/tree-ssa-alias.c
+++ b/gcc/tree-ssa-alias.c
@@ -3144,12 +3144,17 @@ stmt_may_clobber_ref_p_1 (gimple *stmt, ao_ref *ref, bool tbaa_p)
     }
   else if (gimple_assign_single_p (stmt))
     {
-      tree lhs = gimple_assign_lhs (stmt);
+      ao_ref *rp;
+      tree lhs = gimple_assign_lhs_with_ao_ref (as_a <gassign *> (stmt), &rp);
       if (TREE_CODE (lhs) != SSA_NAME)
 	{
 	  ao_ref r;
-	  ao_ref_init (&r, lhs);
-	  return refs_may_alias_p_1 (ref, &r, tbaa_p);
+	  if (!rp)
+	    {
+	      ao_ref_init (&r, lhs);
+	      rp = &r;
+	    }
+	  return refs_may_alias_p_1 (ref, rp, tbaa_p);
 	}
     }
   else if (gimple_code (stmt) == GIMPLE_ASM)
diff --git a/gcc/tree-ssa-loop-im.c b/gcc/tree-ssa-loop-im.c
index 4b187c2cdaf..ae940576e19 100644
--- a/gcc/tree-ssa-loop-im.c
+++ b/gcc/tree-ssa-loop-im.c
@@ -1500,6 +1500,9 @@ gather_mem_refs_stmt (class loop *loop, gimple *stmt)
 	 make sure we can canonicalize the ref in the hashtable if
 	 non-operand_equal_p refs are found.  For the lookup we mark
 	 the case we want strict equality with aor.max_size == -1.  */
+      tree *orig_mem = mem;
+      if (TREE_CODE (*mem) == TREE_AOREFWRAP)
+	mem = &TREE_OPERAND (*mem, 0);
       ao_ref aor;
       ao_ref_init (&aor, *mem);
       ao_ref_base (&aor);
@@ -1603,7 +1606,7 @@ gather_mem_refs_stmt (class loop *loop, gimple *stmt)
 	    }
 	}
 
-      record_mem_ref_loc (ref, stmt, mem);
+      record_mem_ref_loc (ref, stmt, orig_mem);
     }
   if (is_stored)
     {
diff --git a/gcc/tree-ssa-loop-ivopts.c b/gcc/tree-ssa-loop-ivopts.c
index 4a498abe3b0..579cdce2d23 100644
--- a/gcc/tree-ssa-loop-ivopts.c
+++ b/gcc/tree-ssa-loop-ivopts.c
@@ -2302,6 +2302,9 @@ find_interesting_uses_address (struct ivopts_data *data, gimple *stmt,
   if (gimple_has_volatile_ops (stmt))
     goto fail;
 
+  if (TREE_CODE (base) == TREE_AOREFWRAP)
+    base = TREE_OPERAND (base, 0);
+
   /* Ignore bitfields for now.  Not really something terribly complicated
      to handle.  TODO.  */
   if (TREE_CODE (base) == BIT_FIELD_REF)
diff --git a/gcc/tree-ssa-loop.c b/gcc/tree-ssa-loop.c
index 1bbf2f1fb2c..10ba24c5f07 100644
--- a/gcc/tree-ssa-loop.c
+++ b/gcc/tree-ssa-loop.c
@@ -600,6 +600,7 @@ for_each_index (tree *addr_p, bool (*cbck) (tree, tree *, void *), void *data)
 	case VIEW_CONVERT_EXPR:
 	case REALPART_EXPR:
 	case IMAGPART_EXPR:
+	case TREE_AOREFWRAP:
 	  nxt = &TREE_OPERAND (*addr_p, 0);
 	  break;
 
diff --git a/gcc/tree-ssa-operands.c b/gcc/tree-ssa-operands.c
index ebf7eea3b04..4c7910518c4 100644
--- a/gcc/tree-ssa-operands.c
+++ b/gcc/tree-ssa-operands.c
@@ -826,6 +826,7 @@ operands_scanner::get_expr_operands (tree *expr_p, int flags)
     case COMPONENT_REF:
     case REALPART_EXPR:
     case IMAGPART_EXPR:
+    case TREE_AOREFWRAP:
       {
 	if (!(flags & opf_no_vops)
 	    && TREE_THIS_VOLATILE (expr))
diff --git a/gcc/tree-streamer.c b/gcc/tree-streamer.c
index 5a8406ffb3d..e23844bdfef 100644
--- a/gcc/tree-streamer.c
+++ b/gcc/tree-streamer.c
@@ -81,6 +81,7 @@ streamer_check_handled_ts_structures (void)
   handled_p[TS_LIST] = true;
   handled_p[TS_VEC] = true;
   handled_p[TS_EXP] = true;
+  handled_p[TS_AOREFWRAP] = true;
   handled_p[TS_SSA_NAME] = true;
   handled_p[TS_BLOCK] = true;
   handled_p[TS_BINFO] = true;
diff --git a/gcc/tree.c b/gcc/tree.c
index 7bfd64160f4..c6e82962c5c 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -480,10 +480,13 @@ tree_node_structure_for_code (enum tree_code code)
 
     case tcc_type:		return TS_TYPE_NON_COMMON;
 
+    case tcc_reference:
+      if (code == TREE_AOREFWRAP)
+	return TS_AOREFWRAP;
+      /* Fallthru.  */
     case tcc_binary:
     case tcc_comparison:
     case tcc_expression:
-    case tcc_reference:
     case tcc_statement:
     case tcc_unary:
     case tcc_vl_exp:		return TS_EXP;
@@ -624,6 +627,10 @@ initialize_tree_contains_struct (void)
 	  MARK_TS_DECL_COMMON (code);
 	  break;
 
+	case TS_AOREFWRAP:
+	  MARK_TS_EXP (code);
+	  break;
+
 	default:
 	  gcc_unreachable ();
 	}
@@ -998,6 +1005,9 @@ tree_code_size (enum tree_code code)
 	}
 
     case tcc_reference:   /* a reference */
+      if (code == TREE_AOREFWRAP)
+	return sizeof (struct tree_aorefwrap);
+      /* Fallthru. */
     case tcc_expression:  /* an expression */
     case tcc_statement:   /* an expression with side effects */
     case tcc_comparison:  /* a comparison expression */
@@ -4887,7 +4897,8 @@ build0 (enum tree_code code, tree tt MEM_STAT_DECL)
 tree
 build1 (enum tree_code code, tree type, tree node MEM_STAT_DECL)
 {
-  int length = sizeof (struct tree_exp);
+  /* ???  This was all tree_exp.  */
+  size_t length = tree_code_size (code);
   tree t;
 
   record_node_allocation_statistics (code, length);
diff --git a/gcc/tree.def b/gcc/tree.def
index e27bc3e2b1f..d09c77e7e4a 100644
--- a/gcc/tree.def
+++ b/gcc/tree.def
@@ -461,6 +461,10 @@ DEFTREECODE (IMAGPART_EXPR, "imagpart_expr", tcc_reference, 1)
    generating insns.  */
 DEFTREECODE (VIEW_CONVERT_EXPR, "view_convert_expr", tcc_reference, 1)
 
+/* Outermost wrapper around a reference caching an ao_ref for it.  The
+   only operand is the reference itself.  */
+DEFTREECODE (TREE_AOREFWRAP, "aoref_wrap", tcc_reference, 1)
+
 /* C unary `*'.  One operand, an expression for a pointer.  */
 DEFTREECODE (INDIRECT_REF, "indirect_ref", tcc_reference, 1)
 
diff --git a/gcc/treestruct.def b/gcc/treestruct.def
index 822fba17068..aab962a913b 100644
--- a/gcc/treestruct.def
+++ b/gcc/treestruct.def
@@ -61,6 +61,7 @@ DEFTREESTRUCT(TS_TYPE_NON_COMMON, "type non-common")
 DEFTREESTRUCT(TS_LIST, "list")
 DEFTREESTRUCT(TS_VEC, "vec")
 DEFTREESTRUCT(TS_EXP, "exp")
+DEFTREESTRUCT(TS_AOREFWRAP, "aorefwrap")
 DEFTREESTRUCT(TS_SSA_NAME, "ssa name")
 DEFTREESTRUCT(TS_BLOCK, "block")
 DEFTREESTRUCT(TS_BINFO, "binfo")
