Makefile.in: Added rules for ipa-pure-const.c...
2005-07-16 Danny Berlin <dberlin@dberlin.org> Kenneth Zadeck <zadeck@naturalbridge.com> * Makefile.in: Added rules for ipa-pure-const.c, ipa-reference.c, ipa-reference.h, ipa-utils.c, ipa-utils.h, ipa-type-escape.c, ipa-type-escape.h, tree-promote-statics.c * ipa-pure-const.c, ipa-reference.c, ipa-reference.h, ipa-utils.c, ipa-utils.h, ipa-type-escape.c, ipa-type-escape.h, tree-promote-statics.c: new files. * alias.c: (nonlocal_mentioned_p_1, nonlocal_mentioned_p, nonlocal_referenced_p_1, nonlocal_referenced_p, nonlocal_set_p_1, int nonlocal_set_p, mark_constant_function): Deleted. (rest_of_handle_cfg): Removed call to mark_constant_function. (nonoverlapping_component_refs_p): Added calls to support type based aliasing. * tree-ssa-alias.c (may_alias_p, compute_flow_insensitive_aliasing): Ditto. * calls.c (flags_from_decl_or_type): Removed reference to cgraph_rtl_info. (flags_from_decl_or_type): Support ECF_POINTER_NO_CAPTURE attribute. * c-common.c (handle_pointer_no_capture_attribute): New function and added pointer_no_capture attribute. * c-typeck.c (convert_arguments): Make builtins tolerant of having too many arguments. This is necessary for Spec 2000. * cgraph.h (const_function, pure_function): Removed. * common.opt: Added "fipa-pure-const", "fipa-reference", "fipa-type-escape", and "ftree-promote-static". * opts.c: Ditto. * passes.c: Added ipa and tree-promote-statics passes. * timevar.def: Added TV_IPA_PURE_CONST, TV_IPA_REFERENCE, TV_IPA_TYPE_ESCAPE, and TV_PROMOTE_STATICS. * tree.h: Support ECF_POINTER_NO_CAPTURE attribute. * tree-dfa.c (referenced_var_lookup_if_exists): New function. * tree-flow.h: Added exposed sra calls and addition of reference_vars_info field for FUNCTION_DECLS. * tree-pass.h: Added passes. * tree-sra.c: (sra_init_cache): New function. (sra_insert_before, sra_insert_after) Made public. (type_can_be_decomposed_p): Renamed from type_can_be_decomposed_p and made public. * tree-ssa-alias.c (dump_alias_stats): Added stats for type based aliasing. (may_alias_p): Added code to use type escape analysis to improve alias sets. * tree-ssa-operands.c (add_call_clobber_ops): Added parameter and code to prune clobbers of static variables based on information produced in ipa-reference pass. Changed call clobbering so that statics are not marked as clobbered if the call does not clobber them. 2005-07-16 Danny Berlin <dberlin@dberlin.org> Kenneth Zadeck <zadeck@naturalbridge.com> * gcc.dg/tree-ssa/ssa-dce-2.c: Changed dg-options to run at -O2 since pure const detection cannot run at -O1 in c compiler. * gcc.dg/tree-ssa/20030714-1.c Changed scanning patterns because we can now optimize this case properly. * gcc.dg/tree-ssa/sra-2.c: Changed to -O3 and removed xfail because we now pass. * gcc.dg/vect/vect-92.c: Removed out of bounds array access. Co-Authored-By: Kenneth Zadeck <zadeck@naturalbridge.com> From-SVN: r102098
This commit is contained in:
parent
8f59c51bb1
commit
ea900239f4
50
ChangeLog
50
ChangeLog
@ -1,3 +1,53 @@
|
||||
2005-07-16 Danny Berlin <dberlin@dberlin.org>
|
||||
Kenneth Zadeck <zadeck@naturalbridge.com>
|
||||
|
||||
* Makefile.in: Added rules for ipa-pure-const.c, ipa-reference.c,
|
||||
ipa-reference.h, ipa-utils.c, ipa-utils.h, ipa-type-escape.c,
|
||||
ipa-type-escape.h, tree-promote-statics.c
|
||||
* ipa-pure-const.c, ipa-reference.c, ipa-reference.h, ipa-utils.c,
|
||||
ipa-utils.h, ipa-type-escape.c, ipa-type-escape.h,
|
||||
tree-promote-statics.c: new files.
|
||||
* alias.c: (nonlocal_mentioned_p_1, nonlocal_mentioned_p,
|
||||
nonlocal_referenced_p_1, nonlocal_referenced_p, nonlocal_set_p_1,
|
||||
int nonlocal_set_p, mark_constant_function): Deleted.
|
||||
(rest_of_handle_cfg): Removed call to mark_constant_function.
|
||||
(nonoverlapping_component_refs_p): Added calls to support
|
||||
type based aliasing.
|
||||
* tree-ssa-alias.c (may_alias_p,
|
||||
compute_flow_insensitive_aliasing): Ditto.
|
||||
* calls.c (flags_from_decl_or_type): Removed reference to
|
||||
cgraph_rtl_info.
|
||||
(flags_from_decl_or_type): Support ECF_POINTER_NO_CAPTURE attribute.
|
||||
* c-common.c (handle_pointer_no_capture_attribute): New function
|
||||
and added pointer_no_capture attribute.
|
||||
* c-typeck.c (convert_arguments): Make builtins tolerant of having
|
||||
too many arguments. This is necessary for Spec 2000.
|
||||
* cgraph.h (const_function, pure_function): Removed.
|
||||
* common.opt: Added "fipa-pure-const", "fipa-reference",
|
||||
"fipa-type-escape", and "ftree-promote-static".
|
||||
* opts.c: Ditto.
|
||||
* passes.c: Added ipa and tree-promote-statics passes.
|
||||
* timevar.def: Added TV_IPA_PURE_CONST, TV_IPA_REFERENCE,
|
||||
TV_IPA_TYPE_ESCAPE, and TV_PROMOTE_STATICS.
|
||||
* tree.h: Support ECF_POINTER_NO_CAPTURE attribute.
|
||||
* tree-dfa.c (referenced_var_lookup_if_exists): New function.
|
||||
* tree-flow.h: Added exposed sra calls and addition of
|
||||
reference_vars_info field for FUNCTION_DECLS.
|
||||
* tree-pass.h: Added passes.
|
||||
* tree-sra.c: (sra_init_cache): New function.
|
||||
(sra_insert_before, sra_insert_after) Made public.
|
||||
(type_can_be_decomposed_p): Renamed from type_can_be_decomposed_p
|
||||
and made public.
|
||||
* tree-ssa-alias.c (dump_alias_stats): Added stats for type based
|
||||
aliasing. (may_alias_p): Added code to use type escape analysis to
|
||||
improve alias sets.
|
||||
* tree-ssa-operands.c (add_call_clobber_ops): Added parameter and
|
||||
code to prune clobbers of static variables based on information
|
||||
produced in ipa-reference pass. Changed call clobbering so that
|
||||
statics are not marked as clobbered if the call does not clobber
|
||||
them.
|
||||
|
||||
|
||||
2005-07-16 Kelley Cook <kcook@gcc.gnu.org>
|
||||
|
||||
* all files: Update FSF address.
|
||||
|
@ -729,6 +729,9 @@ SCHED_INT_H = sched-int.h $(INSN_ATTR_H) $(BASIC_BLOCK_H) $(RTL_H)
|
||||
INTEGRATE_H = integrate.h varray.h
|
||||
CFGLAYOUT_H = cfglayout.h $(BASIC_BLOCK_H)
|
||||
CFGLOOP_H = cfgloop.h $(BASIC_BLOCK_H) $(RTL_H)
|
||||
IPA_UTILS_H = ipa-utils.h $(TREE_H) $(CGRAPH_H)
|
||||
IPA_REFERENCE_H = ipa-reference.h bitmap.h $(TREE_H)
|
||||
IPA_TYPE_ESCAPE_H = ipa-type-escape.h $(TREE_H)
|
||||
CGRAPH_H = cgraph.h tree.h
|
||||
DF_H = df.h bitmap.h sbitmap.h $(BASIC_BLOCK_H)
|
||||
DDG_H = ddg.h sbitmap.h $(DF_H)
|
||||
@ -750,7 +753,7 @@ TREE_DUMP_H = tree-dump.h $(SPLAY_TREE_H)
|
||||
TREE_GIMPLE_H = tree-gimple.h tree-iterator.h
|
||||
TREE_FLOW_H = tree-flow.h tree-flow-inline.h tree-ssa-operands.h \
|
||||
bitmap.h $(BASIC_BLOCK_H) hard-reg-set.h $(TREE_GIMPLE_H) \
|
||||
$(HASHTAB_H) $(CGRAPH_H)
|
||||
$(HASHTAB_H) $(CGRAPH_H) $(IPA_REFERENCE_H)
|
||||
TREE_SSA_LIVE_H = tree-ssa-live.h $(PARTITION_H)
|
||||
PRETTY_PRINT_H = pretty-print.h input.h $(OBSTACK_H)
|
||||
DIAGNOSTIC_H = diagnostic.h diagnostic.def $(PRETTY_PRINT_H)
|
||||
@ -945,7 +948,7 @@ OBJS-common = \
|
||||
integrate.o intl.o jump.o langhooks.o lcm.o lists.o local-alloc.o \
|
||||
loop.o mode-switching.o modulo-sched.o optabs.o options.o opts.o \
|
||||
params.o postreload.o postreload-gcse.o predict.o \
|
||||
insn-preds.o pointer-set.o \
|
||||
insn-preds.o pointer-set.o tree-promote-statics.o \
|
||||
print-rtl.o print-tree.o profile.o value-prof.o var-tracking.o \
|
||||
real.o recog.o reg-stack.o regclass.o regmove.o regrename.o \
|
||||
reload.o reload1.o reorg.o resource.o rtl.o rtlanal.o rtl-error.o \
|
||||
@ -962,7 +965,8 @@ OBJS-common = \
|
||||
|
||||
OBJS-md = $(out_object_file)
|
||||
OBJS-archive = $(EXTRA_OBJS) $(host_hook_obj) tree-inline.o \
|
||||
cgraph.o cgraphunit.o tree-nomudflap.o ipa.o ipa-inline.o
|
||||
cgraph.o cgraphunit.o tree-nomudflap.o ipa.o ipa-inline.o \
|
||||
ipa-utils.o ipa-reference.o ipa-pure-const.o ipa-type-escape.o
|
||||
|
||||
OBJS = $(OBJS-common) $(out_object_file) $(OBJS-archive)
|
||||
|
||||
@ -1837,11 +1841,12 @@ tree-dfa.o : tree-dfa.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \
|
||||
tree-inline.h $(HASHTAB_H) pointer-set.h $(FLAGS_H) function.h \
|
||||
$(TIMEVAR_H) convert.h $(TM_H) coretypes.h langhooks.h $(TREE_DUMP_H) \
|
||||
tree-pass.h $(PARAMS_H) $(CGRAPH_H) $(BASIC_BLOCK_H) hard-reg-set.h \
|
||||
$(TREE_GIMPLE_H)
|
||||
$(TREE_GIMPLE_H)
|
||||
tree-ssa-operands.o : tree-ssa-operands.c $(TREE_FLOW_H) $(CONFIG_H) \
|
||||
$(SYSTEM_H) $(TREE_H) $(GGC_H) $(DIAGNOSTIC_H) tree-inline.h \
|
||||
$(SYSTEM_H) $(TREE_H) $(GGC_H) $(DIAGNOSTIC_H) errors.h tree-inline.h \
|
||||
$(FLAGS_H) function.h $(TM_H) $(TIMEVAR_H) tree-pass.h toplev.h \
|
||||
gt-tree-ssa-operands.h coretypes.h langhooks.h tree-ssa-opfinalize.h
|
||||
gt-tree-ssa-operands.h coretypes.h langhooks.h tree-ssa-opfinalize.h \
|
||||
$(IPA_REFERENCE_H)
|
||||
tree-eh.o : tree-eh.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \
|
||||
$(RTL_H) $(TREE_H) $(TM_H) $(FLAGS_H) function.h except.h langhooks.h \
|
||||
$(GGC_H) tree-pass.h coretypes.h $(TIMEVAR_H) $(TM_P_H) \
|
||||
@ -1895,7 +1900,8 @@ tree-ssa-alias.o : tree-ssa-alias.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \
|
||||
$(RTL_H) $(TREE_H) $(TM_P_H) $(EXPR_H) $(GGC_H) tree-inline.h $(FLAGS_H) \
|
||||
function.h $(TIMEVAR_H) convert.h $(TM_H) coretypes.h langhooks.h \
|
||||
$(TREE_DUMP_H) tree-pass.h $(PARAMS_H) $(BASIC_BLOCK_H) $(DIAGNOSTIC_H) \
|
||||
hard-reg-set.h $(TREE_GIMPLE_H) vec.h tree-ssa-structalias.h
|
||||
hard-reg-set.h $(TREE_GIMPLE_H) vec.h tree-ssa-structalias.h \
|
||||
$(IPA_TYPE_ESCAPE_H)
|
||||
tree-ssa-reassoc.o : tree-ssa-reassoc.c $(TREE_FLOW_H) $(CONFIG_H) \
|
||||
$(SYSTEM_H) $(TREE_H) $(GGC_H) $(DIAGNOSTIC_H) errors.h $(TIMEVAR_H) \
|
||||
$(TM_H) coretypes.h $(TREE_DUMP_H) tree-pass.h $(FLAGS_H) tree-iterator.h\
|
||||
@ -2142,6 +2148,22 @@ ipa-inline.o : ipa-inline.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
|
||||
$(TREE_H) langhooks.h tree-inline.h $(FLAGS_H) $(CGRAPH_H) intl.h \
|
||||
$(DIAGNOSTIC_H) $(FIBHEAP_H) $(PARAMS_H) $(TIMEVAR_H) tree-pass.h \
|
||||
$(COVERAGE_H) $(HASHTAB_H)
|
||||
ipa-utils.o : ipa-utils.c $(IPA_UTILS_H) $(CONFIG_H) $(SYSTEM_H) \
|
||||
coretypes.h $(TM_H) $(TREE_H) $(TREE_FLOW_H) tree-inline.h langhooks.h \
|
||||
pointer-set.h $(GGC_H) $(C_COMMON_H) $(TREE_GIMPLE_H) \
|
||||
$(CGRAPH_H) output.h $(FLAGS_H) tree-pass.h $(DIAGNOSTIC_H)
|
||||
ipa-reference.o : ipa-reference.c $(CONFIG_H) $(SYSTEM_H) \
|
||||
coretypes.h $(TM_H) $(TREE_H) $(TREE_FLOW_H) tree-inline.h langhooks.h \
|
||||
pointer-set.h $(GGC_H) $(IPA_REFERENCE_H) $(IPA_UTILS_H) $(C_COMMON_H) \
|
||||
$(TREE_GIMPLE_H) $(CGRAPH_H) output.h $(FLAGS_H) tree-pass.h $(DIAGNOSTIC_H)
|
||||
ipa-pure-const.o : ipa-pure-const.c $(CONFIG_H) $(SYSTEM_H) \
|
||||
coretypes.h $(TM_H) $(TREE_H) $(TREE_FLOW_H) tree-inline.h langhooks.h \
|
||||
pointer-set.h $(GGC_H) $(IPA_UTILS_H) $(C_COMMON_H) \
|
||||
$(TREE_GIMPLE_H) $(CGRAPH_H) output.h $(FLAGS_H) tree-pass.h $(DIAGNOSTIC_H)
|
||||
ipa-type-escape.o : ipa-type-escape.c $(CONFIG_H) $(SYSTEM_H) \
|
||||
coretypes.h $(TM_H) $(TREE_H) $(TREE_FLOW_H) tree-inline.h langhooks.h \
|
||||
pointer-set.h $(GGC_H) $(IPA_TYPE_ESCAPE_H) $(IPA_UTILS_H) $(C_COMMON_H) \
|
||||
$(TREE_GIMPLE_H) $(CGRAPH_H) output.h $(FLAGS_H) tree-pass.h $(DIAGNOSTIC_H)
|
||||
coverage.o : coverage.c $(GCOV_IO_H) $(CONFIG_H) $(SYSTEM_H) coretypes.h \
|
||||
$(TM_H) $(RTL_H) $(TREE_H) $(FLAGS_H) output.h $(REGS_H) $(EXPR_H) \
|
||||
function.h toplev.h $(GGC_H) langhooks.h $(COVERAGE_H) gt-coverage.h \
|
||||
@ -2196,6 +2218,9 @@ tree-vect-generic.o : tree-vect-generic.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) \
|
||||
$(FLAGS_H) $(OPTABS_H) $(RTL_H) $(MACHMODE_H) $(EXPR_H) \
|
||||
langhooks.h $(FLAGS_H) $(DIAGNOSTIC_H) gt-tree-vect-generic.h $(GGC_H) \
|
||||
coretypes.h insn-codes.h
|
||||
tree-promote-statics.o : tree-promote-statics.c $(CONFIG_H) system.h \
|
||||
$(TREE_H) $(TM_H) $(BASIC_BLOCK_H) $(TREE_FLOW_H) $(IPA_UTILS_H) \
|
||||
$(IPA_REFERENCE_H) bitmap.h tree-pass.h $(FLAGS_H) $(TIMEVAR_H)
|
||||
df.o : df.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
|
||||
insn-config.h $(RECOG_H) function.h $(REGS_H) alloc-pool.h hard-reg-set.h \
|
||||
$(BASIC_BLOCK_H) $(DF_H) bitmap.h sbitmap.h $(TM_P_H)
|
||||
@ -2345,7 +2370,7 @@ alias.o : alias.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
|
||||
$(FLAGS_H) hard-reg-set.h $(BASIC_BLOCK_H) $(REGS_H) toplev.h output.h \
|
||||
$(ALIAS_H) $(EMIT_RTL_H) $(GGC_H) function.h cselib.h $(TREE_H) $(TM_P_H) \
|
||||
langhooks.h $(TARGET_H) gt-alias.h $(TIMEVAR_H) $(CGRAPH_H) \
|
||||
$(SPLAY_TREE_H) $(VARRAY_H) tree-pass.h
|
||||
$(SPLAY_TREE_H) $(VARRAY_H) $(IPA_TYPE_ESCAPE_H) tree-pass.h
|
||||
regmove.o : regmove.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
|
||||
insn-config.h timevar.h tree-pass.h \
|
||||
$(RECOG_H) output.h $(REGS_H) hard-reg-set.h $(FLAGS_H) function.h \
|
||||
@ -2682,6 +2707,7 @@ GTFILES = $(srcdir)/input.h $(srcdir)/coretypes.h \
|
||||
$(srcdir)/coverage.c $(srcdir)/function.h $(srcdir)/rtl.h \
|
||||
$(srcdir)/optabs.h $(srcdir)/tree.h $(srcdir)/libfuncs.h $(SYMTAB_H) \
|
||||
$(srcdir)/real.h $(srcdir)/varray.h $(srcdir)/insn-addr.h $(srcdir)/hwint.h \
|
||||
$(srcdir)/ipa-reference.h \
|
||||
$(srcdir)/cselib.h $(srcdir)/basic-block.h $(srcdir)/cgraph.h \
|
||||
$(srcdir)/c-common.h $(srcdir)/c-tree.h $(srcdir)/reload.h \
|
||||
$(srcdir)/alias.c $(srcdir)/bitmap.c $(srcdir)/cselib.c $(srcdir)/cgraph.c \
|
||||
@ -2703,6 +2729,7 @@ GTFILES = $(srcdir)/input.h $(srcdir)/coretypes.h \
|
||||
$(srcdir)/tree-chrec.h $(srcdir)/tree-vect-generic.c \
|
||||
$(srcdir)/tree-ssa-operands.h $(srcdir)/tree-ssa-operands.c \
|
||||
$(srcdir)/tree-profile.c $(srcdir)/rtl-profile.c $(srcdir)/tree-nested.c \
|
||||
$(srcdir)/ipa-reference.c \
|
||||
$(srcdir)/targhooks.c $(out_file) \
|
||||
@all_gtfiles@
|
||||
|
||||
|
475
gcc/alias.c
475
gcc/alias.c
@ -45,6 +45,58 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
#include "cgraph.h"
|
||||
#include "varray.h"
|
||||
#include "tree-pass.h"
|
||||
#include "ipa-type-escape.h"
|
||||
|
||||
/* The aliasing API provided here solves related but different problems:
|
||||
|
||||
Say there exists (in c)
|
||||
|
||||
struct X {
|
||||
struct Y y1;
|
||||
struct Z z2;
|
||||
} x1, *px1, *px2;
|
||||
|
||||
struct Y y2, *py;
|
||||
struct Z z2, *pz;
|
||||
|
||||
|
||||
py = &px1.y1;
|
||||
px2 = &x1;
|
||||
|
||||
Consider the four questions:
|
||||
|
||||
Can a store to x1 interfere with px2->y1?
|
||||
Can a store to x1 interfere with px2->z2?
|
||||
(*px2).z2
|
||||
Can a store to x1 change the value pointed to by with py?
|
||||
Can a store to x1 change the value pointed to by with pz?
|
||||
|
||||
The answer to these questions can be yes, yes, yes, and maybe.
|
||||
|
||||
The first two questions can be answered with a simple examination
|
||||
of the type system. If structure X contains a field of type Y then
|
||||
a store thru a pointer to an X can overwrite any field that is
|
||||
contained (recursively) in an X (unless we know that px1 != px2).
|
||||
|
||||
The last two of the questions can be solved in the same way as the
|
||||
first two questions but this is too conservative. The observation
|
||||
is that in some cases analysis we can know if which (if any) fields
|
||||
are addressed and if those addresses are used in bad ways. This
|
||||
analysis may be language specific. In C, arbitrary operations may
|
||||
be applied to pointers. However, there is some indication that
|
||||
this may be too conservative for some C++ types.
|
||||
|
||||
The pass ipa-type-escape does this analysis for the types whose
|
||||
instances do not escape across the compilation boundary.
|
||||
|
||||
Historically in GCC, these two problems were combined and a single
|
||||
data structure was used to represent the solution to these
|
||||
problems. We now have two similar but different data structures,
|
||||
The data structure to solve the last two question is similar to the
|
||||
first, but does not contain have the fields in it whose address are
|
||||
never taken. For types that do escape the compilation unit, the
|
||||
data structures will have identical information.
|
||||
*/
|
||||
|
||||
/* The alias sets assigned to MEMs assist the back-end in determining
|
||||
which MEMs can alias which other MEMs. In general, two MEMs in
|
||||
@ -116,12 +168,6 @@ static rtx adjust_offset_for_component_ref (tree, rtx);
|
||||
static int nonoverlapping_memrefs_p (rtx, rtx);
|
||||
static int write_dependence_p (rtx, rtx, int);
|
||||
|
||||
static int nonlocal_mentioned_p_1 (rtx *, void *);
|
||||
static int nonlocal_mentioned_p (rtx);
|
||||
static int nonlocal_referenced_p_1 (rtx *, void *);
|
||||
static int nonlocal_referenced_p (rtx);
|
||||
static int nonlocal_set_p_1 (rtx *, void *);
|
||||
static int nonlocal_set_p (rtx);
|
||||
static void memory_modified_1 (rtx, rtx, void *);
|
||||
static void record_alias_subset (HOST_WIDE_INT, HOST_WIDE_INT);
|
||||
|
||||
@ -1924,9 +1970,8 @@ nonoverlapping_component_refs_p (tree x, tree y)
|
||||
x = TREE_OPERAND (x, 0);
|
||||
}
|
||||
while (x && TREE_CODE (x) == COMPONENT_REF);
|
||||
|
||||
/* Never found a common type. */
|
||||
return false;
|
||||
return true;
|
||||
|
||||
found:
|
||||
/* If we're left with accessing different fields of a structure,
|
||||
@ -2006,22 +2051,34 @@ nonoverlapping_memrefs_p (rtx x, rtx y)
|
||||
/* Unless both have exprs, we can't tell anything. */
|
||||
if (exprx == 0 || expry == 0)
|
||||
return 0;
|
||||
|
||||
|
||||
/* If both are field references, we may be able to determine something. */
|
||||
if (TREE_CODE (exprx) == COMPONENT_REF
|
||||
&& TREE_CODE (expry) == COMPONENT_REF
|
||||
&& nonoverlapping_component_refs_p (exprx, expry))
|
||||
return 1;
|
||||
|
||||
|
||||
/* If the field reference test failed, look at the DECLs involved. */
|
||||
moffsetx = MEM_OFFSET (x);
|
||||
if (TREE_CODE (exprx) == COMPONENT_REF)
|
||||
{
|
||||
tree t = decl_for_component_ref (exprx);
|
||||
if (! t)
|
||||
return 0;
|
||||
moffsetx = adjust_offset_for_component_ref (exprx, moffsetx);
|
||||
exprx = t;
|
||||
if (TREE_CODE (expry) == VAR_DECL
|
||||
&& POINTER_TYPE_P (TREE_TYPE (expry)))
|
||||
{
|
||||
tree field = TREE_OPERAND (exprx, 1);
|
||||
tree fieldcontext = DECL_FIELD_CONTEXT (field);
|
||||
if (ipa_type_escape_field_does_not_clobber_p (fieldcontext,
|
||||
TREE_TYPE (field)))
|
||||
return 1;
|
||||
}
|
||||
{
|
||||
tree t = decl_for_component_ref (exprx);
|
||||
if (! t)
|
||||
return 0;
|
||||
moffsetx = adjust_offset_for_component_ref (exprx, moffsetx);
|
||||
exprx = t;
|
||||
}
|
||||
}
|
||||
else if (INDIRECT_REF_P (exprx))
|
||||
{
|
||||
@ -2034,11 +2091,22 @@ nonoverlapping_memrefs_p (rtx x, rtx y)
|
||||
moffsety = MEM_OFFSET (y);
|
||||
if (TREE_CODE (expry) == COMPONENT_REF)
|
||||
{
|
||||
tree t = decl_for_component_ref (expry);
|
||||
if (! t)
|
||||
return 0;
|
||||
moffsety = adjust_offset_for_component_ref (expry, moffsety);
|
||||
expry = t;
|
||||
if (TREE_CODE (exprx) == VAR_DECL
|
||||
&& POINTER_TYPE_P (TREE_TYPE (exprx)))
|
||||
{
|
||||
tree field = TREE_OPERAND (expry, 1);
|
||||
tree fieldcontext = DECL_FIELD_CONTEXT (field);
|
||||
if (ipa_type_escape_field_does_not_clobber_p (fieldcontext,
|
||||
TREE_TYPE (field)))
|
||||
return 1;
|
||||
}
|
||||
{
|
||||
tree t = decl_for_component_ref (expry);
|
||||
if (! t)
|
||||
return 0;
|
||||
moffsety = adjust_offset_for_component_ref (expry, moffsety);
|
||||
expry = t;
|
||||
}
|
||||
}
|
||||
else if (INDIRECT_REF_P (expry))
|
||||
{
|
||||
@ -2326,353 +2394,6 @@ output_dependence (rtx mem, rtx x)
|
||||
return write_dependence_p (mem, x, /*writep=*/1);
|
||||
}
|
||||
|
||||
/* A subroutine of nonlocal_mentioned_p, returns 1 if *LOC mentions
|
||||
something which is not local to the function and is not constant. */
|
||||
|
||||
static int
|
||||
nonlocal_mentioned_p_1 (rtx *loc, void *data ATTRIBUTE_UNUSED)
|
||||
{
|
||||
rtx x = *loc;
|
||||
rtx base;
|
||||
int regno;
|
||||
|
||||
if (! x)
|
||||
return 0;
|
||||
|
||||
switch (GET_CODE (x))
|
||||
{
|
||||
case SUBREG:
|
||||
if (REG_P (SUBREG_REG (x)))
|
||||
{
|
||||
/* Global registers are not local. */
|
||||
if (REGNO (SUBREG_REG (x)) < FIRST_PSEUDO_REGISTER
|
||||
&& global_regs[subreg_regno (x)])
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case REG:
|
||||
regno = REGNO (x);
|
||||
/* Global registers are not local. */
|
||||
if (regno < FIRST_PSEUDO_REGISTER && global_regs[regno])
|
||||
return 1;
|
||||
return 0;
|
||||
|
||||
case SCRATCH:
|
||||
case PC:
|
||||
case CC0:
|
||||
case CONST_INT:
|
||||
case CONST_DOUBLE:
|
||||
case CONST_VECTOR:
|
||||
case CONST:
|
||||
case LABEL_REF:
|
||||
return 0;
|
||||
|
||||
case SYMBOL_REF:
|
||||
/* Constants in the function's constants pool are constant. */
|
||||
if (CONSTANT_POOL_ADDRESS_P (x))
|
||||
return 0;
|
||||
return 1;
|
||||
|
||||
case CALL:
|
||||
/* Non-constant calls and recursion are not local. */
|
||||
return 1;
|
||||
|
||||
case MEM:
|
||||
/* Be overly conservative and consider any volatile memory
|
||||
reference as not local. */
|
||||
if (MEM_VOLATILE_P (x))
|
||||
return 1;
|
||||
base = find_base_term (XEXP (x, 0));
|
||||
if (base)
|
||||
{
|
||||
/* A Pmode ADDRESS could be a reference via the structure value
|
||||
address or static chain. Such memory references are nonlocal.
|
||||
|
||||
Thus, we have to examine the contents of the ADDRESS to find
|
||||
out if this is a local reference or not. */
|
||||
if (GET_CODE (base) == ADDRESS
|
||||
&& GET_MODE (base) == Pmode
|
||||
&& (XEXP (base, 0) == stack_pointer_rtx
|
||||
|| XEXP (base, 0) == arg_pointer_rtx
|
||||
#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
|
||||
|| XEXP (base, 0) == hard_frame_pointer_rtx
|
||||
#endif
|
||||
|| XEXP (base, 0) == frame_pointer_rtx))
|
||||
return 0;
|
||||
/* Constants in the function's constant pool are constant. */
|
||||
if (GET_CODE (base) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (base))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
|
||||
case UNSPEC_VOLATILE:
|
||||
case ASM_INPUT:
|
||||
return 1;
|
||||
|
||||
case ASM_OPERANDS:
|
||||
if (MEM_VOLATILE_P (x))
|
||||
return 1;
|
||||
|
||||
/* Fall through. */
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns nonzero if X might mention something which is not
|
||||
local to the function and is not constant. */
|
||||
|
||||
static int
|
||||
nonlocal_mentioned_p (rtx x)
|
||||
{
|
||||
if (INSN_P (x))
|
||||
{
|
||||
if (CALL_P (x))
|
||||
{
|
||||
if (! CONST_OR_PURE_CALL_P (x))
|
||||
return 1;
|
||||
x = CALL_INSN_FUNCTION_USAGE (x);
|
||||
if (x == 0)
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
x = PATTERN (x);
|
||||
}
|
||||
|
||||
return for_each_rtx (&x, nonlocal_mentioned_p_1, NULL);
|
||||
}
|
||||
|
||||
/* A subroutine of nonlocal_referenced_p, returns 1 if *LOC references
|
||||
something which is not local to the function and is not constant. */
|
||||
|
||||
static int
|
||||
nonlocal_referenced_p_1 (rtx *loc, void *data ATTRIBUTE_UNUSED)
|
||||
{
|
||||
rtx x = *loc;
|
||||
|
||||
if (! x)
|
||||
return 0;
|
||||
|
||||
switch (GET_CODE (x))
|
||||
{
|
||||
case MEM:
|
||||
case REG:
|
||||
case SYMBOL_REF:
|
||||
case SUBREG:
|
||||
return nonlocal_mentioned_p (x);
|
||||
|
||||
case CALL:
|
||||
/* Non-constant calls and recursion are not local. */
|
||||
return 1;
|
||||
|
||||
case SET:
|
||||
if (nonlocal_mentioned_p (SET_SRC (x)))
|
||||
return 1;
|
||||
|
||||
if (MEM_P (SET_DEST (x)))
|
||||
return nonlocal_mentioned_p (XEXP (SET_DEST (x), 0));
|
||||
|
||||
/* If the destination is anything other than a CC0, PC,
|
||||
MEM, REG, or a SUBREG of a REG that occupies all of
|
||||
the REG, then X references nonlocal memory if it is
|
||||
mentioned in the destination. */
|
||||
if (GET_CODE (SET_DEST (x)) != CC0
|
||||
&& GET_CODE (SET_DEST (x)) != PC
|
||||
&& !REG_P (SET_DEST (x))
|
||||
&& ! (GET_CODE (SET_DEST (x)) == SUBREG
|
||||
&& REG_P (SUBREG_REG (SET_DEST (x)))
|
||||
&& (((GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_DEST (x))))
|
||||
+ (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)
|
||||
== ((GET_MODE_SIZE (GET_MODE (SET_DEST (x)))
|
||||
+ (UNITS_PER_WORD - 1)) / UNITS_PER_WORD))))
|
||||
return nonlocal_mentioned_p (SET_DEST (x));
|
||||
return 0;
|
||||
|
||||
case CLOBBER:
|
||||
if (MEM_P (XEXP (x, 0)))
|
||||
return nonlocal_mentioned_p (XEXP (XEXP (x, 0), 0));
|
||||
return 0;
|
||||
|
||||
case USE:
|
||||
return nonlocal_mentioned_p (XEXP (x, 0));
|
||||
|
||||
case ASM_INPUT:
|
||||
case UNSPEC_VOLATILE:
|
||||
return 1;
|
||||
|
||||
case ASM_OPERANDS:
|
||||
if (MEM_VOLATILE_P (x))
|
||||
return 1;
|
||||
|
||||
/* Fall through. */
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns nonzero if X might reference something which is not
|
||||
local to the function and is not constant. */
|
||||
|
||||
static int
|
||||
nonlocal_referenced_p (rtx x)
|
||||
{
|
||||
if (INSN_P (x))
|
||||
{
|
||||
if (CALL_P (x))
|
||||
{
|
||||
if (! CONST_OR_PURE_CALL_P (x))
|
||||
return 1;
|
||||
x = CALL_INSN_FUNCTION_USAGE (x);
|
||||
if (x == 0)
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
x = PATTERN (x);
|
||||
}
|
||||
|
||||
return for_each_rtx (&x, nonlocal_referenced_p_1, NULL);
|
||||
}
|
||||
|
||||
/* A subroutine of nonlocal_set_p, returns 1 if *LOC sets
|
||||
something which is not local to the function and is not constant. */
|
||||
|
||||
static int
|
||||
nonlocal_set_p_1 (rtx *loc, void *data ATTRIBUTE_UNUSED)
|
||||
{
|
||||
rtx x = *loc;
|
||||
|
||||
if (! x)
|
||||
return 0;
|
||||
|
||||
switch (GET_CODE (x))
|
||||
{
|
||||
case CALL:
|
||||
/* Non-constant calls and recursion are not local. */
|
||||
return 1;
|
||||
|
||||
case PRE_INC:
|
||||
case PRE_DEC:
|
||||
case POST_INC:
|
||||
case POST_DEC:
|
||||
case PRE_MODIFY:
|
||||
case POST_MODIFY:
|
||||
return nonlocal_mentioned_p (XEXP (x, 0));
|
||||
|
||||
case SET:
|
||||
if (nonlocal_mentioned_p (SET_DEST (x)))
|
||||
return 1;
|
||||
return nonlocal_set_p (SET_SRC (x));
|
||||
|
||||
case CLOBBER:
|
||||
return nonlocal_mentioned_p (XEXP (x, 0));
|
||||
|
||||
case USE:
|
||||
return 0;
|
||||
|
||||
case ASM_INPUT:
|
||||
case UNSPEC_VOLATILE:
|
||||
return 1;
|
||||
|
||||
case ASM_OPERANDS:
|
||||
if (MEM_VOLATILE_P (x))
|
||||
return 1;
|
||||
|
||||
/* Fall through. */
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns nonzero if X might set something which is not
|
||||
local to the function and is not constant. */
|
||||
|
||||
static int
|
||||
nonlocal_set_p (rtx x)
|
||||
{
|
||||
if (INSN_P (x))
|
||||
{
|
||||
if (CALL_P (x))
|
||||
{
|
||||
if (! CONST_OR_PURE_CALL_P (x))
|
||||
return 1;
|
||||
x = CALL_INSN_FUNCTION_USAGE (x);
|
||||
if (x == 0)
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
x = PATTERN (x);
|
||||
}
|
||||
|
||||
return for_each_rtx (&x, nonlocal_set_p_1, NULL);
|
||||
}
|
||||
|
||||
/* Mark the function if it is pure or constant. */
|
||||
|
||||
void
|
||||
mark_constant_function (void)
|
||||
{
|
||||
rtx insn;
|
||||
int nonlocal_memory_referenced;
|
||||
|
||||
if (TREE_READONLY (current_function_decl)
|
||||
|| DECL_IS_PURE (current_function_decl)
|
||||
|| TREE_THIS_VOLATILE (current_function_decl)
|
||||
|| current_function_has_nonlocal_goto
|
||||
|| !targetm.binds_local_p (current_function_decl))
|
||||
return;
|
||||
|
||||
/* A loop might not return which counts as a side effect. */
|
||||
if (mark_dfs_back_edges ())
|
||||
return;
|
||||
|
||||
nonlocal_memory_referenced = 0;
|
||||
|
||||
init_alias_analysis ();
|
||||
|
||||
/* Determine if this is a constant or pure function. */
|
||||
|
||||
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
||||
{
|
||||
if (! INSN_P (insn))
|
||||
continue;
|
||||
|
||||
if (nonlocal_set_p (insn) || global_reg_mentioned_p (insn)
|
||||
|| volatile_refs_p (PATTERN (insn)))
|
||||
break;
|
||||
|
||||
if (! nonlocal_memory_referenced)
|
||||
nonlocal_memory_referenced = nonlocal_referenced_p (insn);
|
||||
}
|
||||
|
||||
end_alias_analysis ();
|
||||
|
||||
/* Mark the function. */
|
||||
|
||||
if (insn)
|
||||
;
|
||||
else if (nonlocal_memory_referenced)
|
||||
{
|
||||
cgraph_rtl_info (current_function_decl)->pure_function = 1;
|
||||
DECL_IS_PURE (current_function_decl) = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
cgraph_rtl_info (current_function_decl)->const_function = 1;
|
||||
TREE_READONLY (current_function_decl) = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
init_alias_once (void)
|
||||
@ -2979,28 +2700,6 @@ rest_of_handle_cfg (void)
|
||||
if (optimize)
|
||||
cleanup_cfg (CLEANUP_EXPENSIVE
|
||||
| (flag_thread_jumps ? CLEANUP_THREADING : 0));
|
||||
|
||||
/* It may make more sense to mark constant functions after dead code is
|
||||
eliminated by life_analysis, but we need to do it early, as -fprofile-arcs
|
||||
may insert code making function non-constant, but we still must consider
|
||||
it as constant, otherwise -fbranch-probabilities will not read data back.
|
||||
|
||||
life_analysis rarely eliminates modification of external memory.
|
||||
|
||||
FIXME: now with tree based profiling we are in the trap described above
|
||||
again. It seems to be easiest to disable the optimization for time
|
||||
being before the problem is either solved by moving the transformation
|
||||
to the IPA level (we need the CFG for this) or the very early optimization
|
||||
passes are made to ignore the const/pure flags so code does not change. */
|
||||
if (optimize
|
||||
&& (!flag_tree_based_profiling
|
||||
|| (!profile_arc_flag && !flag_branch_probabilities)))
|
||||
{
|
||||
/* Alias analysis depends on this information and mark_constant_function
|
||||
depends on alias analysis. */
|
||||
reg_scan (get_insns (), max_reg_num ());
|
||||
mark_constant_function ();
|
||||
}
|
||||
}
|
||||
|
||||
struct tree_opt_pass pass_cfg =
|
||||
|
@ -570,17 +570,8 @@ flags_from_decl_or_type (tree exp)
|
||||
|
||||
if (DECL_P (exp))
|
||||
{
|
||||
struct cgraph_rtl_info *i = cgraph_rtl_info (exp);
|
||||
type = TREE_TYPE (exp);
|
||||
|
||||
if (i)
|
||||
{
|
||||
if (i->pure_function)
|
||||
flags |= ECF_PURE | ECF_LIBCALL_BLOCK;
|
||||
if (i->const_function)
|
||||
flags |= ECF_CONST | ECF_LIBCALL_BLOCK;
|
||||
}
|
||||
|
||||
/* The function exp may have the `malloc' attribute. */
|
||||
if (DECL_IS_MALLOC (exp))
|
||||
flags |= ECF_MALLOC;
|
||||
|
@ -107,8 +107,6 @@ struct cgraph_global_info GTY(())
|
||||
struct cgraph_rtl_info GTY(())
|
||||
{
|
||||
int preferred_incoming_stack_boundary;
|
||||
bool const_function;
|
||||
bool pure_function;
|
||||
};
|
||||
|
||||
/* The cgraph data structure.
|
||||
|
@ -491,6 +491,18 @@ finstrument-functions
|
||||
Common Report Var(flag_instrument_function_entry_exit)
|
||||
Instrument function entry and exit with profiling calls
|
||||
|
||||
fipa-pure-const
|
||||
Common Report Var(flag_ipa_pure_const) Init(0)
|
||||
Discover pure and const functions
|
||||
|
||||
fipa-reference
|
||||
Common Report Var(flag_ipa_reference) Init(0)
|
||||
Discover readonly and non addressable static variables
|
||||
|
||||
fipa-type-escape
|
||||
Common Report Var(flag_ipa_type_escape) Init(0)
|
||||
Type based escape and alias analysis
|
||||
|
||||
fivopts
|
||||
Common Report Var(flag_ivopts) Init(1)
|
||||
Optimize induction variables on trees
|
||||
@ -915,6 +927,10 @@ ftree-pre
|
||||
Common Report Var(flag_tree_pre)
|
||||
Enable SSA-PRE optimization on trees
|
||||
|
||||
ftree-promote-statics
|
||||
Common Report Var(flag_tree_promote_statics) Init(0)
|
||||
Enable promotion of static variables
|
||||
|
||||
ftree-salias
|
||||
Common Report Var(flag_tree_salias)
|
||||
Perform structural alias analysis
|
||||
|
729
gcc/ipa-pure-const.c
Normal file
729
gcc/ipa-pure-const.c
Normal file
@ -0,0 +1,729 @@
|
||||
/* Callgraph based analysis of static variables.
|
||||
Copyright (C) 2004, 2005 Free Software Foundation, Inc.
|
||||
Contributed by Kenneth Zadeck <zadeck@naturalbridge.com>
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation; either version 2, or (at your option) any later
|
||||
version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING. If not, write to the Free
|
||||
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA. */
|
||||
|
||||
/* This file mark functions as being either const (TREE_READONLY) or
|
||||
pure (DECL_IS_PURE).
|
||||
|
||||
This must be run after inlining decisions have been made since
|
||||
otherwise, the local sets will not contain information that is
|
||||
consistent with post inlined state. The global sets are not prone
|
||||
to this problem since they are by definition transitive. */
|
||||
|
||||
/* The code in this module is called by the ipa pass manager. It
|
||||
should be one of the later passes since it's information is used by
|
||||
the rest of the compilation. */
|
||||
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "tm.h"
|
||||
#include "tree.h"
|
||||
#include "tree-flow.h"
|
||||
#include "tree-inline.h"
|
||||
#include "tree-pass.h"
|
||||
#include "langhooks.h"
|
||||
#include "pointer-set.h"
|
||||
#include "ggc.h"
|
||||
#include "ipa-utils.h"
|
||||
#include "c-common.h"
|
||||
#include "tree-gimple.h"
|
||||
#include "cgraph.h"
|
||||
#include "output.h"
|
||||
#include "flags.h"
|
||||
#include "timevar.h"
|
||||
#include "diagnostic.h"
|
||||
#include "langhooks.h"
|
||||
|
||||
static struct pointer_set_t *visited_nodes;
|
||||
|
||||
/* Lattice values for const and pure functions. Everything starts out
|
||||
being const, then may drop to pure and then neither depending on
|
||||
what is found. */
|
||||
enum pure_const_state_e
|
||||
{
|
||||
IPA_CONST,
|
||||
IPA_PURE,
|
||||
IPA_NEITHER
|
||||
};
|
||||
|
||||
/* Holder inserted into the ipa_dfs_info aux field to hold the
|
||||
const_state. */
|
||||
struct funct_state_d
|
||||
{
|
||||
enum pure_const_state_e pure_const_state;
|
||||
bool state_set_in_source;
|
||||
};
|
||||
|
||||
typedef struct funct_state_d * funct_state;
|
||||
|
||||
/* Return the function state from NODE. */
|
||||
|
||||
static inline funct_state
|
||||
get_function_state (struct cgraph_node *node)
|
||||
{
|
||||
struct ipa_dfs_info * info = node->aux;
|
||||
return info->aux;
|
||||
}
|
||||
|
||||
/* Check to see if the use (or definition when CHECHING_WRITE is true)
|
||||
variable T is legal in a function that is either pure or const. */
|
||||
|
||||
static inline void
|
||||
check_decl (funct_state local,
|
||||
tree t, bool checking_write)
|
||||
{
|
||||
/* If the variable has the "used" attribute, treat it as if it had a
|
||||
been touched by the devil. */
|
||||
if (lookup_attribute ("used", DECL_ATTRIBUTES (t)))
|
||||
{
|
||||
local->pure_const_state = IPA_NEITHER;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Do not want to do anything with volatile except mark any
|
||||
function that uses one to be not const or pure. */
|
||||
if (TREE_THIS_VOLATILE (t))
|
||||
{
|
||||
local->pure_const_state = IPA_NEITHER;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Do not care about a local automatic that is not static. */
|
||||
if (!TREE_STATIC (t) && !DECL_EXTERNAL (t))
|
||||
return;
|
||||
|
||||
/* Since we have dealt with the locals and params cases above, if we
|
||||
are CHECKING_WRITE, this cannot be a pure or constant
|
||||
function. */
|
||||
if (checking_write)
|
||||
local->pure_const_state = IPA_NEITHER;
|
||||
|
||||
if (DECL_EXTERNAL (t) || TREE_PUBLIC (t))
|
||||
{
|
||||
/* If the front end set the variable to be READONLY and
|
||||
constant, we can allow this variable in pure or const
|
||||
functions but the scope is too large for our analysis to set
|
||||
these bits ourselves. */
|
||||
|
||||
if (TREE_READONLY (t)
|
||||
&& DECL_INITIAL (t)
|
||||
&& is_gimple_min_invariant (DECL_INITIAL (t)))
|
||||
; /* Read of a constant, do not change the function state. */
|
||||
else
|
||||
{
|
||||
/* Just a regular read. */
|
||||
if (local->pure_const_state == IPA_CONST)
|
||||
local->pure_const_state = IPA_PURE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Compilation level statics can be read if they are readonly
|
||||
variables. */
|
||||
if (TREE_READONLY (t))
|
||||
return;
|
||||
|
||||
/* Just a regular read. */
|
||||
if (local->pure_const_state == IPA_CONST)
|
||||
local->pure_const_state = IPA_PURE;
|
||||
}
|
||||
|
||||
/* If T is a VAR_DECL check to see if it is an allowed reference. */
|
||||
|
||||
static void
|
||||
check_operand (funct_state local,
|
||||
tree t, bool checking_write)
|
||||
{
|
||||
if (!t) return;
|
||||
|
||||
if (TREE_CODE (t) == VAR_DECL)
|
||||
check_decl (local, t, checking_write);
|
||||
}
|
||||
|
||||
/* Examine tree T for references. */
|
||||
|
||||
static void
|
||||
check_tree (funct_state local, tree t, bool checking_write)
|
||||
{
|
||||
if ((TREE_CODE (t) == EXC_PTR_EXPR) || (TREE_CODE (t) == FILTER_EXPR))
|
||||
return;
|
||||
|
||||
while (TREE_CODE (t) == REALPART_EXPR
|
||||
|| TREE_CODE (t) == IMAGPART_EXPR
|
||||
|| handled_component_p (t))
|
||||
{
|
||||
if (TREE_CODE (t) == ARRAY_REF)
|
||||
check_operand (local, TREE_OPERAND (t, 1), false);
|
||||
t = TREE_OPERAND (t, 0);
|
||||
}
|
||||
|
||||
/* The bottom of an indirect reference can only be read, not
|
||||
written. */
|
||||
if (INDIRECT_REF_P (t))
|
||||
{
|
||||
check_tree (local, TREE_OPERAND (t, 0), false);
|
||||
|
||||
/* Any indirect reference that occurs on the lhs
|
||||
disqualifies the function from being pure or const. Any
|
||||
indirect reference that occurs on the rhs disqualifies
|
||||
the function from being const. */
|
||||
if (checking_write)
|
||||
local->pure_const_state = IPA_NEITHER;
|
||||
else
|
||||
if (local->pure_const_state == IPA_CONST)
|
||||
local->pure_const_state = IPA_PURE;
|
||||
}
|
||||
|
||||
if (SSA_VAR_P (t))
|
||||
check_operand (local, t, checking_write);
|
||||
}
|
||||
|
||||
/* Scan tree T to see if there are any addresses taken in within T. */
|
||||
|
||||
static void
|
||||
look_for_address_of (funct_state local, tree t)
|
||||
{
|
||||
if (TREE_CODE (t) == ADDR_EXPR)
|
||||
{
|
||||
tree x = get_base_var (t);
|
||||
if (TREE_CODE (x) == VAR_DECL)
|
||||
{
|
||||
check_decl (local, x, false);
|
||||
|
||||
/* Taking the address of something appears to be reasonable
|
||||
in PURE code. Not allowed in const. */
|
||||
if (local->pure_const_state == IPA_CONST)
|
||||
local->pure_const_state = IPA_PURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check to see if T is a read or address of operation on a var we are
|
||||
interested in analyzing. LOCAL is passed in to get access to its
|
||||
bit vectors. */
|
||||
|
||||
static void
|
||||
check_rhs_var (funct_state local, tree t)
|
||||
{
|
||||
look_for_address_of (local, t);
|
||||
|
||||
/* Memcmp and strlen can both trap and they are declared pure. */
|
||||
if (tree_could_trap_p (t)
|
||||
&& local->pure_const_state == IPA_CONST)
|
||||
local->pure_const_state = IPA_PURE;
|
||||
|
||||
check_tree(local, t, false);
|
||||
}
|
||||
|
||||
/* Check to see if T is an assignment to a var we are interested in
|
||||
analyzing. LOCAL is passed in to get access to its bit vectors. */
|
||||
|
||||
static void
|
||||
check_lhs_var (funct_state local, tree t)
|
||||
{
|
||||
/* Memcmp and strlen can both trap and they are declared pure.
|
||||
Which seems to imply that we can apply the same rule here. */
|
||||
if (tree_could_trap_p (t)
|
||||
&& local->pure_const_state == IPA_CONST)
|
||||
local->pure_const_state = IPA_PURE;
|
||||
|
||||
check_tree(local, t, true);
|
||||
}
|
||||
|
||||
/* This is a scaled down version of get_asm_expr_operands from
|
||||
tree_ssa_operands.c. The version there runs much later and assumes
|
||||
that aliasing information is already available. Here we are just
|
||||
trying to find if the set of inputs and outputs contain references
|
||||
or address of operations to local static variables. STMT is the
|
||||
actual asm statement. */
|
||||
|
||||
static void
|
||||
get_asm_expr_operands (funct_state local, tree stmt)
|
||||
{
|
||||
int noutputs = list_length (ASM_OUTPUTS (stmt));
|
||||
const char **oconstraints
|
||||
= (const char **) alloca ((noutputs) * sizeof (const char *));
|
||||
int i;
|
||||
tree link;
|
||||
const char *constraint;
|
||||
bool allows_mem, allows_reg, is_inout;
|
||||
|
||||
for (i=0, link = ASM_OUTPUTS (stmt); link; ++i, link = TREE_CHAIN (link))
|
||||
{
|
||||
oconstraints[i] = constraint
|
||||
= TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
|
||||
parse_output_constraint (&constraint, i, 0, 0,
|
||||
&allows_mem, &allows_reg, &is_inout);
|
||||
|
||||
check_lhs_var (local, TREE_VALUE (link));
|
||||
}
|
||||
|
||||
for (link = ASM_INPUTS (stmt); link; link = TREE_CHAIN (link))
|
||||
{
|
||||
constraint
|
||||
= TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
|
||||
parse_input_constraint (&constraint, 0, 0, noutputs, 0,
|
||||
oconstraints, &allows_mem, &allows_reg);
|
||||
|
||||
check_rhs_var (local, TREE_VALUE (link));
|
||||
}
|
||||
|
||||
for (link = ASM_CLOBBERS (stmt); link; link = TREE_CHAIN (link))
|
||||
if (simple_cst_equal(TREE_VALUE (link), memory_identifier_string) == 1)
|
||||
/* Abandon all hope, ye who enter here. */
|
||||
local->pure_const_state = IPA_NEITHER;
|
||||
|
||||
if (ASM_VOLATILE_P (stmt))
|
||||
local->pure_const_state = IPA_NEITHER;
|
||||
}
|
||||
|
||||
/* Check the parameters of a function call to CALL_EXPR to see if
|
||||
there are any references in the parameters that are not allowed for
|
||||
pure or const functions. Also check to see if this is either an
|
||||
indirect call, a call outside the compilation unit, or has special
|
||||
attributes that may also effect the purity. The CALL_EXPR node for
|
||||
the entire call expression. */
|
||||
|
||||
static void
|
||||
check_call (funct_state local, tree call_expr)
|
||||
{
|
||||
int flags = call_expr_flags(call_expr);
|
||||
tree operand_list = TREE_OPERAND (call_expr, 1);
|
||||
tree operand;
|
||||
tree callee_t = get_callee_fndecl (call_expr);
|
||||
struct cgraph_node* callee;
|
||||
enum availability avail = AVAIL_NOT_AVAILABLE;
|
||||
|
||||
for (operand = operand_list;
|
||||
operand != NULL_TREE;
|
||||
operand = TREE_CHAIN (operand))
|
||||
{
|
||||
tree argument = TREE_VALUE (operand);
|
||||
check_rhs_var (local, argument);
|
||||
}
|
||||
|
||||
/* The const and pure flags are set by a variety of places in the
|
||||
compiler (including here). If someone has already set the flags
|
||||
for the callee, (such as for some of the builtins) we will use
|
||||
them, otherwise we will compute our own information.
|
||||
|
||||
Const and pure functions have less clobber effects than other
|
||||
functions so we process these first. Otherwise if it is a call
|
||||
outside the compilation unit or an indirect call we punt. This
|
||||
leaves local calls which will be processed by following the call
|
||||
graph. */
|
||||
if (callee_t)
|
||||
{
|
||||
callee = cgraph_node(callee_t);
|
||||
avail = cgraph_function_body_availability (callee);
|
||||
|
||||
/* When bad things happen to bad functions, they cannot be const
|
||||
or pure. */
|
||||
if (setjmp_call_p (callee_t))
|
||||
local->pure_const_state = IPA_NEITHER;
|
||||
|
||||
if (DECL_BUILT_IN_CLASS (callee_t) == BUILT_IN_NORMAL)
|
||||
switch (DECL_FUNCTION_CODE (callee_t))
|
||||
{
|
||||
case BUILT_IN_LONGJMP:
|
||||
case BUILT_IN_NONLOCAL_GOTO:
|
||||
local->pure_const_state = IPA_NEITHER;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* The callee is either unknown (indirect call) or there is just no
|
||||
scannable code for it (external call) . We look to see if there
|
||||
are any bits available for the callee (such as by declaration or
|
||||
because it is builtin) and process solely on the basis of those
|
||||
bits. */
|
||||
if (avail == AVAIL_NOT_AVAILABLE || avail == AVAIL_OVERWRITABLE)
|
||||
{
|
||||
if (flags & ECF_PURE)
|
||||
{
|
||||
if (local->pure_const_state == IPA_CONST)
|
||||
local->pure_const_state = IPA_PURE;
|
||||
}
|
||||
else
|
||||
local->pure_const_state = IPA_NEITHER;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We have the code and we will scan it for the effects. */
|
||||
if (flags & ECF_PURE)
|
||||
{
|
||||
if (local->pure_const_state == IPA_CONST)
|
||||
local->pure_const_state = IPA_PURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* TP is the part of the tree currently under the microscope.
|
||||
WALK_SUBTREES is part of the walk_tree api but is unused here.
|
||||
DATA is cgraph_node of the function being walked. */
|
||||
|
||||
/* FIXME: When this is converted to run over SSA form, this code
|
||||
should be converted to use the operand scanner. */
|
||||
|
||||
static tree
|
||||
scan_function (tree *tp,
|
||||
int *walk_subtrees,
|
||||
void *data)
|
||||
{
|
||||
struct cgraph_node *fn = data;
|
||||
tree t = *tp;
|
||||
funct_state local = get_function_state (fn);
|
||||
|
||||
switch (TREE_CODE (t))
|
||||
{
|
||||
case VAR_DECL:
|
||||
if (DECL_INITIAL (t))
|
||||
walk_tree (&DECL_INITIAL (t), scan_function, fn, visited_nodes);
|
||||
*walk_subtrees = 0;
|
||||
break;
|
||||
|
||||
case MODIFY_EXPR:
|
||||
{
|
||||
/* First look on the lhs and see what variable is stored to */
|
||||
tree lhs = TREE_OPERAND (t, 0);
|
||||
tree rhs = TREE_OPERAND (t, 1);
|
||||
check_lhs_var (local, lhs);
|
||||
|
||||
/* For the purposes of figuring out what the cast affects */
|
||||
|
||||
/* Next check the operands on the rhs to see if they are ok. */
|
||||
switch (TREE_CODE_CLASS (TREE_CODE (rhs)))
|
||||
{
|
||||
case tcc_binary:
|
||||
{
|
||||
tree op0 = TREE_OPERAND (rhs, 0);
|
||||
tree op1 = TREE_OPERAND (rhs, 1);
|
||||
check_rhs_var (local, op0);
|
||||
check_rhs_var (local, op1);
|
||||
}
|
||||
break;
|
||||
case tcc_unary:
|
||||
{
|
||||
tree op0 = TREE_OPERAND (rhs, 0);
|
||||
check_rhs_var (local, op0);
|
||||
}
|
||||
|
||||
break;
|
||||
case tcc_reference:
|
||||
check_rhs_var (local, rhs);
|
||||
break;
|
||||
case tcc_declaration:
|
||||
check_rhs_var (local, rhs);
|
||||
break;
|
||||
case tcc_expression:
|
||||
switch (TREE_CODE (rhs))
|
||||
{
|
||||
case ADDR_EXPR:
|
||||
check_rhs_var (local, rhs);
|
||||
break;
|
||||
case CALL_EXPR:
|
||||
check_call (local, rhs);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
*walk_subtrees = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case ADDR_EXPR:
|
||||
/* This case is here to find addresses on rhs of constructors in
|
||||
decl_initial of static variables. */
|
||||
check_rhs_var (local, t);
|
||||
*walk_subtrees = 0;
|
||||
break;
|
||||
|
||||
case LABEL_EXPR:
|
||||
if (DECL_NONLOCAL (TREE_OPERAND (t, 0)))
|
||||
/* Target of long jump. */
|
||||
local->pure_const_state = IPA_NEITHER;
|
||||
break;
|
||||
|
||||
case CALL_EXPR:
|
||||
check_call (local, t);
|
||||
*walk_subtrees = 0;
|
||||
break;
|
||||
|
||||
case ASM_EXPR:
|
||||
get_asm_expr_operands (local, t);
|
||||
*walk_subtrees = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* This is the main routine for finding the reference patterns for
|
||||
global variables within a function FN. */
|
||||
|
||||
static void
|
||||
analyze_function (struct cgraph_node *fn)
|
||||
{
|
||||
funct_state l = xcalloc (1, sizeof (struct funct_state_d));
|
||||
tree decl = fn->decl;
|
||||
struct ipa_dfs_info * w_info = fn->aux;
|
||||
|
||||
w_info->aux = l;
|
||||
|
||||
l->pure_const_state = IPA_CONST;
|
||||
l->state_set_in_source = false;
|
||||
|
||||
/* If this is a volatile function, do not touch this unless it has
|
||||
been marked as const or pure by the front end. */
|
||||
if (TREE_THIS_VOLATILE (decl))
|
||||
{
|
||||
l->pure_const_state = IPA_NEITHER;
|
||||
return;
|
||||
}
|
||||
|
||||
if (TREE_READONLY (decl))
|
||||
{
|
||||
l->pure_const_state = IPA_CONST;
|
||||
l->state_set_in_source = true;
|
||||
}
|
||||
if (DECL_IS_PURE (decl))
|
||||
{
|
||||
l->pure_const_state = IPA_PURE;
|
||||
l->state_set_in_source = true;
|
||||
}
|
||||
|
||||
if (dump_file)
|
||||
{
|
||||
fprintf (dump_file, "\n local analysis of %s with initial value = %d\n ",
|
||||
cgraph_node_name (fn),
|
||||
l->pure_const_state);
|
||||
}
|
||||
|
||||
if (!l->state_set_in_source)
|
||||
{
|
||||
struct function *this_cfun = DECL_STRUCT_FUNCTION (decl);
|
||||
basic_block this_block;
|
||||
|
||||
FOR_EACH_BB_FN (this_block, this_cfun)
|
||||
{
|
||||
block_stmt_iterator bsi;
|
||||
for (bsi = bsi_start (this_block); !bsi_end_p (bsi); bsi_next (&bsi))
|
||||
{
|
||||
walk_tree (bsi_stmt_ptr (bsi), scan_function,
|
||||
fn, visited_nodes);
|
||||
if (l->pure_const_state == IPA_NEITHER)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (l->pure_const_state != IPA_NEITHER)
|
||||
{
|
||||
tree old_decl = current_function_decl;
|
||||
/* Const functions cannot have back edges (an
|
||||
indication of possible infinite loop side
|
||||
effect. */
|
||||
|
||||
current_function_decl = fn->decl;
|
||||
|
||||
/* The C++ front end, has a tendency to some times jerk away
|
||||
a function after it has created it. This should have
|
||||
been fixed. */
|
||||
gcc_assert (DECL_STRUCT_FUNCTION (fn->decl));
|
||||
|
||||
push_cfun (DECL_STRUCT_FUNCTION (fn->decl));
|
||||
|
||||
if (mark_dfs_back_edges ())
|
||||
l->pure_const_state = IPA_NEITHER;
|
||||
|
||||
current_function_decl = old_decl;
|
||||
pop_cfun ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Produce the global information by preforming a transitive closure
|
||||
on the local information that was produced by ipa_analyze_function
|
||||
and ipa_analyze_variable. */
|
||||
|
||||
static void
|
||||
static_execute (void)
|
||||
{
|
||||
struct cgraph_node *node;
|
||||
struct cgraph_node *w;
|
||||
struct cgraph_node **order =
|
||||
xcalloc (cgraph_n_nodes, sizeof (struct cgraph_node *));
|
||||
int order_pos = order_pos = ipa_utils_reduced_inorder (order, true, false);
|
||||
int i;
|
||||
struct ipa_dfs_info * w_info;
|
||||
|
||||
if (!memory_identifier_string)
|
||||
memory_identifier_string = build_string(7, "memory");
|
||||
|
||||
/* There are some shared nodes, in particular the initializers on
|
||||
static declarations. We do not need to scan them more than once
|
||||
since all we would be interested in are the addressof
|
||||
operations. */
|
||||
visited_nodes = pointer_set_create ();
|
||||
|
||||
/* Process all of the functions.
|
||||
|
||||
We do not want to process any of the clones so we check that this
|
||||
is a master clone. However, we do NOT process any
|
||||
AVAIL_OVERWRITABLE functions (these are never clones) we cannot
|
||||
guarantee that what we learn about the one we see will be true
|
||||
for the one that overriders it.
|
||||
*/
|
||||
for (node = cgraph_nodes; node; node = node->next)
|
||||
if (node->analyzed && cgraph_is_master_clone (node))
|
||||
analyze_function (node);
|
||||
|
||||
pointer_set_destroy (visited_nodes);
|
||||
visited_nodes = NULL;
|
||||
if (dump_file)
|
||||
{
|
||||
dump_cgraph (dump_file);
|
||||
ipa_utils_print_order(dump_file, "reduced", order, order_pos);
|
||||
}
|
||||
|
||||
/* Propagate the local information thru the call graph to produce
|
||||
the global information. All the nodes within a cycle will have
|
||||
the same info so we collapse cycles first. Then we can do the
|
||||
propagation in one pass from the leaves to the roots. */
|
||||
for (i = 0; i < order_pos; i++ )
|
||||
{
|
||||
enum pure_const_state_e pure_const_state = IPA_CONST;
|
||||
node = order[i];
|
||||
|
||||
/* Find the worst state for any node in the cycle. */
|
||||
w = node;
|
||||
while (w)
|
||||
{
|
||||
funct_state w_l = get_function_state (w);
|
||||
if (pure_const_state < w_l->pure_const_state)
|
||||
pure_const_state = w_l->pure_const_state;
|
||||
|
||||
if (pure_const_state == IPA_NEITHER)
|
||||
break;
|
||||
|
||||
if (!w_l->state_set_in_source)
|
||||
{
|
||||
struct cgraph_edge *e;
|
||||
for (e = w->callees; e; e = e->next_callee)
|
||||
{
|
||||
struct cgraph_node *y = e->callee;
|
||||
/* Only look at the master nodes and skip external nodes. */
|
||||
y = cgraph_master_clone (y);
|
||||
if (y)
|
||||
{
|
||||
funct_state y_l = get_function_state (y);
|
||||
if (pure_const_state < y_l->pure_const_state)
|
||||
pure_const_state = y_l->pure_const_state;
|
||||
if (pure_const_state == IPA_NEITHER)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
w_info = w->aux;
|
||||
w = w_info->next_cycle;
|
||||
}
|
||||
|
||||
/* Copy back the region's pure_const_state which is shared by
|
||||
all nodes in the region. */
|
||||
w = node;
|
||||
while (w)
|
||||
{
|
||||
funct_state w_l = get_function_state (w);
|
||||
|
||||
/* All nodes within a cycle share the same info. */
|
||||
if (!w_l->state_set_in_source)
|
||||
{
|
||||
w_l->pure_const_state = pure_const_state;
|
||||
switch (pure_const_state)
|
||||
{
|
||||
case IPA_CONST:
|
||||
TREE_READONLY (w->decl) = 1;
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Function found to be const: %s\n",
|
||||
lang_hooks.decl_printable_name(w->decl, 2));
|
||||
break;
|
||||
|
||||
case IPA_PURE:
|
||||
DECL_IS_PURE (w->decl) = 1;
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Function found to be pure: %s\n",
|
||||
lang_hooks.decl_printable_name(w->decl, 2));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
w_info = w->aux;
|
||||
w = w_info->next_cycle;
|
||||
}
|
||||
}
|
||||
|
||||
/* Cleanup. */
|
||||
for (node = cgraph_nodes; node; node = node->next)
|
||||
/* Get rid of the aux information. */
|
||||
if (node->aux)
|
||||
{
|
||||
free (node->aux);
|
||||
node->aux = NULL;
|
||||
}
|
||||
|
||||
free (order);
|
||||
}
|
||||
|
||||
static bool
|
||||
gate_pure_const (void)
|
||||
{
|
||||
return (flag_unit_at_a_time != 0 && flag_ipa_pure_const
|
||||
/* Don't bother doing anything if the program has errors. */
|
||||
&& !(errorcount || sorrycount));
|
||||
}
|
||||
|
||||
struct tree_opt_pass pass_ipa_pure_const =
|
||||
{
|
||||
"ipa-pure-const", /* name */
|
||||
gate_pure_const, /* gate */
|
||||
static_execute, /* execute */
|
||||
NULL, /* sub */
|
||||
NULL, /* next */
|
||||
0, /* static_pass_number */
|
||||
TV_IPA_PURE_CONST, /* tv_id */
|
||||
0, /* properties_required */
|
||||
0, /* properties_provided */
|
||||
0, /* properties_destroyed */
|
||||
0, /* todo_flags_start */
|
||||
0, /* todo_flags_finish */
|
||||
0 /* letter */
|
||||
};
|
||||
|
||||
|
1317
gcc/ipa-reference.c
Normal file
1317
gcc/ipa-reference.c
Normal file
File diff suppressed because it is too large
Load Diff
83
gcc/ipa-reference.h
Normal file
83
gcc/ipa-reference.h
Normal file
@ -0,0 +1,83 @@
|
||||
/* IPA handling of references.
|
||||
Copyright (C) 2004-2005 Free Software Foundation, Inc.
|
||||
Contributed by Kenneth Zadeck <zadeck@naturalbridge.com>
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation; either version 2, or (at your option) any later
|
||||
version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING. If not, write to the Free
|
||||
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA. */
|
||||
|
||||
#ifndef GCC_IPA_REFERENCE_H
|
||||
#define GCC_IPA_REFERENCE_H
|
||||
#include "bitmap.h"
|
||||
#include "tree.h"
|
||||
|
||||
/* The static variables defined within the compilation unit that are
|
||||
loaded or stored directly by function that owns this structure. */
|
||||
|
||||
struct ipa_reference_local_vars_info_d
|
||||
{
|
||||
bitmap statics_read;
|
||||
bitmap statics_written;
|
||||
|
||||
/* Set when this function calls another function external to the
|
||||
compilation unit or if the function has a asm clobber of memory.
|
||||
In general, such calls are modeled as reading and writing all
|
||||
variables (both bits on) but sometime there are attributes on the
|
||||
called function so we can do better. */
|
||||
bool calls_read_all;
|
||||
bool calls_write_all;
|
||||
};
|
||||
|
||||
struct ipa_reference_global_vars_info_d
|
||||
{
|
||||
bitmap statics_read;
|
||||
bitmap statics_written;
|
||||
bitmap statics_not_read;
|
||||
bitmap statics_not_written;
|
||||
};
|
||||
|
||||
/* Statics that are read and written by some set of functions. The
|
||||
local ones are based on the loads and stores local to the function.
|
||||
The global ones are based on the local info as well as the
|
||||
transitive closure of the functions that are called. The
|
||||
structures are separated to allow the global structures to be
|
||||
shared between several functions since every function within a
|
||||
strongly connected component will have the same information. This
|
||||
sharing saves both time and space in the computation of the vectors
|
||||
as well as their translation from decl_uid form to ann_uid
|
||||
form. */
|
||||
|
||||
typedef struct ipa_reference_local_vars_info_d *ipa_reference_local_vars_info_t;
|
||||
typedef struct ipa_reference_global_vars_info_d *ipa_reference_global_vars_info_t;
|
||||
|
||||
struct ipa_reference_vars_info_d
|
||||
{
|
||||
ipa_reference_local_vars_info_t local;
|
||||
ipa_reference_global_vars_info_t global;
|
||||
};
|
||||
|
||||
typedef struct ipa_reference_vars_info_d *ipa_reference_vars_info_t;
|
||||
|
||||
/* In ipa-reference.c */
|
||||
bitmap ipa_reference_get_read_local (tree fn);
|
||||
bitmap ipa_reference_get_written_local (tree fn);
|
||||
bitmap ipa_reference_get_read_global (tree fn);
|
||||
bitmap ipa_reference_get_written_global (tree fn);
|
||||
bitmap ipa_reference_get_not_read_global (tree fn);
|
||||
bitmap ipa_reference_get_not_written_global (tree fn);
|
||||
|
||||
#endif /* GCC_IPA_REFERENCE_H */
|
||||
|
1854
gcc/ipa-type-escape.c
Normal file
1854
gcc/ipa-type-escape.c
Normal file
File diff suppressed because it is too large
Load Diff
33
gcc/ipa-type-escape.h
Normal file
33
gcc/ipa-type-escape.h
Normal file
@ -0,0 +1,33 @@
|
||||
/* Type based alias analysis.
|
||||
Copyright (C) 2004 Free Software Foundation, Inc.
|
||||
Contributed by Kenneth Zadeck <zadeck@naturalbridge.com>
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation; either version 2, or (at your option) any later
|
||||
version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING. If not, write to the Free
|
||||
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA. */
|
||||
|
||||
#ifndef GCC_IPA_TYPE_ESCAPE_H
|
||||
#define GCC_IPA_TYPE_ESCAPE_H
|
||||
#include "tree.h"
|
||||
|
||||
bool ipa_type_escape_type_contained_p (tree type);
|
||||
bool ipa_type_escape_field_does_not_clobber_p (tree record_type, tree field_type);
|
||||
int ipa_type_escape_star_count_of_interesting_type (tree type);
|
||||
int ipa_type_escape_star_count_of_interesting_or_array_type (tree type);
|
||||
|
||||
|
||||
#endif /* GCC_IPA_TYPE_ESCAPE_H */
|
||||
|
228
gcc/ipa-utils.c
Normal file
228
gcc/ipa-utils.c
Normal file
@ -0,0 +1,228 @@
|
||||
/* Utilities for ipa analysis.
|
||||
Copyright (C) 2005 Free Software Foundation, Inc.
|
||||
Contributed by Kenneth Zadeck <zadeck@naturalbridge.com>
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation; either version 2, or (at your option) any later
|
||||
version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING. If not, write to the Free
|
||||
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "tm.h"
|
||||
#include "tree.h"
|
||||
#include "tree-flow.h"
|
||||
#include "tree-inline.h"
|
||||
#include "tree-pass.h"
|
||||
#include "langhooks.h"
|
||||
#include "pointer-set.h"
|
||||
#include "ggc.h"
|
||||
#include "ipa-utils.h"
|
||||
#include "ipa-reference.h"
|
||||
#include "c-common.h"
|
||||
#include "tree-gimple.h"
|
||||
#include "cgraph.h"
|
||||
#include "output.h"
|
||||
#include "flags.h"
|
||||
#include "timevar.h"
|
||||
#include "diagnostic.h"
|
||||
#include "langhooks.h"
|
||||
|
||||
/* Debugging function for postorder and inorder code. NOTE is a string
|
||||
that is printed before the nodes are printed. ORDER is an array of
|
||||
cgraph_nodes that has COUNT useful nodes in it. */
|
||||
|
||||
void
|
||||
ipa_utils_print_order (FILE* out,
|
||||
const char * note,
|
||||
struct cgraph_node** order,
|
||||
int count)
|
||||
{
|
||||
int i;
|
||||
fprintf (out, "\n\n ordered call graph: %s\n", note);
|
||||
|
||||
for (i = count - 1; i >= 0; i--)
|
||||
dump_cgraph_node(dump_file, order[i]);
|
||||
fprintf (out, "\n");
|
||||
fflush(out);
|
||||
}
|
||||
|
||||
|
||||
struct searchc_env {
|
||||
struct cgraph_node **stack;
|
||||
int stack_size;
|
||||
struct cgraph_node **result;
|
||||
int order_pos;
|
||||
splay_tree nodes_marked_new;
|
||||
bool reduce;
|
||||
int count;
|
||||
};
|
||||
|
||||
/* This is an implementation of Tarjan's strongly connected region
|
||||
finder as reprinted in Aho Hopcraft and Ullman's The Design and
|
||||
Analysis of Computer Programs (1975) pages 192-193. This version
|
||||
has been customized for cgraph_nodes. The env parameter is because
|
||||
it is recursive and there are no nested functions here. This
|
||||
function should only be called from itself or
|
||||
cgraph_reduced_inorder. ENV is a stack env and would be
|
||||
unnecessary if C had nested functions. V is the node to start
|
||||
searching from. */
|
||||
|
||||
static void
|
||||
searchc (struct searchc_env* env, struct cgraph_node *v)
|
||||
{
|
||||
struct cgraph_edge *edge;
|
||||
struct ipa_dfs_info *v_info = v->aux;
|
||||
|
||||
/* mark node as old */
|
||||
v_info->new = false;
|
||||
splay_tree_remove (env->nodes_marked_new, v->uid);
|
||||
|
||||
v_info->dfn_number = env->count;
|
||||
v_info->low_link = env->count;
|
||||
env->count++;
|
||||
env->stack[(env->stack_size)++] = v;
|
||||
v_info->on_stack = true;
|
||||
|
||||
for (edge = v->callees; edge; edge = edge->next_callee)
|
||||
{
|
||||
struct ipa_dfs_info * w_info;
|
||||
struct cgraph_node *w = edge->callee;
|
||||
/* Bypass the clones and only look at the master node. Skip
|
||||
external and other bogus nodes. */
|
||||
w = cgraph_master_clone (w);
|
||||
if (w && w->aux)
|
||||
{
|
||||
w_info = w->aux;
|
||||
if (w_info->new)
|
||||
{
|
||||
searchc (env, w);
|
||||
v_info->low_link =
|
||||
(v_info->low_link < w_info->low_link) ?
|
||||
v_info->low_link : w_info->low_link;
|
||||
}
|
||||
else
|
||||
if ((w_info->dfn_number < v_info->dfn_number)
|
||||
&& (w_info->on_stack))
|
||||
v_info->low_link =
|
||||
(w_info->dfn_number < v_info->low_link) ?
|
||||
w_info->dfn_number : v_info->low_link;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (v_info->low_link == v_info->dfn_number)
|
||||
{
|
||||
struct cgraph_node *last = NULL;
|
||||
struct cgraph_node *x;
|
||||
struct ipa_dfs_info *x_info;
|
||||
do {
|
||||
x = env->stack[--(env->stack_size)];
|
||||
x_info = x->aux;
|
||||
x_info->on_stack = false;
|
||||
|
||||
if (env->reduce)
|
||||
{
|
||||
x_info->next_cycle = last;
|
||||
last = x;
|
||||
}
|
||||
else
|
||||
env->result[env->order_pos++] = x;
|
||||
}
|
||||
while (v != x);
|
||||
if (env->reduce)
|
||||
env->result[env->order_pos++] = v;
|
||||
}
|
||||
}
|
||||
|
||||
/* Topsort the call graph by caller relation. Put the result in ORDER.
|
||||
|
||||
The REDUCE flag is true if you want the cycles reduced to single
|
||||
nodes. Only consider nodes that have the output bit set. */
|
||||
|
||||
int
|
||||
ipa_utils_reduced_inorder (struct cgraph_node **order,
|
||||
bool reduce, bool allow_overwritable)
|
||||
{
|
||||
struct cgraph_node *node;
|
||||
struct searchc_env env;
|
||||
splay_tree_node result;
|
||||
env.stack = xcalloc (cgraph_n_nodes, sizeof (struct cgraph_node *));
|
||||
env.stack_size = 0;
|
||||
env.result = order;
|
||||
env.order_pos = 0;
|
||||
env.nodes_marked_new = splay_tree_new (splay_tree_compare_ints, 0, 0);
|
||||
env.count = 1;
|
||||
env.reduce = reduce;
|
||||
|
||||
for (node = cgraph_nodes; node; node = node->next)
|
||||
if ((node->analyzed)
|
||||
&& (cgraph_is_master_clone (node)
|
||||
|| (allow_overwritable
|
||||
&& (cgraph_function_body_availability (node) ==
|
||||
AVAIL_OVERWRITABLE))))
|
||||
{
|
||||
/* Reuse the info if it is already there. */
|
||||
struct ipa_dfs_info *info = node->aux;
|
||||
if (!info)
|
||||
info = xcalloc (1, sizeof (struct ipa_dfs_info));
|
||||
info->new = true;
|
||||
info->on_stack = false;
|
||||
info->next_cycle = NULL;
|
||||
node->aux = info;
|
||||
|
||||
splay_tree_insert (env.nodes_marked_new,
|
||||
(splay_tree_key)node->uid,
|
||||
(splay_tree_value)node);
|
||||
}
|
||||
else
|
||||
node->aux = NULL;
|
||||
result = splay_tree_min (env.nodes_marked_new);
|
||||
while (result)
|
||||
{
|
||||
node = (struct cgraph_node *)result->value;
|
||||
searchc (&env, node);
|
||||
result = splay_tree_min (env.nodes_marked_new);
|
||||
}
|
||||
splay_tree_delete (env.nodes_marked_new);
|
||||
free (env.stack);
|
||||
|
||||
return env.order_pos;
|
||||
}
|
||||
|
||||
|
||||
/* Given a memory reference T, will return the variable at the bottom
|
||||
of the access. Unlike get_base_address, this will recurse thru
|
||||
INDIRECT_REFS. */
|
||||
|
||||
tree
|
||||
get_base_var (tree t)
|
||||
{
|
||||
if ((TREE_CODE (t) == EXC_PTR_EXPR) || (TREE_CODE (t) == FILTER_EXPR))
|
||||
return t;
|
||||
|
||||
while (!SSA_VAR_P (t)
|
||||
&& (!CONSTANT_CLASS_P (t))
|
||||
&& TREE_CODE (t) != LABEL_DECL
|
||||
&& TREE_CODE (t) != FUNCTION_DECL
|
||||
&& TREE_CODE (t) != CONST_DECL)
|
||||
{
|
||||
t = TREE_OPERAND (t, 0);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
49
gcc/ipa-utils.h
Normal file
49
gcc/ipa-utils.h
Normal file
@ -0,0 +1,49 @@
|
||||
/* Utilities for ipa analysis.
|
||||
Copyright (C) 2004-2005 Free Software Foundation, Inc.
|
||||
Contributed by Kenneth Zadeck <zadeck@naturalbridge.com>
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation; either version 2, or (at your option) any later
|
||||
version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING. If not, write to the Free
|
||||
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA. */
|
||||
|
||||
#ifndef GCC_IPA_UTILS_H
|
||||
#define GCC_IPA_UTILS_H
|
||||
#include "tree.h"
|
||||
#include "cgraph.h"
|
||||
|
||||
/* Used for parsing attributes of asm code. */
|
||||
extern tree memory_identifier_string;
|
||||
|
||||
struct ipa_dfs_info {
|
||||
int dfn_number;
|
||||
int low_link;
|
||||
bool new;
|
||||
bool on_stack;
|
||||
struct cgraph_node* next_cycle;
|
||||
PTR aux;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* In ipa-utils.c */
|
||||
void ipa_utils_print_order (FILE*, const char *, struct cgraph_node**, int);
|
||||
int ipa_utils_reduced_inorder (struct cgraph_node **, bool, bool);
|
||||
tree get_base_var (tree);
|
||||
|
||||
|
||||
#endif /* GCC_IPA_UTILS_H */
|
||||
|
||||
|
@ -523,6 +523,8 @@ decode_options (unsigned int argc, const char **argv)
|
||||
flag_loop_optimize = 1;
|
||||
flag_if_conversion = 1;
|
||||
flag_if_conversion2 = 1;
|
||||
flag_ipa_pure_const = 1;
|
||||
flag_ipa_reference = 1;
|
||||
flag_tree_ccp = 1;
|
||||
flag_tree_dce = 1;
|
||||
flag_tree_dom = 1;
|
||||
@ -556,6 +558,7 @@ decode_options (unsigned int argc, const char **argv)
|
||||
flag_cse_skip_blocks = 1;
|
||||
flag_gcse = 1;
|
||||
flag_expensive_optimizations = 1;
|
||||
flag_ipa_type_escape = 1;
|
||||
flag_strength_reduce = 1;
|
||||
flag_rerun_cse_after_loop = 1;
|
||||
flag_rerun_loop_opt = 1;
|
||||
@ -583,6 +586,7 @@ decode_options (unsigned int argc, const char **argv)
|
||||
|
||||
if (optimize >= 3)
|
||||
{
|
||||
flag_tree_promote_statics = 1;
|
||||
flag_inline_functions = 1;
|
||||
flag_unswitch_loops = 1;
|
||||
flag_gcse_after_reload = 1;
|
||||
|
@ -431,6 +431,9 @@ init_optimization_passes (void)
|
||||
NEXT_PASS (pass_early_ipa_inline);
|
||||
NEXT_PASS (pass_early_local_passes);
|
||||
NEXT_PASS (pass_ipa_inline);
|
||||
NEXT_PASS (pass_ipa_reference);
|
||||
NEXT_PASS (pass_ipa_pure_const);
|
||||
NEXT_PASS (pass_ipa_type_escape);
|
||||
*p = NULL;
|
||||
|
||||
/* All passes needed to lower the function into shape optimizers can operate
|
||||
@ -469,6 +472,7 @@ init_optimization_passes (void)
|
||||
|
||||
p = &pass_all_optimizations.sub;
|
||||
NEXT_PASS (pass_referenced_vars);
|
||||
NEXT_PASS (pass_promote_statics);
|
||||
NEXT_PASS (pass_create_structure_vars);
|
||||
NEXT_PASS (pass_build_ssa);
|
||||
NEXT_PASS (pass_may_alias);
|
||||
|
@ -34,13 +34,13 @@ find_base_value (src)
|
||||
}
|
||||
|
||||
|
||||
/* There should be six IF conditionals. */
|
||||
/* { dg-final { scan-tree-dump-times "if " 6 "dom3"} } */
|
||||
/* There should be four IF conditionals. */
|
||||
/* { dg-final { scan-tree-dump-times "if " 4 "dom3"} } */
|
||||
|
||||
/* There should be no casts to short unsigned int. */
|
||||
/* { dg-final { scan-tree-dump-times "\\(short unsigned int\\)" 0 "dom3"} } */
|
||||
|
||||
/* There should be three loads of ->code. */
|
||||
/* { dg-final { scan-tree-dump-times "->code" 3 "dom3"} } */
|
||||
/* There should be two loads of ->code. */
|
||||
/* { dg-final { scan-tree-dump-times "->code" 2 "dom3"} } */
|
||||
|
||||
/* { dg-final { cleanup-tree-dump "dom3" } } */
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O1 -fdump-tree-optimized" } */
|
||||
/* { dg-options "-O3 -fdump-tree-optimized" } */
|
||||
|
||||
/* Test for SRA. */
|
||||
|
||||
@ -22,5 +22,5 @@ copystruct11 (teststruct *param)
|
||||
|
||||
|
||||
/* There should be no reference to link_error. */
|
||||
/* { dg-final { scan-tree-dump-times "link_error" 0 "optimized" { xfail *-*-* } } } */
|
||||
/* { dg-final { scan-tree-dump-times "link_error" 0 "optimized" } } */
|
||||
/* { dg-final { cleanup-tree-dump "optimized" } } */
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O1 -fdump-tree-dce3" } */
|
||||
/* { dg-options "-O2 -fdump-tree-dce3" } */
|
||||
|
||||
/* We should notice constantness of this function. */
|
||||
int t(int a)
|
||||
|
@ -80,7 +80,7 @@ int main (void)
|
||||
|
||||
main1 (a,b,c);
|
||||
main2 (a,b,c);
|
||||
main3 (a,b,c,N);
|
||||
main3 (a,b,c,N-1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -42,6 +42,9 @@ DEFTIMEVAR (TV_DUMP , "dump files")
|
||||
|
||||
DEFTIMEVAR (TV_CGRAPH , "callgraph construction")
|
||||
DEFTIMEVAR (TV_CGRAPHOPT , "callgraph optimization")
|
||||
DEFTIMEVAR (TV_IPA_REFERENCE , "ipa reference")
|
||||
DEFTIMEVAR (TV_IPA_PURE_CONST , "ipa pure const")
|
||||
DEFTIMEVAR (TV_IPA_TYPE_ESCAPE , "ipa type escape")
|
||||
/* Time spent by constructing CFG. */
|
||||
DEFTIMEVAR (TV_CFG , "cfg construction")
|
||||
/* Time spent by cleaning up CFG. */
|
||||
@ -66,6 +69,7 @@ DEFTIMEVAR (TV_TREE_GIMPLIFY , "tree gimplify")
|
||||
DEFTIMEVAR (TV_TREE_EH , "tree eh")
|
||||
DEFTIMEVAR (TV_TREE_CFG , "tree CFG construction")
|
||||
DEFTIMEVAR (TV_TREE_CLEANUP_CFG , "tree CFG cleanup")
|
||||
DEFTIMEVAR (TV_TREE_PROMOTE_STATICS , "tree promote statics")
|
||||
DEFTIMEVAR (TV_TREE_VRP , "tree VRP")
|
||||
DEFTIMEVAR (TV_TREE_COPY_PROP , "tree copy propagation")
|
||||
DEFTIMEVAR (TV_TREE_STORE_COPY_PROP , "tree store copy prop")
|
||||
|
@ -565,6 +565,20 @@ find_vars_r (tree *tp, int *walk_subtrees, void *data)
|
||||
}
|
||||
|
||||
|
||||
/* Lookup UID in the referenced_vars hashtable and return the associated
|
||||
variable or NULL if it is not there. */
|
||||
|
||||
tree
|
||||
referenced_var_lookup_if_exists (unsigned int uid)
|
||||
{
|
||||
struct int_tree_map *h, in;
|
||||
in.uid = uid;
|
||||
h = htab_find_with_hash (referenced_vars, &in, uid);
|
||||
if (h)
|
||||
return h->to;
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Lookup UID in the referenced_vars hashtable and return the associated
|
||||
variable. */
|
||||
|
||||
|
@ -29,6 +29,7 @@ Boston, MA 02110-1301, USA. */
|
||||
#include "tree-gimple.h"
|
||||
#include "tree-ssa-operands.h"
|
||||
#include "cgraph.h"
|
||||
#include "ipa-reference.h"
|
||||
|
||||
/* Forward declare structures for the garbage collector GTY markers. */
|
||||
#ifndef GCC_BASIC_BLOCK_H
|
||||
@ -239,6 +240,11 @@ struct var_ann_d GTY(())
|
||||
current version of this variable (an SSA_NAME). */
|
||||
tree current_def;
|
||||
|
||||
/* Pointer to the structure that contains the sets of global
|
||||
variables modified by function calls. This field is only used
|
||||
for FUNCTION_DECLs. */
|
||||
ipa_reference_vars_info_t GTY ((skip)) reference_vars_info;
|
||||
|
||||
/* If this variable is a structure, this fields holds a list of
|
||||
symbols representing each of the fields of the structure. */
|
||||
subvar_t subvars;
|
||||
@ -392,6 +398,7 @@ typedef struct
|
||||
extern GTY((param_is (struct int_tree_map))) htab_t referenced_vars;
|
||||
|
||||
extern tree referenced_var_lookup (unsigned int);
|
||||
extern tree referenced_var_lookup_if_exists (unsigned int);
|
||||
#define num_referenced_vars htab_elements (referenced_vars)
|
||||
#define referenced_var(i) referenced_var_lookup (i)
|
||||
|
||||
@ -772,6 +779,10 @@ bool is_hidden_global_store (tree);
|
||||
|
||||
/* In tree-sra.c */
|
||||
void insert_edge_copies (tree, basic_block);
|
||||
void sra_insert_before (block_stmt_iterator *, tree);
|
||||
void sra_insert_after (block_stmt_iterator *, tree);
|
||||
void sra_init_cache (void);
|
||||
bool sra_type_can_be_decomposed_p (tree);
|
||||
|
||||
/* In tree-loop-linear.c */
|
||||
extern void linear_transform_loops (struct loops *);
|
||||
|
@ -278,6 +278,7 @@ extern struct tree_opt_pass pass_store_copy_prop;
|
||||
extern struct tree_opt_pass pass_vrp;
|
||||
extern struct tree_opt_pass pass_create_structure_vars;
|
||||
extern struct tree_opt_pass pass_uncprop;
|
||||
extern struct tree_opt_pass pass_promote_statics;
|
||||
extern struct tree_opt_pass pass_return_slot;
|
||||
extern struct tree_opt_pass pass_reassoc;
|
||||
extern struct tree_opt_pass pass_rebuild_cgraph_edges;
|
||||
@ -285,6 +286,9 @@ extern struct tree_opt_pass pass_rebuild_cgraph_edges;
|
||||
/* IPA Passes */
|
||||
extern struct tree_opt_pass pass_ipa_inline;
|
||||
extern struct tree_opt_pass pass_early_ipa_inline;
|
||||
extern struct tree_opt_pass pass_ipa_reference;
|
||||
extern struct tree_opt_pass pass_ipa_pure_const;
|
||||
extern struct tree_opt_pass pass_ipa_type_escape;
|
||||
extern struct tree_opt_pass pass_early_local_passes;
|
||||
|
||||
extern struct tree_opt_pass pass_all_optimizations;
|
||||
|
597
gcc/tree-promote-statics.c
Normal file
597
gcc/tree-promote-statics.c
Normal file
@ -0,0 +1,597 @@
|
||||
/* Promotion of static variables to ssa registers
|
||||
Copyright (C) 2004-2005 Free Software Foundation, Inc.
|
||||
Contributed by Kenneth Zadeck <zadeck@naturalbridge.com>
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "tm.h"
|
||||
#include "tree.h"
|
||||
#include "basic-block.h"
|
||||
#include "tree-flow.h"
|
||||
#include "ipa-utils.h"
|
||||
#include "ipa-reference.h"
|
||||
#include "bitmap.h"
|
||||
#include "tree-pass.h"
|
||||
#include "flags.h"
|
||||
#include "timevar.h"
|
||||
#include "langhooks.h"
|
||||
|
||||
/*
|
||||
The main idea is to promote some static variables from memory to SSA
|
||||
registers. This transformation is only applied to those static
|
||||
variables for which the effects of subroutine calls can be understood.
|
||||
Such infomation is provided by functions in cgraphunit.c.
|
||||
|
||||
The following table shows the actions that are taken to promote
|
||||
variables. The analysis in cgraphunit constructs information about
|
||||
both local usage and the effect of any particular call. Variables are
|
||||
broken into 4 categories: only-read, only-write, read-write, and no
|
||||
information. (No information variables are never promoted.)
|
||||
|
||||
All information is of the "may" variety: if a function is marked read,
|
||||
it means the call may read the variable, but it also may not read the
|
||||
variable.
|
||||
|
||||
There are two possible ways to perform the promotion: assume that the
|
||||
static is live everywhere or compute the minimal live range for the
|
||||
static variable.
|
||||
|
||||
The minimal live range path has a lot of problems:
|
||||
|
||||
1) live variables and upwards exposed uses must be first comuputed.
|
||||
2) new machiney must be invented to prevent code motion algorithms
|
||||
from floating a use of the surrogate register across a register
|
||||
function call that clobbers the variable, but was not in any minimal
|
||||
live range at the time of this analysis.
|
||||
|
||||
While the first problem is simply a lot of code, the second problem
|
||||
requires a new mechanism for pinning code and teaching all passes that
|
||||
can move code to obey this new fenceposts.
|
||||
|
||||
The maximum live range path has the problem that this technique can
|
||||
create many false live ranges where the register is loaded after on
|
||||
call only to be stored back right before the next call. This will eat
|
||||
a certain amount of space and requires special smarts to get rid of them.
|
||||
|
||||
There are really 7 situations to cover in the following table.
|
||||
|
||||
action read write read-write
|
||||
|
||||
-+---------------------------------------------------------------
|
||||
|
||||
entry | load load load
|
||||
|
|
||||
load | getfromreg xxxxx getfromreg
|
||||
|
|
||||
store | xxxx puttoreg puttoreg
|
||||
|
|
||||
call-read | noaction store before store before
|
||||
|
|
||||
call-write | load after store before store before
|
||||
| load after load after
|
||||
call-readwrite| load after store before store before
|
||||
| load after load after
|
||||
|
|
||||
return | no action store store
|
||||
|
||||
|
||||
l-r l-w c-r c-w store-b load-a
|
||||
|
||||
0 0 0 0 | 0 0
|
||||
0 0 0 1 | 0 0
|
||||
0 0 1 0 | 0 0
|
||||
0 0 1 1 | 0 0
|
||||
0 1 0 0 | 0 0
|
||||
0 1 0 1 | 1 1
|
||||
0 1 1 0 | 1 0
|
||||
0 1 1 1 | 1 1
|
||||
1 0 0 0 | 0 0
|
||||
1 0 0 1 | 0 1
|
||||
1 0 1 0 | 0 0
|
||||
1 0 1 1 | 0 1
|
||||
1 1 0 0 | 0 0
|
||||
1 1 0 1 | 1 1
|
||||
1 1 1 0 | 1 0
|
||||
1 1 1 1 | 1 1
|
||||
|
||||
store_before = local_written & (callee_read | callee_written)
|
||||
load_after = (local_read | local_written) & callee_written
|
||||
*/
|
||||
|
||||
static bitmap_obstack promote_obstack;
|
||||
|
||||
/* All of the static variables under consideration by this pass that
|
||||
do reads or writes withing this function. */
|
||||
static bitmap local_read;
|
||||
static bitmap local_written;
|
||||
static bitmap local_all;
|
||||
|
||||
/* Return true if the asm STMT clobbers memory. */
|
||||
|
||||
static bool
|
||||
asm_clobbers_mem (tree stmt)
|
||||
{
|
||||
tree link;
|
||||
for (link = ASM_CLOBBERS (stmt); link; link = TREE_CHAIN (link))
|
||||
if (simple_cst_equal(TREE_VALUE (link), memory_identifier_string) == 1)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return a INPUT_BITMAP for the asm inputs and OUTPUT_BITMAP for the
|
||||
asm outputs of variables written by the asm STMT. */
|
||||
|
||||
static void
|
||||
get_asm_read_and_write (bitmap input_bitmap, bitmap output_bitmap, tree stmt)
|
||||
{
|
||||
int noutputs = list_length (ASM_OUTPUTS (stmt));
|
||||
const char **oconstraints
|
||||
= (const char **) alloca ((noutputs) * sizeof (const char *));
|
||||
int i;
|
||||
tree link;
|
||||
const char *constraint;
|
||||
bool allows_mem, allows_reg, is_inout;
|
||||
|
||||
for (i=0, link = ASM_OUTPUTS (stmt); link; ++i, link = TREE_CHAIN (link))
|
||||
{
|
||||
oconstraints[i] = constraint
|
||||
= TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
|
||||
parse_output_constraint (&constraint, i, 0, 0,
|
||||
&allows_mem, &allows_reg, &is_inout);
|
||||
|
||||
/* The variable is only added to the bitmap if there is an aux
|
||||
field, ie.this is a variable we care about. */
|
||||
if (!allows_reg && allows_mem)
|
||||
{
|
||||
tree var = TREE_VALUE (link);
|
||||
var = get_base_address (var);
|
||||
if (TREE_CODE (var) == VAR_DECL)
|
||||
{
|
||||
var_ann_t va = var_ann (var);
|
||||
if (va && va->common.aux)
|
||||
bitmap_set_bit(output_bitmap, DECL_UID (var));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (link = ASM_INPUTS (stmt); link; link = TREE_CHAIN (link))
|
||||
{
|
||||
constraint
|
||||
= TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
|
||||
parse_input_constraint (&constraint, 0, 0, noutputs, 0,
|
||||
oconstraints, &allows_mem, &allows_reg);
|
||||
|
||||
/* The variable is only added to the bitmap if there is an aux
|
||||
field, ie.this is a variable we care about. */
|
||||
if (!allows_reg && allows_mem)
|
||||
{
|
||||
tree var = TREE_VALUE (link);
|
||||
var = get_base_address (var);
|
||||
if (TREE_CODE (var) == VAR_DECL)
|
||||
{
|
||||
var_ann_t va = var_ann (var);
|
||||
if (va && va->common.aux)
|
||||
bitmap_set_bit(input_bitmap, DECL_UID (var));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate a series of loads from the static variables pointed to by
|
||||
B1 && B2 or just B1 (if B2 is NULL) and insert them after
|
||||
BSI). */
|
||||
|
||||
static void
|
||||
gen_loads (bitmap b1, bitmap b2, block_stmt_iterator *bsi)
|
||||
{
|
||||
bitmap result;
|
||||
bitmap_iterator bi;
|
||||
unsigned int index;
|
||||
tree list = NULL;
|
||||
|
||||
if (b2)
|
||||
{
|
||||
result = BITMAP_ALLOC (&promote_obstack);
|
||||
bitmap_and (result, b1, b2);
|
||||
}
|
||||
else
|
||||
result = b1;
|
||||
|
||||
EXECUTE_IF_SET_IN_BITMAP(result, 0, index, bi)
|
||||
{
|
||||
tree src = referenced_var (index);
|
||||
tree dest = (tree) (var_ann (src)->common.aux);
|
||||
tree stmt = build (MODIFY_EXPR, TREE_TYPE (src), dest, src);
|
||||
append_to_statement_list (stmt, &list);
|
||||
}
|
||||
|
||||
if (list)
|
||||
sra_insert_after (bsi, list);
|
||||
|
||||
if (b2)
|
||||
BITMAP_FREE (result);
|
||||
}
|
||||
|
||||
/* Generate a series of stores to the static variables pointed to by
|
||||
B1 && B2 or just B1 (if B2 is NULL) and insert them before
|
||||
BSI). */
|
||||
|
||||
static void
|
||||
gen_stores (bitmap b1, bitmap b2, block_stmt_iterator *bsi)
|
||||
{
|
||||
bitmap result;
|
||||
bitmap_iterator bi;
|
||||
unsigned int index;
|
||||
tree list = NULL;
|
||||
|
||||
if (b2)
|
||||
{
|
||||
result = BITMAP_ALLOC (&promote_obstack);
|
||||
bitmap_and (result, b1, b2);
|
||||
}
|
||||
else
|
||||
result = b1;
|
||||
|
||||
EXECUTE_IF_SET_IN_BITMAP(result, 0, index, bi)
|
||||
{
|
||||
tree dest = referenced_var (index);
|
||||
tree src = (tree) (var_ann (dest)->common.aux);
|
||||
tree stmt = build (MODIFY_EXPR, TREE_TYPE (src), dest, src);
|
||||
append_to_statement_list (stmt, &list);
|
||||
}
|
||||
|
||||
if (list)
|
||||
sra_insert_before (bsi, list);
|
||||
|
||||
if (b2)
|
||||
BITMAP_FREE (result);
|
||||
}
|
||||
|
||||
/* Replace the static references if it exists in the TPTR. */
|
||||
|
||||
static void
|
||||
try_replace_operand(tree * tptr)
|
||||
{
|
||||
tree t = *tptr;
|
||||
if (TREE_CODE (t) == VAR_DECL)
|
||||
{
|
||||
var_ann_t va = var_ann (t);
|
||||
tree replacement = (tree) (va->common.aux);
|
||||
if (replacement)
|
||||
*tptr = replacement;
|
||||
}
|
||||
}
|
||||
|
||||
/* Walk an expression TPTR replacing all of the static references. */
|
||||
|
||||
static void
|
||||
try_replace (tree *tptr)
|
||||
{
|
||||
tree t = *tptr;
|
||||
if ((TREE_CODE (t) == EXC_PTR_EXPR) || (TREE_CODE (t) == FILTER_EXPR))
|
||||
return;
|
||||
|
||||
/* The INTEGER_CST is because some people use cute things like &0->a
|
||||
for offsetof. */
|
||||
while (t && !SSA_VAR_P (t)
|
||||
&& (!CONSTANT_CLASS_P (t))
|
||||
&& TREE_CODE (t) != LABEL_DECL
|
||||
&& TREE_CODE (t) != CONST_DECL
|
||||
&& TREE_CODE (t) != FUNCTION_DECL
|
||||
&& TREE_CODE (t) != EXC_PTR_EXPR)
|
||||
{
|
||||
if (TREE_CODE (t) == ARRAY_REF)
|
||||
try_replace_operand (&TREE_OPERAND (t, 1));
|
||||
|
||||
tptr = &TREE_OPERAND (t, 0);
|
||||
t = *tptr;
|
||||
}
|
||||
if (t)
|
||||
try_replace_operand (tptr);
|
||||
}
|
||||
|
||||
/* Repalce the static references that exist in a constructor. */
|
||||
|
||||
static void
|
||||
try_replace_constructor (tree ctor)
|
||||
{
|
||||
tree t;
|
||||
for (t = TREE_OPERAND (ctor, 0); t; t = TREE_CHAIN (t))
|
||||
{
|
||||
try_replace (&TREE_VALUE (t));
|
||||
}
|
||||
}
|
||||
|
||||
/* Replace all the static references in the operand list of
|
||||
CALL_EXPR. */
|
||||
|
||||
static void
|
||||
try_replace_call_operands (tree call_expr)
|
||||
{
|
||||
tree operandList = TREE_OPERAND (call_expr, 1);
|
||||
tree operand;
|
||||
|
||||
for (operand = operandList;
|
||||
operand != NULL_TREE;
|
||||
operand = TREE_CHAIN (operand))
|
||||
|
||||
if (TREE_CODE(TREE_VALUE (operand)) != FUNCTION_DECL)
|
||||
try_replace (&TREE_VALUE (operand));
|
||||
}
|
||||
|
||||
/* Generate loads and stores and replace all the static references in
|
||||
function FN using statement iterator SI. This form is used when
|
||||
there is not info available about the caller. */
|
||||
|
||||
static void
|
||||
gen_dumb_call (tree fn, block_stmt_iterator si)
|
||||
{
|
||||
gen_stores (local_written, NULL, &si);
|
||||
try_replace (&TREE_OPERAND (fn, 0));
|
||||
try_replace_call_operands (fn);
|
||||
gen_loads (local_all, NULL, &si);
|
||||
}
|
||||
|
||||
|
||||
/* Generate loads and stores and replace all the static references in
|
||||
function FN using statement iterator SI. */
|
||||
|
||||
static void
|
||||
try_replace_call (tree fn, block_stmt_iterator si)
|
||||
{
|
||||
/* Store intersection of call_read and local_written
|
||||
registers back to memory before calling. */
|
||||
/* int call_flags = call_expr_flags (fn); */
|
||||
tree callee = get_callee_fndecl (fn);
|
||||
if (callee)
|
||||
{
|
||||
bitmap callee_all = BITMAP_ALLOC (&promote_obstack);
|
||||
bitmap callee_written = ipa_reference_get_written_global (callee);
|
||||
if (callee_written)
|
||||
{
|
||||
bitmap_ior (callee_all,
|
||||
ipa_reference_get_read_global (callee),
|
||||
callee_written);
|
||||
|
||||
gen_stores (local_written, callee_all, &si);
|
||||
|
||||
if (TREE_CODE (callee) != FUNCTION_DECL)
|
||||
try_replace (&TREE_OPERAND (fn, 0));
|
||||
try_replace_call_operands (fn);
|
||||
|
||||
/* This is a hack required because the call_flags are set on a
|
||||
function by function basis during compilation. Thus these
|
||||
flags are only set if the callee has already been compiled. */
|
||||
/* if (!(call_flags & (ECF_PURE | ECF_CONST | ECF_NORETURN))) */
|
||||
gen_loads (local_all, callee_written, &si);
|
||||
BITMAP_FREE (callee_all);
|
||||
}
|
||||
else
|
||||
gen_dumb_call (fn, si);
|
||||
}
|
||||
else
|
||||
gen_dumb_call (fn, si);
|
||||
}
|
||||
|
||||
|
||||
/* Walk the entire function looking uses or stores to global variables
|
||||
and changing them to use ssa shadow registers. */
|
||||
|
||||
static void
|
||||
walk_function (void)
|
||||
{
|
||||
basic_block bb;
|
||||
block_stmt_iterator si, ni;
|
||||
|
||||
FOR_EACH_BB (bb)
|
||||
for (si = bsi_start (bb); !bsi_end_p (si); si = ni)
|
||||
{
|
||||
tree stmt = bsi_stmt (si);
|
||||
|
||||
ni = si;
|
||||
bsi_next (&ni);
|
||||
|
||||
switch (TREE_CODE (stmt))
|
||||
{
|
||||
case RETURN_EXPR:
|
||||
/* Store all of the local_written registers back to memory
|
||||
before returning. */
|
||||
gen_stores (local_written, NULL, &si);
|
||||
break;
|
||||
|
||||
case MODIFY_EXPR:
|
||||
/* Change load of static to use of reg. Change store of
|
||||
static to store of reg. */
|
||||
{
|
||||
tree rhs = TREE_OPERAND (stmt, 1);
|
||||
tree *rhsp = &TREE_OPERAND (stmt, 1);
|
||||
tree *lhsp = &TREE_OPERAND (stmt, 0);
|
||||
|
||||
/* If we have a call on the rhs, try to replace the arguments.
|
||||
Otherwise, try to replace the operand on the LHS and the operand on
|
||||
the RHS. */
|
||||
if (TREE_CODE (rhs) == CALL_EXPR)
|
||||
try_replace_call (rhs, si);
|
||||
else if (TREE_CODE (rhs) == CONSTRUCTOR)
|
||||
try_replace_constructor (rhs);
|
||||
else
|
||||
try_replace (rhsp);
|
||||
try_replace (lhsp);
|
||||
}
|
||||
break;
|
||||
case CALL_EXPR:
|
||||
try_replace_call (stmt, si);
|
||||
|
||||
break;
|
||||
case ASM_EXPR:
|
||||
/* If the asm clobbers memory, just store everything and
|
||||
load it back. */
|
||||
if (asm_clobbers_mem (stmt))
|
||||
{
|
||||
gen_stores (local_written, NULL, &si);
|
||||
gen_loads (local_all, NULL, &si);
|
||||
}
|
||||
else
|
||||
{
|
||||
bitmap store_bitmap = BITMAP_ALLOC (&promote_obstack);
|
||||
bitmap load_bitmap = BITMAP_ALLOC (&promote_obstack);
|
||||
bitmap all_bitmap = BITMAP_ALLOC (&promote_obstack);
|
||||
/* The asm read generates a stores before, and the asm
|
||||
write generates loads after. */
|
||||
get_asm_read_and_write (store_bitmap, load_bitmap, stmt);
|
||||
bitmap_ior (all_bitmap, store_bitmap, load_bitmap);
|
||||
|
||||
gen_stores (local_written, all_bitmap , &si);
|
||||
gen_loads (local_all, load_bitmap, &si);
|
||||
|
||||
BITMAP_FREE (store_bitmap);
|
||||
BITMAP_FREE (load_bitmap);
|
||||
BITMAP_FREE (all_bitmap);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Main entry point for the promotion of statics to ssa regsisters. */
|
||||
|
||||
static void
|
||||
execute_promote_statics (void)
|
||||
{
|
||||
unsigned int index;
|
||||
bitmap_iterator bi;
|
||||
bitmap tb = ipa_reference_get_read_local (current_function_decl);
|
||||
|
||||
|
||||
/* There are some options that cause this pass to run even if file
|
||||
at a time is not set. */
|
||||
if (!tb)
|
||||
return;
|
||||
|
||||
bitmap_obstack_initialize (&promote_obstack);
|
||||
sra_init_cache ();
|
||||
|
||||
local_read = BITMAP_ALLOC (&promote_obstack);
|
||||
bitmap_copy (local_read, tb);
|
||||
tb = ipa_reference_get_written_local (current_function_decl);
|
||||
local_written = BITMAP_ALLOC (&promote_obstack);
|
||||
bitmap_copy (local_written, tb);
|
||||
|
||||
local_all = BITMAP_ALLOC (&promote_obstack);
|
||||
tb = BITMAP_ALLOC (&promote_obstack);
|
||||
bitmap_ior (local_all, local_read, local_written);
|
||||
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "promoting in %s\n",
|
||||
lang_hooks.decl_printable_name (current_function_decl, 2));
|
||||
|
||||
EXECUTE_IF_SET_IN_BITMAP (local_all, 0, index, bi)
|
||||
{
|
||||
tree svar = referenced_var_lookup_if_exists (index);
|
||||
if (svar)
|
||||
{
|
||||
tree type = TREE_TYPE (svar);
|
||||
/* We only promote variables that are either scalars or if
|
||||
they are aggregrates, they must be a type that sra is
|
||||
willing to scalarize. Otherwise there is no reason to
|
||||
promote it a register.
|
||||
|
||||
We also do not promote anything that is marked READONLY
|
||||
since there is little gain. The optimizations should
|
||||
generally be able to look thru the operations and find the
|
||||
constants. */
|
||||
if ((!TREE_READONLY(svar))
|
||||
&& (TREE_CODE (type) != ARRAY_TYPE)
|
||||
&& ((!AGGREGATE_TYPE_P (type))
|
||||
|| (sra_type_can_be_decomposed_p (type))))
|
||||
{
|
||||
tree tmp = create_tmp_var (type, get_name (svar));
|
||||
add_referenced_tmp_var (tmp);
|
||||
var_ann (svar)->common.aux = tmp;
|
||||
|
||||
/* Insert loads from all read statics in the entry
|
||||
block. */
|
||||
insert_edge_copies (build (MODIFY_EXPR, TREE_TYPE (svar),
|
||||
tmp, svar),
|
||||
ENTRY_BLOCK_PTR);
|
||||
if (dump_file)
|
||||
fprintf (dump_file, " var=%s, read=%d,write=%d\n",
|
||||
get_name (svar),
|
||||
bitmap_bit_p (local_read, index),
|
||||
bitmap_bit_p (local_written, index));
|
||||
}
|
||||
else
|
||||
/* There is nothing to be done with this variable. */
|
||||
bitmap_set_bit (tb, index);
|
||||
}
|
||||
else
|
||||
/* There is nothing to be done with this variable because the
|
||||
reference was optimized out before we got here. */
|
||||
bitmap_set_bit (tb, index);
|
||||
}
|
||||
|
||||
/* Clear the to be ignored variables from the local maps. */
|
||||
bitmap_and_compl_into (local_read, tb);
|
||||
bitmap_and_compl_into (local_written, tb);
|
||||
bitmap_and_compl_into (local_all, tb);
|
||||
|
||||
walk_function ();
|
||||
bsi_commit_edge_inserts ();
|
||||
|
||||
EXECUTE_IF_SET_IN_BITMAP (local_all, 0, index, bi)
|
||||
{
|
||||
tree svar = referenced_var (index);
|
||||
var_ann (svar)->common.aux = NULL;
|
||||
}
|
||||
|
||||
bitmap_obstack_release (&promote_obstack);
|
||||
}
|
||||
|
||||
static bool
|
||||
gate_promote_statics (void)
|
||||
{
|
||||
return flag_unit_at_a_time != 0
|
||||
&& flag_ipa_reference
|
||||
&& flag_tree_promote_statics;
|
||||
}
|
||||
|
||||
struct tree_opt_pass pass_promote_statics =
|
||||
{
|
||||
"tree-promote-static", /* name */
|
||||
gate_promote_statics, /* gate */
|
||||
execute_promote_statics, /* execute */
|
||||
NULL, /* sub */
|
||||
NULL, /* next */
|
||||
0, /* static_pass_number */
|
||||
TV_TREE_PROMOTE_STATICS, /* tv_id */
|
||||
PROP_cfg, /* properties_required */
|
||||
0, /* properties_provided */
|
||||
0, /* properties_destroyed */
|
||||
0, /* todo_flags_start */
|
||||
TODO_dump_func, /* todo_flags_finish */
|
||||
0 /* letter */
|
||||
};
|
||||
|
||||
|
@ -172,8 +172,8 @@ is_sra_scalar_type (tree type)
|
||||
instantiated, just that if we decide to break up the type into
|
||||
separate pieces that it can be done. */
|
||||
|
||||
static bool
|
||||
type_can_be_decomposed_p (tree type)
|
||||
bool
|
||||
sra_type_can_be_decomposed_p (tree type)
|
||||
{
|
||||
unsigned int cache = TYPE_UID (TYPE_MAIN_VARIANT (type)) * 2;
|
||||
tree t;
|
||||
@ -275,7 +275,7 @@ decl_can_be_decomposed_p (tree var)
|
||||
}
|
||||
|
||||
/* We must be able to decompose the variable's type. */
|
||||
if (!type_can_be_decomposed_p (TREE_TYPE (var)))
|
||||
if (!sra_type_can_be_decomposed_p (TREE_TYPE (var)))
|
||||
{
|
||||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||||
{
|
||||
@ -296,7 +296,7 @@ type_can_instantiate_all_elements (tree type)
|
||||
{
|
||||
if (is_sra_scalar_type (type))
|
||||
return true;
|
||||
if (!type_can_be_decomposed_p (type))
|
||||
if (!sra_type_can_be_decomposed_p (type))
|
||||
return false;
|
||||
|
||||
switch (TREE_CODE (type))
|
||||
@ -1769,7 +1769,7 @@ insert_edge_copies (tree stmt, basic_block bb)
|
||||
|
||||
/* Helper function to insert LIST before BSI, and set up line number info. */
|
||||
|
||||
static void
|
||||
void
|
||||
sra_insert_before (block_stmt_iterator *bsi, tree list)
|
||||
{
|
||||
tree stmt = bsi_stmt (*bsi);
|
||||
@ -1781,7 +1781,7 @@ sra_insert_before (block_stmt_iterator *bsi, tree list)
|
||||
|
||||
/* Similarly, but insert after BSI. Handles insertion onto edges as well. */
|
||||
|
||||
static void
|
||||
void
|
||||
sra_insert_after (block_stmt_iterator *bsi, tree list)
|
||||
{
|
||||
tree stmt = bsi_stmt (*bsi);
|
||||
@ -2138,6 +2138,16 @@ debug_sra_elt_name (struct sra_elt *elt)
|
||||
fputc ('\n', stderr);
|
||||
}
|
||||
|
||||
void
|
||||
sra_init_cache (void)
|
||||
{
|
||||
if (sra_type_decomp_cache)
|
||||
return;
|
||||
|
||||
sra_type_decomp_cache = BITMAP_ALLOC (NULL);
|
||||
sra_type_inst_cache = BITMAP_ALLOC (NULL);
|
||||
}
|
||||
|
||||
/* Main entry point. */
|
||||
|
||||
static void
|
||||
@ -2147,8 +2157,7 @@ tree_sra (void)
|
||||
gcc_obstack_init (&sra_obstack);
|
||||
sra_candidates = BITMAP_ALLOC (NULL);
|
||||
needs_copy_in = BITMAP_ALLOC (NULL);
|
||||
sra_type_decomp_cache = BITMAP_ALLOC (NULL);
|
||||
sra_type_inst_cache = BITMAP_ALLOC (NULL);
|
||||
sra_init_cache ();
|
||||
sra_map = htab_create (101, sra_elt_hash, sra_elt_eq, NULL);
|
||||
|
||||
/* Scan. If we find anything, instantiate and scalarize. */
|
||||
|
@ -43,6 +43,7 @@ Boston, MA 02110-1301, USA. */
|
||||
#include "tree-ssa-structalias.h"
|
||||
#include "convert.h"
|
||||
#include "params.h"
|
||||
#include "ipa-type-escape.h"
|
||||
#include "vec.h"
|
||||
#include "bitmap.h"
|
||||
|
||||
@ -86,6 +87,8 @@ struct alias_stats_d
|
||||
unsigned int simple_resolved;
|
||||
unsigned int tbaa_queries;
|
||||
unsigned int tbaa_resolved;
|
||||
unsigned int structnoaddress_queries;
|
||||
unsigned int structnoaddress_resolved;
|
||||
};
|
||||
|
||||
|
||||
@ -95,7 +98,7 @@ static struct alias_stats_d alias_stats;
|
||||
/* Local functions. */
|
||||
static void compute_flow_insensitive_aliasing (struct alias_info *);
|
||||
static void dump_alias_stats (FILE *);
|
||||
static bool may_alias_p (tree, HOST_WIDE_INT, tree, HOST_WIDE_INT);
|
||||
static bool may_alias_p (tree, HOST_WIDE_INT, tree, HOST_WIDE_INT, bool);
|
||||
static tree create_memory_tag (tree type, bool is_type_tag);
|
||||
static tree get_tmt_for (tree, struct alias_info *);
|
||||
static tree get_nmt_for (tree);
|
||||
@ -346,6 +349,7 @@ count_ptr_derefs (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED, void *data)
|
||||
struct count_ptr_d *count_p = (struct count_ptr_d *) data;
|
||||
|
||||
if (INDIRECT_REF_P (*tp) && TREE_OPERAND (*tp, 0) == count_p->ptr)
|
||||
/* || (TREE_CODE (*tp) == MEM_REF && MEM_REF_SYMBOL (*tp) == count_p->ptr)) */
|
||||
count_p->count++;
|
||||
|
||||
return NULL_TREE;
|
||||
@ -433,7 +437,6 @@ count_uses_and_derefs (tree ptr, tree stmt, unsigned *num_uses_p,
|
||||
gcc_assert (*num_uses_p >= *num_derefs_p);
|
||||
}
|
||||
|
||||
|
||||
/* Initialize the data structures used for alias analysis. */
|
||||
|
||||
static struct alias_info *
|
||||
@ -780,8 +783,8 @@ compute_flow_insensitive_aliasing (struct alias_info *ai)
|
||||
|| bitmap_bit_p (ai->written_vars, DECL_UID (var));
|
||||
if (!tag_stored_p && !var_stored_p)
|
||||
continue;
|
||||
|
||||
if (may_alias_p (p_map->var, p_map->set, var, v_map->set))
|
||||
|
||||
if (may_alias_p (p_map->var, p_map->set, var, v_map->set, false))
|
||||
{
|
||||
subvar_t svars;
|
||||
size_t num_tag_refs, num_var_refs;
|
||||
@ -862,7 +865,7 @@ compute_flow_insensitive_aliasing (struct alias_info *ai)
|
||||
bitmap may_aliases2 = p_map2->may_aliases;
|
||||
|
||||
/* If the pointers may not point to each other, do nothing. */
|
||||
if (!may_alias_p (p_map1->var, p_map1->set, tag2, p_map2->set))
|
||||
if (!may_alias_p (p_map1->var, p_map1->set, tag2, p_map2->set, true))
|
||||
continue;
|
||||
|
||||
/* The two pointers may alias each other. If they already have
|
||||
@ -1453,7 +1456,8 @@ maybe_create_global_var (struct alias_info *ai)
|
||||
|
||||
static bool
|
||||
may_alias_p (tree ptr, HOST_WIDE_INT mem_alias_set,
|
||||
tree var, HOST_WIDE_INT var_alias_set)
|
||||
tree var, HOST_WIDE_INT var_alias_set,
|
||||
bool alias_set_only)
|
||||
{
|
||||
tree mem;
|
||||
var_ann_t m_ann;
|
||||
@ -1520,6 +1524,65 @@ may_alias_p (tree ptr, HOST_WIDE_INT mem_alias_set,
|
||||
alias_stats.tbaa_resolved++;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If var is a record or union type, ptr cannot point into var
|
||||
unless there is some operation explicit address operation in the
|
||||
program that can reference a field of the ptr's dereferenced
|
||||
type. This also assumes that the types of both var and ptr are
|
||||
contained within the compilation unit, and that there is no fancy
|
||||
addressing arithmetic associated with any of the types
|
||||
involved. */
|
||||
|
||||
if ((mem_alias_set != 0) && (var_alias_set != 0))
|
||||
{
|
||||
tree ptr_type = TREE_TYPE (ptr);
|
||||
tree var_type = TREE_TYPE (var);
|
||||
|
||||
/* The star count is -1 if the type at the end of the pointer_to
|
||||
chain is not a record or union type. */
|
||||
if ((!alias_set_only) &&
|
||||
ipa_type_escape_star_count_of_interesting_type (var_type) >= 0)
|
||||
{
|
||||
int ptr_star_count = 0;
|
||||
|
||||
/* Ipa_type_escape_star_count_of_interesting_type is a little to
|
||||
restrictive for the pointer type, need to allow pointers to
|
||||
primitive types as long as those types cannot be pointers
|
||||
to everything. */
|
||||
while (POINTER_TYPE_P (ptr_type))
|
||||
/* Strip the *'s off. */
|
||||
{
|
||||
ptr_type = TREE_TYPE (ptr_type);
|
||||
ptr_star_count++;
|
||||
}
|
||||
|
||||
/* There does not appear to be a better test to see if the
|
||||
pointer type was one of the pointer to everything
|
||||
types. */
|
||||
|
||||
if (ptr_star_count > 0)
|
||||
{
|
||||
alias_stats.structnoaddress_queries++;
|
||||
if (ipa_type_escape_field_does_not_clobber_p (var_type,
|
||||
TREE_TYPE (ptr)))
|
||||
{
|
||||
alias_stats.structnoaddress_resolved++;
|
||||
alias_stats.alias_noalias++;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (ptr_star_count == 0)
|
||||
{
|
||||
/* If ptr_type was not really a pointer to type, it cannot
|
||||
alias. */
|
||||
alias_stats.structnoaddress_queries++;
|
||||
alias_stats.structnoaddress_resolved++;
|
||||
alias_stats.alias_noalias++;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
alias_stats.alias_mayalias++;
|
||||
return true;
|
||||
}
|
||||
@ -1851,6 +1914,10 @@ dump_alias_stats (FILE *file)
|
||||
alias_stats.tbaa_queries);
|
||||
fprintf (file, "Total TBAA resolved:\t%u\n",
|
||||
alias_stats.tbaa_resolved);
|
||||
fprintf (file, "Total non-addressable structure type queries:\t%u\n",
|
||||
alias_stats.structnoaddress_queries);
|
||||
fprintf (file, "Total non-addressable structure type resolved:\t%u\n",
|
||||
alias_stats.structnoaddress_resolved);
|
||||
}
|
||||
|
||||
|
||||
|
@ -33,6 +33,7 @@ Boston, MA 02110-1301, USA. */
|
||||
#include "timevar.h"
|
||||
#include "toplev.h"
|
||||
#include "langhooks.h"
|
||||
#include "ipa-reference.h"
|
||||
|
||||
/* This file contains the code required to manage the operands cache of the
|
||||
SSA optimizer. For every stmt, we maintain an operand cache in the stmt
|
||||
@ -156,7 +157,7 @@ static inline void append_def (tree *);
|
||||
static inline void append_use (tree *);
|
||||
static void append_v_may_def (tree);
|
||||
static void append_v_must_def (tree);
|
||||
static void add_call_clobber_ops (tree);
|
||||
static void add_call_clobber_ops (tree, tree);
|
||||
static void add_call_read_ops (tree);
|
||||
static void add_stmt_operand (tree *, stmt_ann_t, int);
|
||||
static void build_ssa_operands (tree stmt);
|
||||
@ -1727,7 +1728,7 @@ get_call_expr_operands (tree stmt, tree expr)
|
||||
there is no point in recording that. */
|
||||
if (TREE_SIDE_EFFECTS (expr)
|
||||
&& !(call_flags & (ECF_PURE | ECF_CONST | ECF_NORETURN)))
|
||||
add_call_clobber_ops (stmt);
|
||||
add_call_clobber_ops (stmt, get_callee_fndecl (expr));
|
||||
else if (!(call_flags & ECF_CONST))
|
||||
add_call_read_ops (stmt);
|
||||
}
|
||||
@ -1944,7 +1945,7 @@ add_to_addressable_set (tree ref, bitmap *addresses_taken)
|
||||
clobbered variables in the function. */
|
||||
|
||||
static void
|
||||
add_call_clobber_ops (tree stmt)
|
||||
add_call_clobber_ops (tree stmt, tree callee)
|
||||
{
|
||||
int i;
|
||||
unsigned u;
|
||||
@ -1952,6 +1953,7 @@ add_call_clobber_ops (tree stmt)
|
||||
bitmap_iterator bi;
|
||||
stmt_ann_t s_ann = stmt_ann (stmt);
|
||||
struct stmt_ann_d empty_ann;
|
||||
bitmap not_read_b, not_written_b;
|
||||
|
||||
/* Functions that are not const, pure or never return may clobber
|
||||
call-clobbered variables. */
|
||||
@ -1966,8 +1968,22 @@ add_call_clobber_ops (tree stmt)
|
||||
return;
|
||||
}
|
||||
|
||||
/* FIXME - if we have better information from the static vars
|
||||
analysis, we need to make the cache call site specific. This way
|
||||
we can have the performance benefits even if we are doing good
|
||||
optimization. */
|
||||
|
||||
/* Get info for local and module level statics. There is a bit
|
||||
set for each static if the call being processed does not read
|
||||
or write that variable. */
|
||||
|
||||
not_read_b = callee ? ipa_reference_get_not_read_global (callee) : NULL;
|
||||
not_written_b = callee ? ipa_reference_get_not_written_global (callee) : NULL;
|
||||
|
||||
/* If cache is valid, copy the elements into the build vectors. */
|
||||
if (ssa_call_clobbered_cache_valid)
|
||||
if (ssa_call_clobbered_cache_valid
|
||||
&& (!not_read_b || bitmap_empty_p (not_read_b))
|
||||
&& (!not_written_b || bitmap_empty_p (not_written_b)))
|
||||
{
|
||||
/* Process the caches in reverse order so we are always inserting at
|
||||
the head of the list. */
|
||||
@ -2002,43 +2018,62 @@ add_call_clobber_ops (tree stmt)
|
||||
if (unmodifiable_var_p (var))
|
||||
add_stmt_operand (&var, &empty_ann, opf_none);
|
||||
else
|
||||
add_stmt_operand (&var, &empty_ann, opf_is_def | opf_non_specific);
|
||||
{
|
||||
bool not_read
|
||||
= not_read_b ? bitmap_bit_p (not_read_b, u) : false;
|
||||
bool not_written
|
||||
= not_written_b ? bitmap_bit_p (not_written_b, u) : false;
|
||||
|
||||
if ((TREE_READONLY (var)
|
||||
&& (TREE_STATIC (var) || DECL_EXTERNAL (var)))
|
||||
|| not_written)
|
||||
{
|
||||
if (!not_read)
|
||||
add_stmt_operand (&var, &empty_ann, opf_none);
|
||||
}
|
||||
else
|
||||
add_stmt_operand (&var, &empty_ann, opf_is_def);
|
||||
}
|
||||
}
|
||||
|
||||
clobbered_aliased_loads = empty_ann.makes_aliased_loads;
|
||||
clobbered_aliased_stores = empty_ann.makes_aliased_stores;
|
||||
|
||||
/* Set the flags for a stmt's annotation. */
|
||||
if (s_ann)
|
||||
if ((!not_read_b || bitmap_empty_p (not_read_b))
|
||||
&& (!not_written_b || bitmap_empty_p (not_written_b)))
|
||||
{
|
||||
s_ann->makes_aliased_loads = empty_ann.makes_aliased_loads;
|
||||
s_ann->makes_aliased_stores = empty_ann.makes_aliased_stores;
|
||||
clobbered_aliased_loads = empty_ann.makes_aliased_loads;
|
||||
clobbered_aliased_stores = empty_ann.makes_aliased_stores;
|
||||
|
||||
/* Set the flags for a stmt's annotation. */
|
||||
if (s_ann)
|
||||
{
|
||||
s_ann->makes_aliased_loads = empty_ann.makes_aliased_loads;
|
||||
s_ann->makes_aliased_stores = empty_ann.makes_aliased_stores;
|
||||
}
|
||||
|
||||
/* Prepare empty cache vectors. */
|
||||
VEC_truncate (tree, clobbered_vuses, 0);
|
||||
VEC_truncate (tree, clobbered_v_may_defs, 0);
|
||||
|
||||
/* Now fill the clobbered cache with the values that have been found. */
|
||||
for (i = opbuild_first (&build_vuses);
|
||||
i != OPBUILD_LAST;
|
||||
i = opbuild_next (&build_vuses, i))
|
||||
VEC_safe_push (tree, heap, clobbered_vuses,
|
||||
opbuild_elem_virtual (&build_vuses, i));
|
||||
|
||||
gcc_assert (opbuild_num_elems (&build_vuses)
|
||||
== VEC_length (tree, clobbered_vuses));
|
||||
|
||||
for (i = opbuild_first (&build_v_may_defs);
|
||||
i != OPBUILD_LAST;
|
||||
i = opbuild_next (&build_v_may_defs, i))
|
||||
VEC_safe_push (tree, heap, clobbered_v_may_defs,
|
||||
opbuild_elem_virtual (&build_v_may_defs, i));
|
||||
|
||||
gcc_assert (opbuild_num_elems (&build_v_may_defs)
|
||||
== VEC_length (tree, clobbered_v_may_defs));
|
||||
|
||||
ssa_call_clobbered_cache_valid = true;
|
||||
}
|
||||
|
||||
/* Prepare empty cache vectors. */
|
||||
VEC_truncate (tree, clobbered_vuses, 0);
|
||||
VEC_truncate (tree, clobbered_v_may_defs, 0);
|
||||
|
||||
/* Now fill the clobbered cache with the values that have been found. */
|
||||
for (i = opbuild_first (&build_vuses);
|
||||
i != OPBUILD_LAST;
|
||||
i = opbuild_next (&build_vuses, i))
|
||||
VEC_safe_push (tree, heap, clobbered_vuses,
|
||||
opbuild_elem_virtual (&build_vuses, i));
|
||||
|
||||
gcc_assert (opbuild_num_elems (&build_vuses)
|
||||
== VEC_length (tree, clobbered_vuses));
|
||||
|
||||
for (i = opbuild_first (&build_v_may_defs);
|
||||
i != OPBUILD_LAST;
|
||||
i = opbuild_next (&build_v_may_defs, i))
|
||||
VEC_safe_push (tree, heap, clobbered_v_may_defs,
|
||||
opbuild_elem_virtual (&build_v_may_defs, i));
|
||||
|
||||
gcc_assert (opbuild_num_elems (&build_v_may_defs)
|
||||
== VEC_length (tree, clobbered_v_may_defs));
|
||||
|
||||
ssa_call_clobbered_cache_valid = true;
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user