diff options
Diffstat (limited to 'gcc/builtins.c')
-rw-r--r-- | gcc/builtins.c | 113 |
1 files changed, 98 insertions, 15 deletions
diff --git a/gcc/builtins.c b/gcc/builtins.c index 0b25adc..f8063c1 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -72,6 +72,7 @@ along with GCC; see the file COPYING3. If not see #include "file-prefix-map.h" /* remap_macro_filename() */ #include "gomp-constants.h" #include "omp-general.h" +#include "tree-dfa.h" struct target_builtins default_target_builtins; #if SWITCHABLE_TARGET @@ -3561,6 +3562,54 @@ check_access (tree exp, tree, tree, tree dstwrite, return true; } +/* 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. + Returns the size (which might be zero for an object with + an uninitialized flexible array member) or null if the size + cannot be determined. */ + +static tree +component_size (tree ref) +{ + gcc_assert (TREE_CODE (ref) == COMPONENT_REF); + + tree member = TREE_OPERAND (ref, 1); + + /* If the member is not last or has a size greater than one, return + it. Otherwise it's either a 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 + && (!array_at_struct_end_p (ref) + || (!integer_zerop (size) + && !integer_onep (size)))) + return size; + + /* 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); + if (!base || !VAR_P (base)) + 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 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)); + } + + return DECL_EXTERNAL (base) ? NULL_TREE : integer_zero_node; +} + /* Helper to compute the size of the object referenced by the DEST expression which must have pointer type, using Object Size type OSTYPE (only the least significant 2 bits are used). Return @@ -3568,12 +3617,18 @@ check_access (tree exp, tree, tree, tree dstwrite, the size cannot be determined. When the referenced object involves a non-constant offset in some range the returned value represents the largest size given the smallest non-negative offset in the - range. The function is intended for diagnostics and should not - be used to influence code generation or optimization. */ + range. If nonnull, set *PDECL to the decl of the referenced + subobject if it can be determined, or to null otherwise. + The function is intended for diagnostics and should not be used + to influence code generation or optimization. */ tree -compute_objsize (tree dest, int ostype) +compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */) { + tree dummy = NULL_TREE; + if (!pdecl) + pdecl = &dummy; + unsigned HOST_WIDE_INT size; /* Only the two least significant bits are meaningful. */ @@ -3600,7 +3655,7 @@ compute_objsize (tree dest, int ostype) tree off = gimple_assign_rhs2 (stmt); if (TREE_CODE (off) == INTEGER_CST) { - if (tree size = compute_objsize (dest, ostype)) + if (tree size = compute_objsize (dest, ostype, pdecl)) { wide_int wioff = wi::to_wide (off); wide_int wisiz = wi::to_wide (size); @@ -3625,7 +3680,7 @@ compute_objsize (tree dest, int ostype) if (rng == VR_RANGE) { - if (tree size = compute_objsize (dest, ostype)) + if (tree size = compute_objsize (dest, ostype, pdecl)) { wide_int wisiz = wi::to_wide (size); @@ -3653,12 +3708,31 @@ compute_objsize (tree dest, int ostype) if (!ostype) return NULL_TREE; - if (TREE_CODE (dest) == MEM_REF) + if (TREE_CODE (dest) == ARRAY_REF + || TREE_CODE (dest) == MEM_REF) { tree ref = TREE_OPERAND (dest, 0); tree off = TREE_OPERAND (dest, 1); - if (tree size = compute_objsize (ref, ostype)) + if (tree size = compute_objsize (ref, ostype, pdecl)) { + /* If the declaration of the destination object is known + to have zero size, return zero. */ + if (integer_zerop (size)) + return integer_zero_node; + + if (TREE_CODE (off) != INTEGER_CST + || TREE_CODE (size) != INTEGER_CST) + return NULL_TREE; + + if (TREE_CODE (dest) == ARRAY_REF) + { + tree eltype = TREE_TYPE (dest); + if (tree tpsize = TYPE_SIZE_UNIT (eltype)) + off = fold_build2 (MULT_EXPR, size_type_node, off, tpsize); + else + return NULL_TREE; + } + if (tree_int_cst_lt (off, size)) return fold_build2 (MINUS_EXPR, size_type_node, size, off); return integer_zero_node; @@ -3667,9 +3741,22 @@ compute_objsize (tree dest, int ostype) return NULL_TREE; } + if (TREE_CODE (dest) == COMPONENT_REF) + { + *pdecl = TREE_OPERAND (dest, 1); + return component_size (dest); + } + if (TREE_CODE (dest) != ADDR_EXPR) return NULL_TREE; + tree ref = TREE_OPERAND (dest, 0); + if (DECL_P (ref)) + { + *pdecl = ref; + return DECL_SIZE_UNIT (ref); + } + tree type = TREE_TYPE (dest); if (TREE_CODE (type) == POINTER_TYPE) type = TREE_TYPE (type); @@ -3677,14 +3764,10 @@ compute_objsize (tree dest, int ostype) type = TYPE_MAIN_VARIANT (type); if (TREE_CODE (type) == ARRAY_TYPE - && !array_at_struct_end_p (TREE_OPERAND (dest, 0))) - { - /* Return the constant size unless it's zero (that's a zero-length - array likely at the end of a struct). */ - tree size = TYPE_SIZE_UNIT (type); - if (size && TREE_CODE (size) == INTEGER_CST - && !integer_zerop (size)) - return size; + && !array_at_struct_end_p (ref)) + { + if (tree size = TYPE_SIZE_UNIT (type)) + return TREE_CODE (size) == INTEGER_CST ? size : NULL_TREE; } return NULL_TREE; |