aboutsummaryrefslogtreecommitdiff
path: root/gcc/tree.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/tree.c')
-rw-r--r--gcc/tree.c225
1 files changed, 194 insertions, 31 deletions
diff --git a/gcc/tree.c b/gcc/tree.c
index 741f7a2..3299b34 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -3089,6 +3089,25 @@ first_field (const_tree type)
return t;
}
+/* Returns the last FIELD_DECL in the TYPE_FIELDS of the RECORD_TYPE or
+ UNION_TYPE TYPE, or NULL_TREE if none. */
+
+tree
+last_field (const_tree type)
+{
+ tree last = NULL_TREE;
+
+ for (tree fld = TYPE_FIELDS (type); fld; fld = TREE_CHAIN (fld))
+ {
+ if (TREE_CODE (fld) != FIELD_DECL)
+ continue;
+
+ last = fld;
+ }
+
+ return last;
+}
+
/* Concatenate two chains of nodes (chained through TREE_CHAIN)
by modifying the last node in chain 1 to point to chain 2.
This is the Lisp primitive `nconc'. */
@@ -13363,8 +13382,8 @@ array_ref_up_bound (tree exp)
return NULL_TREE;
}
-/* Returns true if REF is an array reference or a component reference
- to an array at the end of a structure.
+/* Returns true if REF is an array reference, component reference,
+ or memory reference to an array at the end of a structure.
If this is the case, the array may be allocated larger
than its upper bound implies. */
@@ -13382,6 +13401,28 @@ array_at_struct_end_p (tree ref)
else if (TREE_CODE (ref) == COMPONENT_REF
&& TREE_CODE (TREE_TYPE (TREE_OPERAND (ref, 1))) == ARRAY_TYPE)
atype = TREE_TYPE (TREE_OPERAND (ref, 1));
+ else if (TREE_CODE (ref) == MEM_REF)
+ {
+ tree arg = TREE_OPERAND (ref, 0);
+ if (TREE_CODE (arg) == ADDR_EXPR)
+ arg = TREE_OPERAND (arg, 0);
+ tree argtype = TREE_TYPE (arg);
+ if (TREE_CODE (argtype) == RECORD_TYPE)
+ {
+ if (tree fld = last_field (argtype))
+ {
+ atype = TREE_TYPE (fld);
+ if (TREE_CODE (atype) != ARRAY_TYPE)
+ return false;
+ if (VAR_P (arg) && DECL_SIZE (fld))
+ return false;
+ }
+ else
+ return false;
+ }
+ else
+ return false;
+ }
else
return false;
@@ -13498,33 +13539,72 @@ component_ref_field_offset (tree exp)
return SUBSTITUTE_PLACEHOLDER_IN_EXPR (DECL_FIELD_OFFSET (field), exp);
}
+/* Given the initializer INIT, return the initializer for the field
+ DECL if it exists, otherwise null. Used to obtain the initializer
+ for a flexible array member and determine its size. */
+
+static tree
+get_initializer_for (tree init, tree decl)
+{
+ STRIP_NOPS (init);
+
+ tree fld, fld_init;
+ unsigned HOST_WIDE_INT i;
+ FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (init), i, fld, fld_init)
+ {
+ if (decl == fld)
+ return fld_init;
+
+ if (TREE_CODE (fld) == CONSTRUCTOR)
+ {
+ fld_init = get_initializer_for (fld_init, decl);
+ if (fld_init)
+ return fld_init;
+ }
+ }
+
+ return NULL_TREE;
+}
+
/* Determines the size of the member referenced by the COMPONENT_REF
REF, using its initializer expression if necessary in order to
determine the size of an initialized flexible array member.
+ If non-null, *INTERIOR_ZERO_LENGTH is set when REF refers to
+ an interior zero-length array.
Returns the size (which might be zero for an object with
an uninitialized flexible array member) or null if the size
cannot be determined. */
tree
-component_ref_size (tree ref)
+component_ref_size (tree ref, bool *interior_zero_length /* = NULL */)
{
gcc_assert (TREE_CODE (ref) == COMPONENT_REF);
+ bool int_0_len = false;
+ if (!interior_zero_length)
+ interior_zero_length = &int_0_len;
+
tree member = TREE_OPERAND (ref, 1);
- /* If the member is not an array, or is not last, or is an array with
- more than one element, return its size. Otherwise it's either
- a bona fide flexible array member, or a zero-length array member,
- or an array of length one treated as such. */
- tree size = DECL_SIZE_UNIT (member);
- if (size)
+ tree memsize = DECL_SIZE_UNIT (member);
+ if (memsize)
{
tree memtype = TREE_TYPE (member);
- if (TREE_CODE (memtype) != ARRAY_TYPE
- || !array_at_struct_end_p (ref))
- return size;
+ if (TREE_CODE (memtype) != ARRAY_TYPE)
+ return memsize;
+
+ bool trailing = array_at_struct_end_p (ref);
+ bool zero_length = integer_zerop (memsize);
+ if (!trailing && (!interior_zero_length || !zero_length))
+ /* MEMBER is either an interior array or is an array with
+ more than one element. */
+ return memsize;
+
+ *interior_zero_length = zero_length && !trailing;
+ if (*interior_zero_length)
+ memsize = NULL_TREE;
- if (!integer_zerop (size))
+ if (!zero_length)
if (tree dom = TYPE_DOMAIN (memtype))
if (tree min = TYPE_MIN_VALUE (dom))
if (tree max = TYPE_MAX_VALUE (dom))
@@ -13533,37 +13613,120 @@ component_ref_size (tree ref)
{
offset_int minidx = wi::to_offset (min);
offset_int maxidx = wi::to_offset (max);
- if (maxidx - minidx > 1)
- return size;
+ if (maxidx - minidx > 0)
+ /* MEMBER is an array with more than 1 element. */
+ return memsize;
}
}
+ /* MEMBER is either a bona fide flexible array member, or a zero-length
+ array member, or an array of length one treated as such. */
+
/* If the reference is to a declared object and the member a true
flexible array, try to determine its size from its initializer. */
- poly_int64 off = 0;
- tree base = get_addr_base_and_unit_offset (ref, &off);
+ poly_int64 baseoff = 0;
+ tree base = get_addr_base_and_unit_offset (ref, &baseoff);
if (!base || !VAR_P (base))
- return NULL_TREE;
+ {
+ if (!*interior_zero_length)
+ return NULL_TREE;
- /* The size of any member of a declared object other than a flexible
- array member is that obtained above. */
- if (size)
- return size;
+ if (TREE_CODE (TREE_OPERAND (ref, 0)) != COMPONENT_REF)
+ return NULL_TREE;
- if (tree init = DECL_INITIAL (base))
- if (TREE_CODE (init) == CONSTRUCTOR)
- {
- off <<= LOG2_BITS_PER_UNIT;
- init = fold_ctor_reference (NULL_TREE, init, off, 0, base);
- if (init)
- return TYPE_SIZE_UNIT (TREE_TYPE (init));
- }
+ base = TREE_OPERAND (ref, 0);
+ baseoff = tree_to_poly_int64 (byte_position (TREE_OPERAND (ref, 1)));
+ }
+
+ /* BASE is the declared object of which MEMBER is either a member
+ or that is is cast to REFTYPE (e.g., a char buffer used to store
+ a REFTYPE object). */
+ tree reftype = TREE_TYPE (TREE_OPERAND (ref, 0));
+ tree basetype = TREE_TYPE (base);
+
+ /* Determine the base type of the referenced object. If it's
+ the same as REFTYPE and MEMBER has a known size, return it. */
+ tree bt = basetype;
+ if (!*interior_zero_length)
+ while (TREE_CODE (bt) == ARRAY_TYPE)
+ bt = TREE_TYPE (bt);
+ bool typematch = useless_type_conversion_p (reftype, bt);
+ if (memsize && typematch)
+ return memsize;
+
+ memsize = NULL_TREE;
+
+ /* MEMBER is a true flexible array member. Compute its size from
+ the initializer of the BASE object if it has one. */
+ if (tree init = DECL_P (base) ? DECL_INITIAL (base) : NULL_TREE)
+ {
+ init = get_initializer_for (init, member);
+ if (init)
+ {
+ memsize = TYPE_SIZE_UNIT (TREE_TYPE (init));
+ if (tree refsize = TYPE_SIZE_UNIT (reftype))
+ {
+ /* Use the larger of the initializer size and the tail
+ padding in the enclosing struct. */
+ poly_int64 rsz = tree_to_poly_int64 (refsize);
+ rsz -= baseoff;
+ if (known_lt (tree_to_poly_int64 (memsize), rsz))
+ memsize = wide_int_to_tree (TREE_TYPE (memsize), rsz);
+ }
+
+ baseoff = 0;
+ }
+ }
+
+ if (!memsize)
+ {
+ if (typematch)
+ {
+ if (DECL_P (base)
+ && DECL_EXTERNAL (base)
+ && bt == basetype
+ && !*interior_zero_length)
+ /* The size of a flexible array member of an extern struct
+ with no initializer cannot be determined (it's defined
+ in another translation unit and can have an initializer
+ witth an arbitrary number of elements). */
+ return NULL_TREE;
+
+ /* Use the size of the base struct or, for interior zero-length
+ arrays, the size of the enclosing type. */
+ memsize = TYPE_SIZE_UNIT (bt);
+ }
+ else
+ /* Use the size of the BASE object (possibly an array of some
+ other type such as char used to store the struct). */
+ memsize = DECL_SIZE_UNIT (base);
+ }
+
+ /* If the flexible array member has a known size use the greater
+ of it and the tail padding in the enclosing struct.
+ Otherwise, when the size of the flexible array member is unknown
+ and the referenced object is not a struct, use the size of its
+ type when known. This detects sizes of array buffers when cast
+ to struct types with flexible array members. */
+ if (memsize)
+ {
+ poly_int64 memsz64 = memsize ? tree_to_poly_int64 (memsize) : 0;
+ if (known_lt (baseoff, memsz64))
+ {
+ memsz64 -= baseoff;
+ return wide_int_to_tree (TREE_TYPE (memsize), memsz64);
+ }
+ return integer_zero_node;
+ }
/* Return "don't know" for an external non-array object since its
flexible array member can be initialized to have any number of
elements. Otherwise, return zero because the flexible array
member has no elements. */
- return (DECL_EXTERNAL (base) && TREE_CODE (TREE_TYPE (base)) != ARRAY_TYPE
+ return (DECL_P (base)
+ && DECL_EXTERNAL (base)
+ && (!typematch
+ || TREE_CODE (basetype) != ARRAY_TYPE)
? NULL_TREE : integer_zero_node);
}