diff options
author | dberlin <dberlin@138bc75d-0d04-0410-961f-82ee72b054a4> | 2006-02-15 22:09:45 +0000 |
---|---|---|
committer | dberlin <dberlin@138bc75d-0d04-0410-961f-82ee72b054a4> | 2006-02-15 22:09:45 +0000 |
commit | 0b3f639dba8d8de709f155e4281e709ddb3b0070 (patch) | |
tree | da2b2114c7702693918635be6c7ae1407c8010c8 /gcc/tree-ssa-operands.c | |
parent | e87210bbabb7f59320cfa270596162c3b3f5e4ef (diff) | |
download | gcc-0b3f639dba8d8de709f155e4281e709ddb3b0070.tar.gz |
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.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@111120 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/tree-ssa-operands.c')
-rw-r--r-- | gcc/tree-ssa-operands.c | 388 |
1 files changed, 296 insertions, 92 deletions
diff --git a/gcc/tree-ssa-operands.c b/gcc/tree-ssa-operands.c index fbee0b915e1..2d0e71cf372 100644 --- a/gcc/tree-ssa-operands.c +++ b/gcc/tree-ssa-operands.c @@ -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,30 +1609,151 @@ 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. -/* 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 - the statement's real operands, otherwise it is added to virtual - operands. */ + + 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; +} -static void -add_stmt_operand (tree *var_p, stmt_ann_t s_ann, int flags) +/* 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) { - bool is_real_op; - tree var, sym; + VEC(tree,gc) *aliases; + tree sym; var_ann_t v_ann; - - var = *var_p; - gcc_assert (SSA_VAR_P (var)); - - is_real_op = is_gimple_reg (var); - /* If this is a real operand, the operand is either ssa name or decl. - Virtual operands may only be decls. */ - gcc_assert (is_real_op || DECL_P (var)); - + 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) @@ -1623,93 +1773,146 @@ add_stmt_operand (tree *var_p, stmt_ann_t s_ann, int flags) 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); - } + flags &= ~(opf_is_def | opf_kill_def); + - if (is_real_op) + /* 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 a GIMPLE register. Add it to real operands. */ + /* The variable is not aliased or it is an alias tag. */ if (flags & opf_is_def) - append_def (var_p); + { + 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_use (var_p); + append_vuse (var); } 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; + 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; - aliases = v_ann->may_aliases; + 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 (aliases == NULL) - { - /* The variable is not aliased or it is an alias tag. */ - 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. + + 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)) { - 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); - } + /* 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 - 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 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); + if (!access_can_touch_variable (full_ref, al, offset, size)) + continue; + none_added = false; + append_vuse (al); } - else + + /* Similarly, append a virtual uses for VAR itself, when + it is an alias tag. */ + if (v_ann->is_alias_tag || none_added) { - /* Similarly, append a virtual uses for VAR itself, when - it is an alias tag. */ - if (v_ann->is_alias_tag) - append_vuse (var); + gcc_assert (!(none_added && TREE_CODE (var) == NAME_MEMORY_TAG)); - for (i = 0; VEC_iterate (tree, aliases, i, al); i++) - append_vuse (al); + 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 + the statement's real operands, otherwise it is added to virtual + operands. */ + +static void +add_stmt_operand (tree *var_p, stmt_ann_t s_ann, int flags) +{ + bool is_real_op; + tree var, sym; + var_ann_t v_ann; + + var = *var_p; + gcc_assert (SSA_VAR_P (var)); + + is_real_op = is_gimple_reg (var); + /* If this is a real operand, the operand is either ssa name or decl. + Virtual operands may only be decls. */ + gcc_assert (is_real_op || DECL_P (var)); + + 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 (is_real_op) + { + /* The variable is a GIMPLE register. Add it to real operands. */ + if (flags & opf_is_def) + append_def (var_p); + else + append_use (var_p); + } + else + 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); } } |