+/* Attempt to express (ORIG_TYPE)&BASE+OFFSET as &BASE->field_of_orig_type
+ or &BASE[index] or by combination of those.
+
+ Before attempting the conversion strip off existing component refs. */
+
+tree
+maybe_fold_offset_to_address (tree addr, tree offset, tree orig_type)
+{
+ tree t;
+
+ gcc_assert (POINTER_TYPE_P (TREE_TYPE (addr))
+ && POINTER_TYPE_P (orig_type));
+
+ t = maybe_fold_offset_to_reference (addr, offset, TREE_TYPE (orig_type));
+ if (t != NULL_TREE)
+ {
+ tree orig = addr;
+ tree ptr_type;
+
+ /* For __builtin_object_size to function correctly we need to
+ make sure not to fold address arithmetic so that we change
+ reference from one array to another. This would happen for
+ example for
+
+ struct X { char s1[10]; char s2[10] } s;
+ char *foo (void) { return &s.s2[-4]; }
+
+ where we need to avoid generating &s.s1[6]. As the C and
+ C++ frontends create different initial trees
+ (char *) &s.s1 + -4 vs. &s.s1[-4] we have to do some
+ sophisticated comparisons here. Note that checking for the
+ condition after the fact is easier than trying to avoid doing
+ the folding. */
+ STRIP_NOPS (orig);
+ if (TREE_CODE (orig) == ADDR_EXPR)
+ orig = TREE_OPERAND (orig, 0);
+ if ((TREE_CODE (orig) == ARRAY_REF
+ || (TREE_CODE (orig) == COMPONENT_REF
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (orig, 1))) == ARRAY_TYPE))
+ && (TREE_CODE (t) == ARRAY_REF
+ || (TREE_CODE (t) == COMPONENT_REF
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 1))) == ARRAY_TYPE))
+ && !operand_equal_p (TREE_CODE (orig) == ARRAY_REF
+ ? TREE_OPERAND (orig, 0) : orig,
+ TREE_CODE (t) == ARRAY_REF
+ ? TREE_OPERAND (t, 0) : t, 0))
+ return NULL_TREE;
+
+ ptr_type = build_pointer_type (TREE_TYPE (t));
+ if (!useless_type_conversion_p (orig_type, ptr_type))
+ return NULL_TREE;
+ return build_fold_addr_expr_with_type (t, ptr_type);
+ }
+
+ return NULL_TREE;
+}
+