diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index c451a7eac1ca0329e4c72d051d6112f70d31afe6..da0843d3467e3b638eea8712939ebb489492d83a 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,16 @@
+2006-01-03  Steven Bosscher  <stevenb.gcc@gmail.com>
+
+	* fold-const.c (operand_equal_p): Accept a NULL operand 0 for
+	COMPONENT_REFs.
+	* emit-rtl.c (mem_attrs_htab_eq): Use iterative_hash_expr for
+	hashing trees instead of a pointer hash.
+	(mem_attrs_htab_eq): Do a deep compare instead of a pointer
+	compare for MEM_EXPR.
+
+	PR rtl-optimization/25130
+	* cse.c (exp_equiv_p): Compare MEM_ATTRS instead of MEM_ALIAS_SET
+	when comparing MEMs for GCSE
+
 2006-01-03  Ben Elliston  <bje@au.ibm.com>
 
 	* targhooks.h (default_decimal_float_supported_p): Declare.
diff --git a/gcc/cse.c b/gcc/cse.c
index 284f3fd5bea24c9899bbb855728fbee3bed35473..a352c0efadf1f17ee95b23481bfc8c0979c891f7 100644
--- a/gcc/cse.c
+++ b/gcc/cse.c
@@ -2538,16 +2538,26 @@ exp_equiv_p (rtx x, rtx y, int validate, bool for_gcse)
     case MEM:
       if (for_gcse)
 	{
-	  /* Can't merge two expressions in different alias sets, since we
-	     can decide that the expression is transparent in a block when
-	     it isn't, due to it being set with the different alias set.  */
-	  if (MEM_ALIAS_SET (x) != MEM_ALIAS_SET (y))
-	    return 0;
-
 	  /* A volatile mem should not be considered equivalent to any
 	     other.  */
 	  if (MEM_VOLATILE_P (x) || MEM_VOLATILE_P (y))
 	    return 0;
+
+	  /* Can't merge two expressions in different alias sets, since we
+	     can decide that the expression is transparent in a block when
+	     it isn't, due to it being set with the different alias set.
+
+	     Also, can't merge two expressions with different MEM_ATTRS.
+	     They could e.g. be two different entities allocated into the
+	     same space on the stack (see e.g. PR25130).  In that case, the
+	     MEM addresses can be the same, even though the two MEMs are
+	     absolutely not equivalent.  
+   
+	     But because really all MEM attributes should be the same for
+	     equivalent MEMs, we just use the invariant that MEMs that have
+	     the same attributes share the same mem_attrs data structure.  */
+	  if (MEM_ATTRS (x) != MEM_ATTRS (y))
+	    return 0;
 	}
       break;
 
diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c
index 828651041a08d5cc8fb08704a5fefb5ab8051f7b..aaab19ed4d3d1fdb2915f8d80dcd15bd0d5722da 100644
--- a/gcc/emit-rtl.c
+++ b/gcc/emit-rtl.c
@@ -254,7 +254,7 @@ mem_attrs_htab_hash (const void *x)
   return (p->alias ^ (p->align * 1000)
 	  ^ ((p->offset ? INTVAL (p->offset) : 0) * 50000)
 	  ^ ((p->size ? INTVAL (p->size) : 0) * 2500000)
-	  ^ (size_t) p->expr);
+	  ^ (size_t) iterative_hash_expr (p->expr, 0));
 }
 
 /* Returns nonzero if the value represented by X (which is really a
@@ -267,8 +267,11 @@ mem_attrs_htab_eq (const void *x, const void *y)
   mem_attrs *p = (mem_attrs *) x;
   mem_attrs *q = (mem_attrs *) y;
 
-  return (p->alias == q->alias && p->expr == q->expr && p->offset == q->offset
-	  && p->size == q->size && p->align == q->align);
+  return (p->alias == q->alias && p->offset == q->offset
+	  && p->size == q->size && p->align == q->align
+	  && (p->expr == q->expr
+	      || (p->expr != NULL_TREE && q->expr != NULL_TREE
+		  && operand_equal_p (p->expr, q->expr, 0))));
 }
 
 /* Allocate a new mem_attrs structure and insert it into the hash table if
diff --git a/gcc/fold-const.c b/gcc/fold-const.c
index 2366c754dda91340c2510fbb2a88f855705661f0..7cab3c464beee53ecfb2793f55e8c1d637ecace6 100644
--- a/gcc/fold-const.c
+++ b/gcc/fold-const.c
@@ -2635,8 +2635,11 @@ operand_equal_p (tree arg0, tree arg1, unsigned int flags)
 		  && OP_SAME_WITH_NULL (3));
 
 	case COMPONENT_REF:
-	  /* Handle operand 2 the same as for ARRAY_REF.  */
-	  return OP_SAME (0) && OP_SAME (1) && OP_SAME_WITH_NULL (2);
+	  /* Handle operand 2 the same as for ARRAY_REF.  Operand 0
+	     may be NULL when we're called to compare MEM_EXPRs.  */
+	  return OP_SAME_WITH_NULL (0)
+		 && OP_SAME (1)
+		 && OP_SAME_WITH_NULL (2);
 
 	case BIT_FIELD_REF:
 	  return OP_SAME (0) && OP_SAME (1) && OP_SAME (2);