mirror of
https://gcc.gnu.org/git/gcc.git
synced 2024-12-25 12:05:06 +08:00
tree.c (init_ttree): Add STRUCT_FIELD_TAG handling.
2006-02-15 Daniel Berlin <dberlin@dberlin.org> * tree.c (init_ttree): Add STRUCT_FIELD_TAG handling. (tree_code_size): Ditto. * tree.h (struct tree_memory_tag): Remove parent_var. (struct tree_struct_field_tag): New. (SFT_OFFSET): New. (SFT_SIZE): New. (union tree_node): Add sft member. * tree-ssa-alias.c (get_tmt_for): Don't handle TYPE_READONLY specially here. (create_sft): Add size and offset argument, set SFT_OFFSET and SFT_SIZE. (create_overlap_variables_for): Update for SFT_OFFSET/SFT_SIZE. * treestruct.def: Add TS_STRUCT_FIELD_TAG. * tree-flow-inline.h (get_subvar_at): Update for SFT_OFFSET/SFT_SIZE. (var_can_have_subvars): Ditto. (overlap_subvar): Ditto. * print-tree.c (print_node): Print out interesting things for SFT's. * tree-flow.h (struct subvar): Remove offset and size members. * tree-ssa-operands.c (get_expr_operands): Update for get_indirect_ref_operands changes. (get_indirect_ref_operands): Call add_virtual_operand instead of add_stmt_operand. Only recurse on base var if requested. (access_can_touch_variable): New function. (add_stmt_operand): Split virtual operand handling into ... (add_virtual_operand): Here. Add offset, size, and for_clobber arguments. Prune alias sets. (add_call_clobber_ops): Call add_virtual_operand. From-SVN: r111120
This commit is contained in:
parent
cce283c7fe
commit
3c0b6c4300
@ -1,3 +1,35 @@
|
||||
2006-02-15 Daniel Berlin <dberlin@dberlin.org>
|
||||
|
||||
* tree.c (init_ttree): Add STRUCT_FIELD_TAG handling.
|
||||
(tree_code_size): Ditto.
|
||||
* tree.h (struct tree_memory_tag): Remove parent_var.
|
||||
(struct tree_struct_field_tag): New.
|
||||
(SFT_OFFSET): New.
|
||||
(SFT_SIZE): New.
|
||||
(union tree_node): Add sft member.
|
||||
* tree-ssa-alias.c (get_tmt_for): Don't handle TYPE_READONLY
|
||||
specially here.
|
||||
(create_sft): Add size and offset argument, set SFT_OFFSET and
|
||||
SFT_SIZE.
|
||||
(create_overlap_variables_for): Update for SFT_OFFSET/SFT_SIZE.
|
||||
* treestruct.def: Add TS_STRUCT_FIELD_TAG.
|
||||
* tree-flow-inline.h (get_subvar_at): Update for
|
||||
SFT_OFFSET/SFT_SIZE.
|
||||
(var_can_have_subvars): Ditto.
|
||||
(overlap_subvar): Ditto.
|
||||
* print-tree.c (print_node): Print out interesting things for
|
||||
SFT's.
|
||||
* tree-flow.h (struct subvar): Remove offset and size members.
|
||||
* tree-ssa-operands.c (get_expr_operands): Update for
|
||||
get_indirect_ref_operands changes.
|
||||
(get_indirect_ref_operands): Call add_virtual_operand instead of
|
||||
add_stmt_operand. Only recurse on base var if requested.
|
||||
(access_can_touch_variable): New function.
|
||||
(add_stmt_operand): Split virtual operand handling into ...
|
||||
(add_virtual_operand): Here. Add offset, size, and for_clobber
|
||||
arguments. Prune alias sets.
|
||||
(add_call_clobber_ops): Call add_virtual_operand.
|
||||
|
||||
2006-02-15 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
PR middle-end/26300
|
||||
|
@ -510,6 +510,15 @@ print_node (FILE *file, const char *prefix, tree node, int indent)
|
||||
&& DECL_HAS_VALUE_EXPR_P (node))
|
||||
print_node (file, "value-expr", DECL_VALUE_EXPR (node), indent + 4);
|
||||
|
||||
if (TREE_CODE (node) == STRUCT_FIELD_TAG)
|
||||
{
|
||||
fprintf (file, " sft size " HOST_WIDE_INT_PRINT_DEC,
|
||||
SFT_SIZE (node));
|
||||
fprintf (file, " sft offset " HOST_WIDE_INT_PRINT_DEC,
|
||||
SFT_OFFSET (node));
|
||||
print_node_brief (file, "parent var", SFT_PARENT_VAR (node),
|
||||
indent + 4);
|
||||
}
|
||||
/* Print the decl chain only if decl is at second level. */
|
||||
if (indent == 4)
|
||||
print_node (file, "chain", TREE_CHAIN (node), indent + 4);
|
||||
|
@ -1450,7 +1450,7 @@ get_subvar_at (tree var, unsigned HOST_WIDE_INT offset)
|
||||
subvar_t sv;
|
||||
|
||||
for (sv = get_subvars_for_var (var); sv; sv = sv->next)
|
||||
if (sv->offset == offset)
|
||||
if (SFT_OFFSET (sv->var) == offset)
|
||||
return sv->var;
|
||||
|
||||
return NULL_TREE;
|
||||
@ -1491,7 +1491,7 @@ var_can_have_subvars (tree v)
|
||||
|
||||
static inline bool
|
||||
overlap_subvar (unsigned HOST_WIDE_INT offset, unsigned HOST_WIDE_INT size,
|
||||
subvar_t sv, bool *exact)
|
||||
tree sv, bool *exact)
|
||||
{
|
||||
/* There are three possible cases of overlap.
|
||||
1. We can have an exact overlap, like so:
|
||||
@ -1511,17 +1511,19 @@ overlap_subvar (unsigned HOST_WIDE_INT offset, unsigned HOST_WIDE_INT size,
|
||||
|
||||
if (exact)
|
||||
*exact = false;
|
||||
if (offset == sv->offset && size == sv->size)
|
||||
if (offset == SFT_OFFSET (sv) && size == SFT_SIZE (sv))
|
||||
{
|
||||
if (exact)
|
||||
*exact = true;
|
||||
return true;
|
||||
}
|
||||
else if (offset >= sv->offset && offset < (sv->offset + sv->size))
|
||||
else if (offset >= SFT_OFFSET (sv)
|
||||
&& offset < (SFT_OFFSET (sv) + SFT_SIZE (sv)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (offset < sv->offset && (size > sv->offset - offset))
|
||||
else if (offset < SFT_OFFSET (sv)
|
||||
&& (size > SFT_OFFSET (sv) - offset))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -149,12 +149,6 @@ struct subvar GTY(())
|
||||
/* Fake variable. */
|
||||
tree var;
|
||||
|
||||
/* Offset inside structure. */
|
||||
unsigned HOST_WIDE_INT offset;
|
||||
|
||||
/* Size of the field. */
|
||||
unsigned HOST_WIDE_INT size;
|
||||
|
||||
/* Next subvar for this structure. */
|
||||
subvar_t next;
|
||||
};
|
||||
@ -610,7 +604,7 @@ extern tree get_ref_base_and_extent (tree, HOST_WIDE_INT *,
|
||||
static inline bool var_can_have_subvars (tree);
|
||||
static inline bool overlap_subvar (unsigned HOST_WIDE_INT,
|
||||
unsigned HOST_WIDE_INT,
|
||||
subvar_t, bool *);
|
||||
tree, bool *);
|
||||
|
||||
/* Call-back function for walk_use_def_chains(). At each reaching
|
||||
definition, a function with this prototype is called. */
|
||||
|
@ -2061,8 +2061,7 @@ get_tmt_for (tree ptr, struct alias_info *ai)
|
||||
{
|
||||
struct alias_map_d *curr = ai->pointers[i];
|
||||
tree curr_tag = var_ann (curr->var)->type_mem_tag;
|
||||
if (tag_set == curr->set
|
||||
&& TYPE_READONLY (tag_type) == TYPE_READONLY (TREE_TYPE (curr_tag)))
|
||||
if (tag_set == curr->set)
|
||||
{
|
||||
tag = curr_tag;
|
||||
break;
|
||||
@ -2099,10 +2098,6 @@ get_tmt_for (tree ptr, struct alias_info *ai)
|
||||
pointed-to type. */
|
||||
gcc_assert (tag_set == get_alias_set (tag));
|
||||
|
||||
/* If PTR's pointed-to type is read-only, then TAG's type must also
|
||||
be read-only. */
|
||||
gcc_assert (TYPE_READONLY (tag_type) == TYPE_READONLY (TREE_TYPE (tag)));
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
@ -2749,11 +2744,12 @@ get_or_create_used_part_for (size_t uid)
|
||||
}
|
||||
|
||||
|
||||
/* Create and return a structure sub-variable for field type FIELD of
|
||||
variable VAR. */
|
||||
/* Create and return a structure sub-variable for field type FIELD at
|
||||
offset OFFSET, with size SIZE, of variable VAR. */
|
||||
|
||||
static tree
|
||||
create_sft (tree var, tree field)
|
||||
create_sft (tree var, tree field, unsigned HOST_WIDE_INT offset,
|
||||
unsigned HOST_WIDE_INT size)
|
||||
{
|
||||
var_ann_t ann;
|
||||
tree subvar = create_tag_raw (STRUCT_FIELD_TAG, field, "SFT");
|
||||
@ -2771,7 +2767,8 @@ create_sft (tree var, tree field)
|
||||
ann->type_mem_tag = NULL;
|
||||
add_referenced_tmp_var (subvar);
|
||||
SFT_PARENT_VAR (subvar) = var;
|
||||
|
||||
SFT_OFFSET (subvar) = offset;
|
||||
SFT_SIZE (subvar) = size;
|
||||
return subvar;
|
||||
}
|
||||
|
||||
@ -2882,19 +2879,17 @@ create_overlap_variables_for (tree var)
|
||||
&& currfotype == lastfotype))
|
||||
continue;
|
||||
sv = GGC_NEW (struct subvar);
|
||||
sv->offset = fo->offset;
|
||||
sv->size = fosize;
|
||||
sv->next = *subvars;
|
||||
sv->var = create_sft (var, fo->type);
|
||||
sv->var = create_sft (var, fo->type, fo->offset, fosize);
|
||||
|
||||
if (dump_file)
|
||||
{
|
||||
fprintf (dump_file, "structure field tag %s created for var %s",
|
||||
get_name (sv->var), get_name (var));
|
||||
fprintf (dump_file, " offset " HOST_WIDE_INT_PRINT_DEC,
|
||||
sv->offset);
|
||||
SFT_OFFSET (sv->var));
|
||||
fprintf (dump_file, " size " HOST_WIDE_INT_PRINT_DEC,
|
||||
sv->size);
|
||||
SFT_SIZE (sv->var));
|
||||
fprintf (dump_file, "\n");
|
||||
}
|
||||
|
||||
|
@ -128,7 +128,8 @@ static unsigned operand_memory_index;
|
||||
|
||||
static void get_expr_operands (tree, tree *, int);
|
||||
static void get_asm_expr_operands (tree);
|
||||
static void get_indirect_ref_operands (tree, tree, int);
|
||||
static void get_indirect_ref_operands (tree, tree, int, tree, HOST_WIDE_INT,
|
||||
HOST_WIDE_INT, bool);
|
||||
static void get_tmr_operands (tree, tree, int);
|
||||
static void get_call_expr_operands (tree, tree);
|
||||
static inline void append_def (tree *);
|
||||
@ -138,6 +139,9 @@ static void append_v_must_def (tree);
|
||||
static void add_call_clobber_ops (tree, tree);
|
||||
static void add_call_read_ops (tree, tree);
|
||||
static void add_stmt_operand (tree *, stmt_ann_t, int);
|
||||
static void add_virtual_operand (tree, stmt_ann_t, int, tree,
|
||||
HOST_WIDE_INT, HOST_WIDE_INT,
|
||||
bool);
|
||||
static void build_ssa_operands (tree stmt);
|
||||
|
||||
static def_optype_p free_defs = NULL;
|
||||
@ -1123,7 +1127,8 @@ get_expr_operands (tree stmt, tree *expr_p, int flags)
|
||||
|
||||
case ALIGN_INDIRECT_REF:
|
||||
case INDIRECT_REF:
|
||||
get_indirect_ref_operands (stmt, expr, flags);
|
||||
get_indirect_ref_operands (stmt, expr, flags, NULL_TREE,
|
||||
0, -1, true);
|
||||
return;
|
||||
|
||||
case TARGET_MEM_REF:
|
||||
@ -1165,7 +1170,7 @@ get_expr_operands (tree stmt, tree *expr_p, int flags)
|
||||
for (sv = svars; sv; sv = sv->next)
|
||||
{
|
||||
bool exact;
|
||||
if (overlap_subvar (offset, maxsize, sv, &exact))
|
||||
if (overlap_subvar (offset, maxsize, sv->var, &exact))
|
||||
{
|
||||
int subvar_flags = flags;
|
||||
none = false;
|
||||
@ -1178,6 +1183,12 @@ get_expr_operands (tree stmt, tree *expr_p, int flags)
|
||||
if (!none)
|
||||
flags |= opf_no_vops;
|
||||
}
|
||||
else if (TREE_CODE (ref) == INDIRECT_REF)
|
||||
{
|
||||
get_indirect_ref_operands (stmt, ref, flags, expr,
|
||||
offset, maxsize, false);
|
||||
flags |= opf_no_vops;
|
||||
}
|
||||
|
||||
/* Even if we found subvars above we need to ensure to see
|
||||
immediate uses for d in s.a[d]. In case of s.a having
|
||||
@ -1416,10 +1427,24 @@ get_asm_expr_operands (tree stmt)
|
||||
}
|
||||
|
||||
/* A subroutine of get_expr_operands to handle INDIRECT_REF,
|
||||
ALIGN_INDIRECT_REF and MISALIGNED_INDIRECT_REF. */
|
||||
ALIGN_INDIRECT_REF and MISALIGNED_INDIRECT_REF.
|
||||
STMT is the statement being processed, EXPR is the INDIRECT_REF
|
||||
that got us here. FLAGS is as in get_expr_operands.
|
||||
FULL_REF contains the full pointer dereference expression, if we
|
||||
have it, or NULL otherwise.
|
||||
OFFSET and SIZE are the location of the access inside the
|
||||
dereferenced pointer, if known.
|
||||
RECURSE_ON_BASE should be set to true if we want to continue
|
||||
calling get_expr_operands on the base pointer, and false if
|
||||
something else will do it for us.
|
||||
|
||||
*/
|
||||
|
||||
static void
|
||||
get_indirect_ref_operands (tree stmt, tree expr, int flags)
|
||||
get_indirect_ref_operands (tree stmt, tree expr, int flags,
|
||||
tree full_ref,
|
||||
HOST_WIDE_INT offset, HOST_WIDE_INT size,
|
||||
bool recurse_on_base)
|
||||
{
|
||||
tree *pptr = &TREE_OPERAND (expr, 0);
|
||||
tree ptr = *pptr;
|
||||
@ -1438,7 +1463,8 @@ get_indirect_ref_operands (tree stmt, tree expr, int flags)
|
||||
&& pi->name_mem_tag)
|
||||
{
|
||||
/* PTR has its own memory tag. Use it. */
|
||||
add_stmt_operand (&pi->name_mem_tag, s_ann, flags);
|
||||
add_virtual_operand (pi->name_mem_tag, s_ann, flags,
|
||||
full_ref, offset, size, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1464,8 +1490,10 @@ get_indirect_ref_operands (tree stmt, tree expr, int flags)
|
||||
if (TREE_CODE (ptr) == SSA_NAME)
|
||||
ptr = SSA_NAME_VAR (ptr);
|
||||
v_ann = var_ann (ptr);
|
||||
|
||||
if (v_ann->type_mem_tag)
|
||||
add_stmt_operand (&v_ann->type_mem_tag, s_ann, flags);
|
||||
add_virtual_operand (v_ann->type_mem_tag, s_ann, flags,
|
||||
full_ref, offset, size, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1483,7 +1511,8 @@ get_indirect_ref_operands (tree stmt, tree expr, int flags)
|
||||
gcc_unreachable ();
|
||||
|
||||
/* Add a USE operand for the base pointer. */
|
||||
get_expr_operands (stmt, pptr, opf_none);
|
||||
if (recurse_on_base)
|
||||
get_expr_operands (stmt, pptr, opf_none);
|
||||
}
|
||||
|
||||
/* A subroutine of get_expr_operands to handle TARGET_MEM_REF. */
|
||||
@ -1528,7 +1557,7 @@ get_tmr_operands (tree stmt, tree expr, int flags)
|
||||
for (sv = svars; sv; sv = sv->next)
|
||||
{
|
||||
bool exact;
|
||||
if (overlap_subvar (offset, maxsize, sv, &exact))
|
||||
if (overlap_subvar (offset, maxsize, sv->var, &exact))
|
||||
{
|
||||
int subvar_flags = flags;
|
||||
if (!exact || size != maxsize)
|
||||
@ -1580,6 +1609,269 @@ get_call_expr_operands (tree stmt, tree expr)
|
||||
|
||||
}
|
||||
|
||||
/* REF is a tree that contains the entire pointer dereference
|
||||
expression, if available, or NULL otherwise. ALIAS is the variable
|
||||
we are asking if REF can access. OFFSET and SIZE come from the
|
||||
memory access expression that generated this virtual operand.
|
||||
FOR_CLOBBER is true is this is adding a virtual operand for a call
|
||||
clobber. */
|
||||
|
||||
static bool
|
||||
access_can_touch_variable (tree ref, tree alias, HOST_WIDE_INT offset,
|
||||
HOST_WIDE_INT size)
|
||||
{
|
||||
bool offsetgtz = offset > 0;
|
||||
unsigned HOST_WIDE_INT uoffset = (unsigned HOST_WIDE_INT) offset;
|
||||
tree base = ref ? get_base_address (ref) : NULL;
|
||||
|
||||
/* If ALIAS is an SFT, it can't be touched if the offset
|
||||
and size of the access is not overlapping with the SFT offset and
|
||||
size. This is only true if we are accessing through a pointer
|
||||
to a type that is the same as SFT_PARENT_VAR. Otherwise, we may
|
||||
be accessing through a pointer to some substruct of the
|
||||
structure, and if we try to prune there, we will have the wrong
|
||||
offset, and get the wrong answer.
|
||||
i.e., we can't prune without more work if we have something like
|
||||
struct gcc_target
|
||||
{
|
||||
struct asm_out
|
||||
{
|
||||
const char *byte_op;
|
||||
struct asm_int_op
|
||||
{
|
||||
const char *hi;
|
||||
} aligned_op;
|
||||
} asm_out;
|
||||
} targetm;
|
||||
|
||||
foo = &targetm.asm_out.aligned_op;
|
||||
return foo->hi;
|
||||
|
||||
SFT.1, which represents hi, will have SFT_OFFSET=32 because in
|
||||
terms of SFT_PARENT_VAR, that is where it is.
|
||||
However, the access through the foo pointer will be at offset 0.
|
||||
*/
|
||||
if (size != -1
|
||||
&& TREE_CODE (alias) == STRUCT_FIELD_TAG
|
||||
&& base
|
||||
&& TREE_TYPE (base) == TREE_TYPE (SFT_PARENT_VAR (alias))
|
||||
&& !overlap_subvar (offset, size, alias, NULL))
|
||||
{
|
||||
#ifdef ACCESS_DEBUGGING
|
||||
fprintf (stderr, "Access to ");
|
||||
print_generic_expr (stderr, ref, 0);
|
||||
fprintf (stderr, " may not touch ");
|
||||
print_generic_expr (stderr, alias, 0);
|
||||
fprintf (stderr, " in function %s\n", get_name (current_function_decl));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
/* Without strict aliasing, it is impossible for a component access
|
||||
through a pointer to touch a random variable, unless that
|
||||
variable *is* a structure or a pointer.
|
||||
|
||||
|
||||
IE given p->c, and some random global variable b,
|
||||
there is no legal way that p->c could be an access to b.
|
||||
|
||||
Without strict aliasing on, we consider it legal to do something
|
||||
like:
|
||||
struct foos { int l; };
|
||||
int foo;
|
||||
static struct foos *getfoo(void);
|
||||
int main (void)
|
||||
{
|
||||
struct foos *f = getfoo();
|
||||
f->l = 1;
|
||||
foo = 2;
|
||||
if (f->l == 1)
|
||||
abort();
|
||||
exit(0);
|
||||
}
|
||||
static struct foos *getfoo(void)
|
||||
{ return (struct foos *)&foo; }
|
||||
|
||||
(taken from 20000623-1.c)
|
||||
*/
|
||||
else if (ref
|
||||
&& flag_strict_aliasing
|
||||
&& TREE_CODE (ref) != INDIRECT_REF
|
||||
&& !MTAG_P (alias)
|
||||
&& !AGGREGATE_TYPE_P (TREE_TYPE (alias))
|
||||
&& !TREE_CODE (TREE_TYPE (alias)) == COMPLEX_TYPE
|
||||
&& !POINTER_TYPE_P (TREE_TYPE (alias)))
|
||||
{
|
||||
#ifdef ACCESS_DEBUGGING
|
||||
fprintf (stderr, "Access to ");
|
||||
print_generic_expr (stderr, ref, 0);
|
||||
fprintf (stderr, " may not touch ");
|
||||
print_generic_expr (stderr, alias, 0);
|
||||
fprintf (stderr, " in function %s\n", get_name (current_function_decl));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
/* If the offset of the access is greater than the size of one of
|
||||
the possible aliases, it can't be touching that alias, because it
|
||||
would be past the end of the structure. */
|
||||
else if (ref
|
||||
&& flag_strict_aliasing
|
||||
&& TREE_CODE (ref) != INDIRECT_REF
|
||||
&& !MTAG_P (alias)
|
||||
&& !POINTER_TYPE_P (TREE_TYPE (alias))
|
||||
&& offsetgtz
|
||||
&& DECL_SIZE (alias)
|
||||
&& TREE_CODE (DECL_SIZE (alias)) == INTEGER_CST
|
||||
&& uoffset > TREE_INT_CST_LOW (DECL_SIZE (alias)))
|
||||
{
|
||||
#ifdef ACCESS_DEBUGGING
|
||||
fprintf (stderr, "Access to ");
|
||||
print_generic_expr (stderr, ref, 0);
|
||||
fprintf (stderr, " may not touch ");
|
||||
print_generic_expr (stderr, alias, 0);
|
||||
fprintf (stderr, " in function %s\n", get_name (current_function_decl));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Add VAR to the virtual operands array. FLAGS is as in
|
||||
get_expr_operands. FULL_REF is a tree that contains the entire
|
||||
pointer dereference expression, if available, or NULL otherwise.
|
||||
OFFSET and SIZE come from the memory access expression that
|
||||
generated this virtual operand. FOR_CLOBBER is true is this is
|
||||
adding a virtual operand for a call clobber. */
|
||||
|
||||
static void
|
||||
add_virtual_operand (tree var, stmt_ann_t s_ann, int flags,
|
||||
tree full_ref, HOST_WIDE_INT offset,
|
||||
HOST_WIDE_INT size, bool for_clobber)
|
||||
{
|
||||
VEC(tree,gc) *aliases;
|
||||
tree sym;
|
||||
var_ann_t v_ann;
|
||||
|
||||
sym = (TREE_CODE (var) == SSA_NAME ? SSA_NAME_VAR (var) : var);
|
||||
v_ann = var_ann (sym);
|
||||
|
||||
/* Mark statements with volatile operands. Optimizers should back
|
||||
off from statements having volatile operands. */
|
||||
if (TREE_THIS_VOLATILE (sym) && s_ann)
|
||||
s_ann->has_volatile_ops = true;
|
||||
|
||||
/* If the variable cannot be modified and this is a V_MAY_DEF change
|
||||
it into a VUSE. This happens when read-only variables are marked
|
||||
call-clobbered and/or aliased to writable variables. So we only
|
||||
check that this only happens on non-specific stores.
|
||||
|
||||
Note that if this is a specific store, i.e. associated with a
|
||||
modify_expr, then we can't suppress the V_DEF, lest we run into
|
||||
validation problems.
|
||||
|
||||
This can happen when programs cast away const, leaving us with a
|
||||
store to read-only memory. If the statement is actually executed
|
||||
at runtime, then the program is ill formed. If the statement is
|
||||
not executed then all is well. At the very least, we cannot ICE. */
|
||||
if ((flags & opf_non_specific) && unmodifiable_var_p (var))
|
||||
flags &= ~(opf_is_def | opf_kill_def);
|
||||
|
||||
|
||||
/* The variable is not a GIMPLE register. Add it (or its aliases) to
|
||||
virtual operands, unless the caller has specifically requested
|
||||
not to add virtual operands (used when adding operands inside an
|
||||
ADDR_EXPR expression). */
|
||||
if (flags & opf_no_vops)
|
||||
return;
|
||||
|
||||
aliases = v_ann->may_aliases;
|
||||
if (aliases == NULL)
|
||||
{
|
||||
/* The variable is not aliased or it is an alias tag. */
|
||||
if (flags & opf_is_def)
|
||||
{
|
||||
if (flags & opf_kill_def)
|
||||
{
|
||||
/* Only regular variables or struct fields may get a
|
||||
V_MUST_DEF operand. */
|
||||
gcc_assert (!MTAG_P (var)
|
||||
|| TREE_CODE (var) == STRUCT_FIELD_TAG);
|
||||
/* V_MUST_DEF for non-aliased, non-GIMPLE register
|
||||
variable definitions. */
|
||||
append_v_must_def (var);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Add a V_MAY_DEF for call-clobbered variables and
|
||||
memory tags. */
|
||||
append_v_may_def (var);
|
||||
}
|
||||
}
|
||||
else
|
||||
append_vuse (var);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned i;
|
||||
tree al;
|
||||
|
||||
/* The variable is aliased. Add its aliases to the virtual
|
||||
operands. */
|
||||
gcc_assert (VEC_length (tree, aliases) != 0);
|
||||
|
||||
if (flags & opf_is_def)
|
||||
{
|
||||
|
||||
bool none_added = true;
|
||||
|
||||
for (i = 0; VEC_iterate (tree, aliases, i, al); i++)
|
||||
{
|
||||
if (!access_can_touch_variable (full_ref, al, offset, size))
|
||||
continue;
|
||||
|
||||
none_added = false;
|
||||
append_v_may_def (al);
|
||||
}
|
||||
|
||||
/* If the variable is also an alias tag, add a virtual
|
||||
operand for it, otherwise we will miss representing
|
||||
references to the members of the variable's alias set.
|
||||
This fixes the bug in gcc.c-torture/execute/20020503-1.c.
|
||||
|
||||
It is also necessary to add bare defs on clobbers for
|
||||
TMT's, so that bare TMT uses caused by pruning all the
|
||||
aliases will link up properly with calls. */
|
||||
if (v_ann->is_alias_tag || none_added
|
||||
|| (TREE_CODE (var) == TYPE_MEMORY_TAG && for_clobber))
|
||||
{
|
||||
/* We should never end up with adding no aliases of an
|
||||
NMT, as that would imply we got the set wrong. */
|
||||
gcc_assert (!(none_added && TREE_CODE (var) == NAME_MEMORY_TAG));
|
||||
|
||||
append_v_may_def (var);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool none_added = true;
|
||||
for (i = 0; VEC_iterate (tree, aliases, i, al); i++)
|
||||
{
|
||||
if (!access_can_touch_variable (full_ref, al, offset, size))
|
||||
continue;
|
||||
none_added = false;
|
||||
append_vuse (al);
|
||||
}
|
||||
|
||||
/* Similarly, append a virtual uses for VAR itself, when
|
||||
it is an alias tag. */
|
||||
if (v_ann->is_alias_tag || none_added)
|
||||
{
|
||||
gcc_assert (!(none_added && TREE_CODE (var) == NAME_MEMORY_TAG));
|
||||
|
||||
append_vuse (var);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Add *VAR_P to the appropriate operand array for INFO. FLAGS is as in
|
||||
get_expr_operands. If *VAR_P is a GIMPLE register, it will be added to
|
||||
@ -1609,25 +1901,6 @@ add_stmt_operand (tree *var_p, stmt_ann_t s_ann, int flags)
|
||||
if (TREE_THIS_VOLATILE (sym) && s_ann)
|
||||
s_ann->has_volatile_ops = true;
|
||||
|
||||
/* If the variable cannot be modified and this is a V_MAY_DEF change
|
||||
it into a VUSE. This happens when read-only variables are marked
|
||||
call-clobbered and/or aliased to writable variables. So we only
|
||||
check that this only happens on non-specific stores.
|
||||
|
||||
Note that if this is a specific store, i.e. associated with a
|
||||
modify_expr, then we can't suppress the V_DEF, lest we run into
|
||||
validation problems.
|
||||
|
||||
This can happen when programs cast away const, leaving us with a
|
||||
store to read-only memory. If the statement is actually executed
|
||||
at runtime, then the program is ill formed. If the statement is
|
||||
not executed then all is well. At the very least, we cannot ICE. */
|
||||
if ((flags & opf_non_specific) && unmodifiable_var_p (var))
|
||||
{
|
||||
gcc_assert (!is_real_op);
|
||||
flags &= ~(opf_is_def | opf_kill_def);
|
||||
}
|
||||
|
||||
if (is_real_op)
|
||||
{
|
||||
/* The variable is a GIMPLE register. Add it to real operands. */
|
||||
@ -1637,79 +1910,9 @@ add_stmt_operand (tree *var_p, stmt_ann_t s_ann, int flags)
|
||||
append_use (var_p);
|
||||
}
|
||||
else
|
||||
{
|
||||
VEC(tree,gc) *aliases;
|
||||
|
||||
/* The variable is not a GIMPLE register. Add it (or its aliases) to
|
||||
virtual operands, unless the caller has specifically requested
|
||||
not to add virtual operands (used when adding operands inside an
|
||||
ADDR_EXPR expression). */
|
||||
if (flags & opf_no_vops)
|
||||
return;
|
||||
|
||||
aliases = v_ann->may_aliases;
|
||||
|
||||
if (aliases == NULL)
|
||||
{
|
||||
/* The variable is not aliased or it is an alias tag. */
|
||||
if (flags & opf_is_def)
|
||||
{
|
||||
if (flags & opf_kill_def)
|
||||
{
|
||||
/* Only regular variables or struct fields may get a
|
||||
V_MUST_DEF operand. */
|
||||
gcc_assert (!MTAG_P (var)
|
||||
|| TREE_CODE (var) == STRUCT_FIELD_TAG);
|
||||
/* V_MUST_DEF for non-aliased, non-GIMPLE register
|
||||
variable definitions. */
|
||||
append_v_must_def (var);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Add a V_MAY_DEF for call-clobbered variables and
|
||||
memory tags. */
|
||||
append_v_may_def (var);
|
||||
}
|
||||
}
|
||||
else
|
||||
append_vuse (var);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned i;
|
||||
tree al;
|
||||
|
||||
/* The variable is aliased. Add its aliases to the virtual
|
||||
operands. */
|
||||
gcc_assert (VEC_length (tree, aliases) != 0);
|
||||
|
||||
if (flags & opf_is_def)
|
||||
{
|
||||
/* If the variable is also an alias tag, add a virtual
|
||||
operand for it, otherwise we will miss representing
|
||||
references to the members of the variable's alias set.
|
||||
This fixes the bug in gcc.c-torture/execute/20020503-1.c. */
|
||||
if (v_ann->is_alias_tag)
|
||||
append_v_may_def (var);
|
||||
|
||||
for (i = 0; VEC_iterate (tree, aliases, i, al); i++)
|
||||
append_v_may_def (al);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Similarly, append a virtual uses for VAR itself, when
|
||||
it is an alias tag. */
|
||||
if (v_ann->is_alias_tag)
|
||||
append_vuse (var);
|
||||
|
||||
for (i = 0; VEC_iterate (tree, aliases, i, al); i++)
|
||||
append_vuse (al);
|
||||
}
|
||||
}
|
||||
}
|
||||
add_virtual_operand (var, s_ann, flags, NULL_TREE, 0, -1, false);
|
||||
}
|
||||
|
||||
|
||||
/* Add the base address of REF to the set *ADDRESSES_TAKEN. If
|
||||
*ADDRESSES_TAKEN is NULL, a new set is created. REF may be
|
||||
a single variable whose address has been taken or any other valid
|
||||
@ -1836,7 +2039,8 @@ add_call_clobber_ops (tree stmt, tree callee)
|
||||
clobber_stats.static_read_clobbers_avoided++;
|
||||
}
|
||||
else
|
||||
add_stmt_operand (&var, s_ann, opf_is_def);
|
||||
add_virtual_operand (var, s_ann, opf_is_def,
|
||||
NULL, 0, -1, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -276,6 +276,8 @@ init_ttree (void)
|
||||
tree_contains_struct[NAME_MEMORY_TAG][TS_MEMORY_TAG] = 1;
|
||||
tree_contains_struct[TYPE_MEMORY_TAG][TS_MEMORY_TAG] = 1;
|
||||
|
||||
tree_contains_struct[STRUCT_FIELD_TAG][TS_STRUCT_FIELD_TAG] = 1;
|
||||
|
||||
tree_contains_struct[VAR_DECL][TS_DECL_WITH_VIS] = 1;
|
||||
tree_contains_struct[FUNCTION_DECL][TS_DECL_WITH_VIS] = 1;
|
||||
tree_contains_struct[TYPE_DECL][TS_DECL_WITH_VIS] = 1;
|
||||
@ -335,8 +337,9 @@ tree_code_size (enum tree_code code)
|
||||
return sizeof (struct tree_function_decl);
|
||||
case NAME_MEMORY_TAG:
|
||||
case TYPE_MEMORY_TAG:
|
||||
case STRUCT_FIELD_TAG:
|
||||
return sizeof (struct tree_memory_tag);
|
||||
case STRUCT_FIELD_TAG:
|
||||
return sizeof (struct tree_struct_field_tag);
|
||||
default:
|
||||
return sizeof (struct tree_decl_non_common);
|
||||
}
|
||||
|
21
gcc/tree.h
21
gcc/tree.h
@ -2309,12 +2309,28 @@ struct tree_decl_minimal GTY(())
|
||||
struct tree_memory_tag GTY(())
|
||||
{
|
||||
struct tree_decl_minimal common;
|
||||
tree parent_var;
|
||||
unsigned int is_global:1;
|
||||
};
|
||||
|
||||
#define MTAG_GLOBAL(NODE) (TREE_MEMORY_TAG_CHECK (NODE)->mtag.is_global)
|
||||
#define SFT_PARENT_VAR(NODE) (STRUCT_FIELD_TAG_CHECK (NODE)->mtag.parent_var)
|
||||
|
||||
struct tree_struct_field_tag GTY(())
|
||||
{
|
||||
struct tree_memory_tag common;
|
||||
|
||||
/* Parent variable. */
|
||||
tree parent_var;
|
||||
|
||||
/* Offset inside structure. */
|
||||
unsigned HOST_WIDE_INT offset;
|
||||
|
||||
/* Size of the field. */
|
||||
unsigned HOST_WIDE_INT size;
|
||||
|
||||
};
|
||||
#define SFT_PARENT_VAR(NODE) (STRUCT_FIELD_TAG_CHECK (NODE)->sft.parent_var)
|
||||
#define SFT_OFFSET(NODE) (STRUCT_FIELD_TAG_CHECK (NODE)->sft.offset)
|
||||
#define SFT_SIZE(NODE) (STRUCT_FIELD_TAG_CHECK (NODE)->sft.size)
|
||||
|
||||
/* For any sort of a ..._DECL node, this points to the original (abstract)
|
||||
decl node which this decl is an instance of, or else it is NULL indicating
|
||||
@ -3124,6 +3140,7 @@ union tree_node GTY ((ptr_alias (union lang_tree_node),
|
||||
struct tree_value_handle GTY ((tag ("TS_VALUE_HANDLE"))) value_handle;
|
||||
struct tree_constructor GTY ((tag ("TS_CONSTRUCTOR"))) constructor;
|
||||
struct tree_memory_tag GTY ((tag ("TS_MEMORY_TAG"))) mtag;
|
||||
struct tree_struct_field_tag GTY ((tag ("TS_STRUCT_FIELD_TAG"))) sft;
|
||||
struct tree_omp_clause GTY ((tag ("TS_OMP_CLAUSE"))) omp_clause;
|
||||
};
|
||||
|
||||
|
@ -60,4 +60,5 @@ DEFTREESTRUCT(TS_STATEMENT_LIST, "statement list")
|
||||
DEFTREESTRUCT(TS_VALUE_HANDLE, "value handle")
|
||||
DEFTREESTRUCT(TS_CONSTRUCTOR, "constructor")
|
||||
DEFTREESTRUCT(TS_MEMORY_TAG, "memory tag")
|
||||
DEFTREESTRUCT(TS_STRUCT_FIELD_TAG, "struct field tag")
|
||||
DEFTREESTRUCT(TS_OMP_CLAUSE, "omp clause")
|
||||
|
Loading…
Reference in New Issue
Block a user