aboutsummaryrefslogtreecommitdiff
path: root/gcc/builtins.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/builtins.c')
-rw-r--r--gcc/builtins.c113
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;