* Makefile.in (tree-ssa-sink.o): New.
(OBJS-common): Add tree-ssa-sink.o.
* common.opt: Add -ftree-sink
* opts.c (decode_options): flag_tree_sink is set at O1 or higher.
* timevar.def (TV_TREE_SINK): new timevar.
* tree-flow.h (is_hidden_global_store): Prototype.
* tree-optimize.c (init_tree_optimization_passes): Add
pass_sink_code.
* tree-pass.h (pass_sink_code): New.
* tree-ssa-dce.c (mark_stmt_if_obviously_necessary): Move checking
for non-obvious global store store to is_hidden_global_store, and
call that new function.
* tree-ssa-sink.c: New file.
* doc/invoke.texi: Document -fdump-tree-sink and -ftree-sink.
* doc/passes.texi: Document forward store motion.
* testsuite/gcc.dg/tree-ssa/ssa-sink-1.c: New test
* testsuite/gcc.dg/tree-ssa/ssa-sink-2.c: New test
* testsuite/gcc.dg/tree-ssa/ssa-sink-3.c: New test
* testsuite/gcc.dg/tree-ssa/ssa-sink-4.c: New test
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@95750
138bc75d-0d04-0410-961f-
82ee72b054a4
+2005-03-01 Daniel Berlin <dberlin@dberlin.org>
+
+ * Makefile.in (tree-ssa-sink.o): New.
+ (OBJS-common): Add tree-ssa-sink.o.
+ * common.opt: Add -ftree-sink
+ * opts.c (decode_options): flag_tree_sink is set at O1 or higher.
+ * timevar.def (TV_TREE_SINK): new timevar.
+ * tree-flow.h (is_hidden_global_store): Prototype.
+ * tree-optimize.c (init_tree_optimization_passes): Add
+ pass_sink_code.
+ * tree-pass.h (pass_sink_code): New.
+ * tree-ssa-dce.c (mark_stmt_if_obviously_necessary): Move checking
+ for non-obvious global store store to is_hidden_global_store, and
+ call that new function.
+ * tree-ssa-sink.c: New file.
+ * doc/invoke.texi: Document -fdump-tree-sink and -ftree-sink.
+ * doc/passes.texi: Document forward store motion.
+ * testsuite/gcc.dg/tree-ssa/ssa-sink-1.c: New test
+ * testsuite/gcc.dg/tree-ssa/ssa-sink-2.c: New test
+ * testsuite/gcc.dg/tree-ssa/ssa-sink-3.c: New test
+ * testsuite/gcc.dg/tree-ssa/ssa-sink-4.c: New test
+
2005-03-01 Per Bothner <per@bothner.com>
* diagnostic.c (diagnostic_build_prefix): If USE_MAPPED_LOCATION
varasm.o varray.o vec.o version.o vmsdbgout.o xcoffout.o alloc-pool.o \
et-forest.o cfghooks.o bt-load.o pretty-print.o $(GGC) web.o passes.o \
rtl-profile.o tree-profile.o rtlhooks.o cfgexpand.o lambda-mat.o \
- lambda-trans.o lambda-code.o tree-loop-linear.o
+ lambda-trans.o lambda-code.o tree-loop-linear.o tree-ssa-sink.o
OBJS-md = $(out_object_file)
OBJS-archive = $(EXTRA_OBJS) $(host_hook_obj) tree-inline.o \
tree-tailcall.o : tree-tailcall.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \
$(RTL_H) $(TREE_H) $(TM_P_H) function.h $(TM_H) coretypes.h \
$(TREE_DUMP_H) diagnostic.h except.h tree-pass.h $(FLAGS_H) langhooks.h
+tree-ssa-sink.o : tree-ssa-sink.c $(TREE_FLOW_H) $(CONFIG_H) \
+ $(SYSTEM_H) $(TREE_H) $(TM_P_H) $(EXPR_H) \
+ $(GGC_H) output.h diagnostic.h errors.h toplev.h $(TIMEVAR_H) \
+ $(TM_H) coretypes.h $(TREE_DUMP_H) tree-pass.h $(FLAGS_H)
tree-nested.o: tree-nested.c $(CONFIG_H) $(SYSTEM_H) $(TM_H) $(TREE_H) \
$(RTL_H) $(TM_P_H) function.h tree-dump.h tree-inline.h tree-iterator.h \
tree-gimple.h $(CGRAPH_H) $(EXPR_H) langhooks.h $(GGC_H) gt-tree-nested.h
Common Report Var(flag_tree_pre)
Enable SSA-PRE optimization on trees
+ftree-sink
+Common Report Var(flag_tree_sink)
+Enable SSA code sinking on trees
+
ftree-sra
Common Report Var(flag_tree_sra)
Perform scalar replacement of aggregates
-fdump-tree-forwprop@r{[}-@var{n}@r{]} @gol
-fdump-tree-copyrename@r{[}-@var{n}@r{]} @gol
-fdump-tree-nrv -fdump-tree-vect @gol
+-fdump-tree-sink @gol
-fdump-tree-sra@r{[}-@var{n}@r{]} @gol
-fdump-tree-fre@r{[}-@var{n}@r{]} @gol
-ftree-vectorizer-verbose=@var{n} @gol
-fvariable-expansion-in-unroller @gol
-ftree-pre -ftree-ccp -ftree-dce -ftree-loop-optimize @gol
-ftree-loop-linear -ftree-loop-im -ftree-loop-ivcanon -fivopts @gol
--ftree-dominator-opts -ftree-dse -ftree-copyrename @gol
+-ftree-dominator-opts -ftree-dse -ftree-copyrename -ftree-sink @gol
-ftree-ch -ftree-sra -ftree-ter -ftree-lrs -ftree-fre -ftree-vectorize @gol
--param @var{name}=@var{value}
-O -O0 -O1 -O2 -O3 -Os}
Dump each function after performing scalar replacement of aggregates. The
file name is made by appending @file{.sra} to the source file name.
+@item sink
+@opindex fdump-tree-sink
+Dump each function after performing code sinking. The file name is made
+by appending @file{.sink} to the source file name.
+
@item dom
@opindex fdump-tree-dom
Dump each function after applying dominator tree optimizations. The file
This analysis faster than PRE, though it exposes fewer redundancies.
This flag is enabled by default at @option{-O} and higher.
+@item -ftree-sink
+Perform forward store motion on trees. This flag is
+enabled by default at @option{-O} and higher.
+
@item -ftree-ccp
Perform sparse conditional constant propagation (CCP) on trees. This flag
is enabled by default at @option{-O} and higher.
This pass transforms tail recursion into a loop. It is located in
@file{tree-tailcall.c} and is described by @code{pass_tail_recursion}.
+@item Forward store motion
+
+This pass sinks stores and assignments down the flowgraph closer to it's
+use point. The pass is located in @file{tree-ssa-sink.c} and is
+described by @code{pass_sink_code}
+
@item Partial redundancy elimination
This pass eliminates partially redundant computations, as well as
flag_tree_sra = 1;
flag_tree_copyrename = 1;
flag_tree_fre = 1;
+ flag_tree_sink = 1;
if (!optimize_size)
{
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-sink-stats" } */
+int
+foo (int a, int b, int c)
+{
+ int x = a * b;
+ return c ? x : a;
+}
+/* We should sink the x = a * b calculation into the branch that returns x. */
+/* { dg-final { scan-tree-dump-times "Sunk statements:1" 1 "sink"} } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-sink-stats" } */
+int
+bar (int a, int b, int c)
+{
+ int y = a * b;
+ if (c)
+ y = 12;
+ return y;
+}
+/* We should sink the x = a * b calculation into the else branch */
+/* { dg-final { scan-tree-dump-times "Sunk statements:1" 1 "sink"} } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-sink-stats" } */
+extern void foo(int a);
+int
+main (int argc)
+{
+ int a;
+ a = argc + 1;
+ if (argc + 3)
+ {
+ foo (a);
+ }
+}
+/* We should sink the a = argc + 1 calculation into the if branch */
+/* { dg-final { scan-tree-dump-times "Sunk statements:1" 1 "sink"} } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-sink-stats" } */
+extern int foo (int *, int *);
+extern int foo2 (int);
+int
+main (int argc)
+{
+ int a, b, c;
+ b = argc + 1;
+ c = argc + 2;
+ a = b + c;
+ if (argc)
+ {
+ foo (&b, &c);
+ a = b + c;
+ }
+ foo2 (a);
+}
+/* We should sink the first a = b + c calculation into the else branch */
+/* { dg-final { scan-tree-dump-times "Sunk statements:1" 1 "sink"} } */
DEFTIMEVAR (TV_TREE_PRE , "tree PRE")
DEFTIMEVAR (TV_TREE_REDPHI , "tree remove redundant PHIs")
DEFTIMEVAR (TV_TREE_FRE , "tree FRE")
+DEFTIMEVAR (TV_TREE_SINK , "tree code sinking")
DEFTIMEVAR (TV_TREE_PHIOPT , "tree linearize phis")
DEFTIMEVAR (TV_TREE_FORWPROP , "tree forward propagate")
DEFTIMEVAR (TV_TREE_DCE , "tree conservative DCE")
void vn_init (void);
void vn_delete (void);
+/* In tree-ssa-sink.c */
+bool is_hidden_global_store (tree);
/* In tree-sra.c */
void insert_edge_copies (tree, basic_block);
NEXT_PASS (pass_may_alias);
NEXT_PASS (pass_split_crit_edges);
NEXT_PASS (pass_pre);
+ NEXT_PASS (pass_sink_code);
NEXT_PASS (pass_loop);
NEXT_PASS (pass_dominator);
NEXT_PASS (pass_redundant_phi);
extern struct tree_opt_pass pass_rename_ssa_copies;
extern struct tree_opt_pass pass_expand;
extern struct tree_opt_pass pass_rest_of_compilation;
+extern struct tree_opt_pass pass_sink_code;
extern struct tree_opt_pass pass_fre;
extern struct tree_opt_pass pass_linear_transform;
static void
mark_stmt_if_obviously_necessary (tree stmt, bool aggressive)
{
- v_may_def_optype v_may_defs;
- v_must_def_optype v_must_defs;
stmt_ann_t ann;
tree op, def;
ssa_op_iter iter;
return;
}
}
-
- /* Check virtual definitions. If we get here, the only virtual
- definitions we should see are those generated by assignment
- statements. */
- v_may_defs = V_MAY_DEF_OPS (ann);
- v_must_defs = V_MUST_DEF_OPS (ann);
- if (NUM_V_MAY_DEFS (v_may_defs) > 0 || NUM_V_MUST_DEFS (v_must_defs) > 0)
+ if (is_hidden_global_store (stmt))
{
- tree lhs;
-
- gcc_assert (TREE_CODE (stmt) == MODIFY_EXPR);
-
- /* Note that we must not check the individual virtual operands
- here. In particular, if this is an aliased store, we could
- end up with something like the following (SSA notation
- redacted for brevity):
-
- foo (int *p, int i)
- {
- int x;
- p_1 = (i_2 > 3) ? &x : p_1;
-
- # x_4 = V_MAY_DEF <x_3>
- *p_1 = 5;
-
- return 2;
- }
-
- Notice that the store to '*p_1' should be preserved, if we
- were to check the virtual definitions in that store, we would
- not mark it needed. This is because 'x' is not a global
- variable.
-
- Therefore, we check the base address of the LHS. If the
- address is a pointer, we check if its name tag or type tag is
- a global variable. Otherwise, we check if the base variable
- is a global. */
- lhs = TREE_OPERAND (stmt, 0);
- if (REFERENCE_CLASS_P (lhs))
- lhs = get_base_address (lhs);
-
- if (lhs == NULL_TREE)
- {
- /* If LHS is NULL, it means that we couldn't get the base
- address of the reference. In which case, we should not
- remove this store. */
- mark_stmt_necessary (stmt, true);
- }
- else if (DECL_P (lhs))
- {
- /* If the store is to a global symbol, we need to keep it. */
- if (is_global_var (lhs))
- mark_stmt_necessary (stmt, true);
- }
- else if (INDIRECT_REF_P (lhs))
- {
- tree ptr = TREE_OPERAND (lhs, 0);
- struct ptr_info_def *pi = SSA_NAME_PTR_INFO (ptr);
- tree nmt = (pi) ? pi->name_mem_tag : NULL_TREE;
- tree tmt = var_ann (SSA_NAME_VAR (ptr))->type_mem_tag;
-
- /* If either the name tag or the type tag for PTR is a
- global variable, then the store is necessary. */
- if ((nmt && is_global_var (nmt))
- || (tmt && is_global_var (tmt)))
- {
- mark_stmt_necessary (stmt, true);
- return;
- }
- }
- else
- gcc_unreachable ();
+ mark_stmt_necessary (stmt, true);
+ return;
}
return;