diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 07e73eaa0fa779c9cd3291b24b087f87a4fbca62..6bc0bd6809bec612ebf71b78a3101f515b8dccc4 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,9 @@
+2011-10-12  Richard Guenther  <rguenther@suse.de>
+
+	PR tree-optimization/50700
+	* tree-object-size.c (addr_object_size): Simplify and treat
+	MEM_REF bases consistently.
+
 2011-10-12  Bernd Schmidt  <bernds@codesourcery.com>
 
 	* function.c (prepare_shrink_wrap, bb_active_p): New function.
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 70f89f9330cd27c3286b38fde585129e030fa5e0..827aa2dced8565171aa9088b605c8882de7f5ccb 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2011-10-12  Richard Guenther  <rguenther@suse.de>
+
+	PR tree-optimization/50700
+	* gcc.dg/builtin-object-size-12.c: New testcase.
+
 2011-10-12  Joseph Myers  <joseph@codesourcery.com>
 
 	PR c/50565
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-12.c b/gcc/testsuite/gcc.dg/builtin-object-size-12.c
new file mode 100644
index 0000000000000000000000000000000000000000..b21eb0071b340325360e0f4922d41276cf3b7642
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-12.c
@@ -0,0 +1,19 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+extern void abort (void);
+struct S {
+    int len;
+    char s[0];
+};
+int main()
+{
+  char buf[sizeof (struct S) + 32];
+  if (__builtin_object_size (((struct S *)&buf[0])->s, 1) != 32)
+    abort ();
+  if (__builtin_object_size (((struct S *)&buf[1])->s, 1) != 31)
+    abort ();
+  if (__builtin_object_size (((struct S *)&buf[64])->s, 0) != 0)
+    abort ();
+  return 0;
+}
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index 2998fb59da05edf6f56f64b2a11c24c5ee2904ea..017f8c5e933c9c1b9ed54b9c71c3ef4baf8e26c5 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -166,24 +166,19 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
   gcc_assert (TREE_CODE (ptr) == ADDR_EXPR);
 
   pt_var = TREE_OPERAND (ptr, 0);
-  if (REFERENCE_CLASS_P (pt_var))
-    pt_var = get_base_address (pt_var);
+  while (handled_component_p (pt_var))
+    pt_var = TREE_OPERAND (pt_var, 0);
 
   if (pt_var
-      && TREE_CODE (pt_var) == MEM_REF
-      && TREE_CODE (TREE_OPERAND (pt_var, 0)) == SSA_NAME
-      && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (pt_var, 0))))
+      && TREE_CODE (pt_var) == MEM_REF)
     {
       unsigned HOST_WIDE_INT sz;
 
-      if (!osi || (object_size_type & 1) != 0)
+      if (!osi || (object_size_type & 1) != 0
+	  || TREE_CODE (pt_var) != SSA_NAME)
 	{
 	  sz = compute_builtin_object_size (TREE_OPERAND (pt_var, 0),
 					    object_size_type & ~1);
-	  if (host_integerp (TREE_OPERAND (pt_var, 1), 0))
-	    sz -= TREE_INT_CST_LOW (TREE_OPERAND (pt_var, 1));
-	  else
-	    sz = offset_limit;
 	}
       else
 	{
@@ -195,10 +190,17 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 	    sz = object_sizes[object_size_type][SSA_NAME_VERSION (var)];
 	  else
 	    sz = unknown[object_size_type];
-	  if (host_integerp (TREE_OPERAND (pt_var, 1), 0))
-	    sz -= TREE_INT_CST_LOW (TREE_OPERAND (pt_var, 1));
+	}
+      if (sz != unknown[object_size_type])
+	{
+	  double_int dsz = double_int_sub (uhwi_to_double_int (sz),
+					   mem_ref_offset (pt_var));
+	  if (double_int_negative_p (dsz))
+	    sz = 0;
+	  else if (double_int_fits_in_uhwi_p (dsz))
+	    sz = double_int_to_uhwi (dsz);
 	  else
-	    sz = offset_limit;
+	    sz = unknown[object_size_type];
 	}
 
       if (sz != unknown[object_size_type] && sz < offset_limit)
@@ -211,7 +213,7 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 	        tree_low_cst (DECL_SIZE_UNIT (pt_var), 1) < offset_limit)
     pt_var_size = DECL_SIZE_UNIT (pt_var);
   else if (pt_var
-	   && (SSA_VAR_P (pt_var) || TREE_CODE (pt_var) == STRING_CST)
+	   && TREE_CODE (pt_var) == STRING_CST
 	   && TYPE_SIZE_UNIT (TREE_TYPE (pt_var))
 	   && host_integerp (TYPE_SIZE_UNIT (TREE_TYPE (pt_var)), 1)
 	   && (unsigned HOST_WIDE_INT)