diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 531f6888e02..5a6d033e06a 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,39 @@ +2007-01-28 Zdenek Dvorak + + * tree-ssa-loop-unswitch.c: Include tree-inline.h. + (tree_unswitch_single_loop): Pass eni_size_weights to + tree_num_loop_insns. + * tree-ssa-loop-manip.c: Include tree-inline.h. + (can_unroll_loop_p): Pass eni_size_weights to + tree_num_loop_insns. + * tree-ssa-loop-ch.c (should_duplicate_loop_header_p): + Pass eni_size_weights to estimate_num_insns. + * tree.h (init_inline_once): Export. + * toplev.c (backend_init): Call init_inline_once. + * cgraphunit.c (cgraph_process_new_functions, + cgraph_analyze_function): Pass eni_inlining_weights to + estimate_num_insns. + * ipa-inline.c (compute_inline_parameters): Ditto. + * tree-ssa-loop-ivcanon.c (tree_num_loop_insns): Pass weights + to estimate_num_insns. + (try_unroll_loop_completely): Pass eni_size_weights to + tree_num_loop_insns. + * tree-eh.c (decide_copy_try_finally): Pass eni_size_weights + ot estimate_num_insns. + * tree-ssa-loop-prefetch.c: Include tree-inline.h. + (loop_prefetch_arrays): Pass eni_time_weights to tree_num_loop_insns. + * tree-inline.c (eni_inlining_weights, eni_size_weights, + eni_time_weights): New variables. + (init_inline_once): Initialize them. + (struct eni_data): Mew. + (estimate_num_insns_1, estimate_num_insns): Use weights. + * tree-inline.h (struct eni_weights_d): New. + (eni_inlining_weights, eni_size_weights, eni_time_weights): Declare. + (estimate_num_insns): Declaration changed. + * cfgloop.h (tree_num_loop_insns): Declaration changed. + * Makefile.in (tree-ssa-loop-unswitch.o, tree-ssa-loop-prefetch.o, + tree-ssa-loop-manip.o): Add TREE_INLINE_H dependency. + 2007-01-28 Zdenek Dvorak * tree-data-ref.c (conflict_fn): Assert that the number of affine diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 1bde78373fa..01d190da44c 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -2107,7 +2107,8 @@ tree-ssa-loop.o : tree-ssa-loop.c $(TREE_FLOW_H) $(CONFIG_H) \ tree-ssa-loop-unswitch.o : tree-ssa-loop-unswitch.c $(TREE_FLOW_H) \ $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) $(TM_P_H) $(CFGLOOP_H) \ domwalk.h $(PARAMS_H) output.h $(DIAGNOSTIC_H) $(TIMEVAR_H) $(TM_H) \ - coretypes.h $(TREE_DUMP_H) tree-pass.h $(BASIC_BLOCK_H) hard-reg-set.h + coretypes.h $(TREE_DUMP_H) tree-pass.h $(BASIC_BLOCK_H) hard-reg-set.h \ + $(TREE_INLINE_H) tree-ssa-address.o : tree-ssa-address.c $(TREE_FLOW_H) $(CONFIG_H) \ $(SYSTEM_H) $(RTL_H) $(TREE_H) $(TM_P_H) \ output.h $(DIAGNOSTIC_H) $(TIMEVAR_H) $(TM_H) coretypes.h $(TREE_DUMP_H) \ @@ -2132,7 +2133,7 @@ tree-ssa-loop-prefetch.o: tree-ssa-loop-prefetch.c $(TREE_FLOW_H) $(CONFIG_H) \ output.h $(DIAGNOSTIC_H) $(TIMEVAR_H) $(TM_H) coretypes.h $(TREE_DUMP_H) \ tree-pass.h $(GGC_H) $(RECOG_H) insn-config.h $(HASHTAB_H) $(SCEV_H) \ $(CFGLOOP_H) $(PARAMS_H) langhooks.h $(BASIC_BLOCK_H) hard-reg-set.h \ - tree-chrec.h toplev.h langhooks.h + tree-chrec.h toplev.h langhooks.h $(TREE_INLINE_H) tree-ssa-loop-ivopts.o : tree-ssa-loop-ivopts.c $(TREE_FLOW_H) $(CONFIG_H) \ $(SYSTEM_H) $(RTL_H) $(TREE_H) $(TM_P_H) $(CFGLOOP_H) $(EXPR_H) \ output.h $(DIAGNOSTIC_H) $(TIMEVAR_H) $(TM_H) coretypes.h $(TREE_DUMP_H) \ @@ -2146,7 +2147,7 @@ tree-ssa-loop-manip.o : tree-ssa-loop-manip.c $(TREE_FLOW_H) $(CONFIG_H) \ $(SYSTEM_H) $(RTL_H) $(TREE_H) $(TM_P_H) $(CFGLOOP_H) \ output.h $(DIAGNOSTIC_H) $(TIMEVAR_H) $(TM_H) coretypes.h $(TREE_DUMP_H) \ tree-pass.h $(CFGLAYOUT_H) $(SCEV_H) $(BASIC_BLOCK_H) hard-reg-set.h \ - $(PARAMS_H) + $(PARAMS_H) $(TREE_INLINE_H) tree-ssa-loop-im.o : tree-ssa-loop-im.c $(TREE_FLOW_H) $(CONFIG_H) \ $(SYSTEM_H) $(RTL_H) $(TREE_H) $(TM_P_H) $(CFGLOOP_H) domwalk.h \ $(PARAMS_H) output.h $(DIAGNOSTIC_H) $(TIMEVAR_H) $(TM_H) coretypes.h \ diff --git a/gcc/cfgloop.h b/gcc/cfgloop.h index 47bfa576103..0e1b13a94f4 100644 --- a/gcc/cfgloop.h +++ b/gcc/cfgloop.h @@ -218,7 +218,8 @@ extern bool flow_loop_nested_p (const struct loop *, const struct loop *); extern bool flow_bb_inside_loop_p (const struct loop *, const basic_block); extern struct loop * find_common_loop (struct loop *, struct loop *); struct loop *superloop_at_depth (struct loop *, unsigned); -extern unsigned tree_num_loop_insns (struct loop *); +struct eni_weights_d; +extern unsigned tree_num_loop_insns (struct loop *, struct eni_weights_d *); extern int num_loop_insns (struct loop *); extern int average_num_loop_insns (struct loop *); extern unsigned get_loop_level (const struct loop *); diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c index 055399e8330..c4cf9e92580 100644 --- a/gcc/cgraphunit.c +++ b/gcc/cgraphunit.c @@ -297,7 +297,8 @@ cgraph_process_new_functions (void) push_cfun (DECL_STRUCT_FUNCTION (fndecl)); current_function_decl = fndecl; node->local.inlinable = tree_inlinable_function_p (fndecl); - node->local.self_insns = estimate_num_insns (fndecl); + node->local.self_insns = estimate_num_insns (fndecl, + &eni_inlining_weights); node->local.disregard_inline_limits = lang_hooks.tree_inlining.disregard_inline_limits (fndecl); /* Inlining characteristics are maintained by the @@ -677,7 +678,7 @@ cgraph_analyze_function (struct cgraph_node *node) node->global.stack_frame_offset = 0; node->local.inlinable = tree_inlinable_function_p (decl); if (!flag_unit_at_a_time) - node->local.self_insns = estimate_num_insns (decl); + node->local.self_insns = estimate_num_insns (decl, &eni_inlining_weights); if (node->local.inlinable) node->local.disregard_inline_limits = lang_hooks.tree_inlining.disregard_inline_limits (decl); diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c index 5ddbbefe1f0..7640fb79501 100644 --- a/gcc/ipa-inline.c +++ b/gcc/ipa-inline.c @@ -1379,7 +1379,8 @@ compute_inline_parameters (void) node->global.estimated_stack_size = node->local.estimated_self_stack_size; node->global.stack_frame_offset = 0; node->local.inlinable = tree_inlinable_function_p (current_function_decl); - node->local.self_insns = estimate_num_insns (current_function_decl); + node->local.self_insns = estimate_num_insns (current_function_decl, + &eni_inlining_weights); if (node->local.inlinable) node->local.disregard_inline_limits = lang_hooks.tree_inlining.disregard_inline_limits (current_function_decl); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index c5769557ae3..62634656b17 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2007-01-28 Zdenek Dvorak + + * gcc.dg/tree-ssa/loop-23.c: New test. + 2007-01-28 Thomas Koenig PR libfortran/30389 diff --git a/gcc/testsuite/gcc.dg/tree-ssa/loop-23.c b/gcc/testsuite/gcc.dg/tree-ssa/loop-23.c new file mode 100644 index 00000000000..a16dc5f0357 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/loop-23.c @@ -0,0 +1,26 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -funroll-loops -fdump-tree-cunroll-details" } */ + +void bla(int); + +void foo(void) +{ + int i; + + /* This loop used to appear to be too large for unrolling. */ + for (i = 0; i < 4; i++) + { + bla (i); + bla (2*i); + bla (3*i); + bla (4*i); + bla (5*i); + bla (6*i); + bla (7*i); + bla (8*i); + } +} + +/* { dg-final { scan-tree-dump-times "Unrolled loop 1 completely" 1 "cunroll" } } */ + +/* { dg-final { cleanup-tree-dump "cunroll" } } */ diff --git a/gcc/toplev.c b/gcc/toplev.c index 569dbfdc747..cf95f02c94e 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -1949,6 +1949,7 @@ backend_init (void) init_regs (); init_fake_stack_mems (); init_alias_once (); + init_inline_once (); init_reload (); init_varasm_once (); diff --git a/gcc/tree-eh.c b/gcc/tree-eh.c index c0590a5c4e5..cfd99bcd7ba 100644 --- a/gcc/tree-eh.c +++ b/gcc/tree-eh.c @@ -1308,7 +1308,7 @@ decide_copy_try_finally (int ndests, tree finally) return false; /* Finally estimate N times, plus N gotos. */ - f_estimate = estimate_num_insns (finally); + f_estimate = estimate_num_insns (finally, &eni_size_weights); f_estimate = (f_estimate + 1) * ndests; /* Switch statement (cost 10), N variable assignments, N gotos. */ diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index 813f18db307..d68190fa899 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -107,6 +107,21 @@ int flag_inline_trees = 0; o Provide heuristics to clamp inlining of recursive template calls? */ + +/* Weights that estimate_num_insns uses for heuristics in inlining. */ + +eni_weights eni_inlining_weights; + +/* Weights that estimate_num_insns uses to estimate the size of the + produced code. */ + +eni_weights eni_size_weights; + +/* Weights that estimate_num_insns uses to estimate the time necessary + to execute the produced code. */ + +eni_weights eni_time_weights; + /* Prototypes. */ static tree declare_return_variable (copy_body_data *, tree, tree, tree *); @@ -1904,14 +1919,26 @@ estimate_move_cost (tree type) return ((size + MOVE_MAX_PIECES - 1) / MOVE_MAX_PIECES); } +/* Arguments for estimate_num_insns_1. */ + +struct eni_data +{ + /* Used to return the number of insns. */ + int count; + + /* Weights of various constructs. */ + eni_weights *weights; +}; + /* Used by estimate_num_insns. Estimate number of instructions seen by given statement. */ static tree estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data) { - int *count = (int *) data; + struct eni_data *d = data; tree x = *tp; + unsigned cost; if (IS_TYPE_OR_DECL_P (x)) { @@ -2026,7 +2053,7 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data) /* Otherwise it's a store, so fall through to compute the move cost. */ case CONSTRUCTOR: - *count += estimate_move_cost (TREE_TYPE (x)); + d->count += estimate_move_cost (TREE_TYPE (x)); break; /* Assign cost of 1 to usual operations. @@ -2090,8 +2117,6 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data) case POSTDECREMENT_EXPR: case POSTINCREMENT_EXPR: - case SWITCH_EXPR: - case ASM_EXPR: case REALIGN_LOAD_EXPR: @@ -2116,7 +2141,13 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data) case VEC_INTERLEAVE_LOW_EXPR: case RESX_EXPR: - *count += 1; + d->count += 1; + break; + + case SWITCH_EXPR: + /* TODO: Cost of a switch should be derived from the number of + branches. */ + d->count += d->weights->switch_cost; break; /* Few special cases of expensive operations. This is useful @@ -2131,13 +2162,14 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data) case FLOOR_MOD_EXPR: case ROUND_MOD_EXPR: case RDIV_EXPR: - *count += 10; + d->count += d->weights->div_mod_cost; break; case CALL_EXPR: { tree decl = get_callee_fndecl (x); tree arg; + cost = d->weights->call_cost; if (decl && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL) switch (DECL_FUNCTION_CODE (decl)) { @@ -2146,6 +2178,10 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data) return NULL_TREE; case BUILT_IN_EXPECT: return NULL_TREE; + /* Prefetch instruction is not expensive. */ + case BUILT_IN_PREFETCH: + cost = 1; + break; default: break; } @@ -2155,15 +2191,15 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data) if (!decl) { for (arg = TREE_OPERAND (x, 1); arg; arg = TREE_CHAIN (arg)) - *count += estimate_move_cost (TREE_TYPE (TREE_VALUE (arg))); + d->count += estimate_move_cost (TREE_TYPE (TREE_VALUE (arg))); } else { for (arg = DECL_ARGUMENTS (decl); arg; arg = TREE_CHAIN (arg)) - *count += estimate_move_cost (TREE_TYPE (arg)); + d->count += estimate_move_cost (TREE_TYPE (arg)); } - *count += PARAM_VALUE (PARAM_INLINE_CALL_COST); + d->count += cost; break; } @@ -2177,7 +2213,7 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data) case OMP_CRITICAL: case OMP_ATOMIC: /* OpenMP directives are generally very expensive. */ - *count += 40; + d->count += d->weights->omp_cost; break; default: @@ -2186,16 +2222,20 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data) return NULL; } -/* Estimate number of instructions that will be created by expanding EXPR. */ +/* Estimate number of instructions that will be created by expanding EXPR. + WEIGHTS contains weigths attributed to various constructs. */ int -estimate_num_insns (tree expr) +estimate_num_insns (tree expr, eni_weights *weights) { - int num = 0; struct pointer_set_t *visited_nodes; basic_block bb; block_stmt_iterator bsi; struct function *my_function; + struct eni_data data; + + data.count = 0; + data.weights = weights; /* If we're given an entire function, walk the CFG. */ if (TREE_CODE (expr) == FUNCTION_DECL) @@ -2210,15 +2250,40 @@ estimate_num_insns (tree expr) bsi_next (&bsi)) { walk_tree (bsi_stmt_ptr (bsi), estimate_num_insns_1, - &num, visited_nodes); + &data, visited_nodes); } } pointer_set_destroy (visited_nodes); } else - walk_tree_without_duplicates (&expr, estimate_num_insns_1, &num); + walk_tree_without_duplicates (&expr, estimate_num_insns_1, &data); - return num; + return data.count; +} + +/* Initializes weights used by estimate_num_insns. */ + +void +init_inline_once (void) +{ + eni_inlining_weights.call_cost = PARAM_VALUE (PARAM_INLINE_CALL_COST); + eni_inlining_weights.div_mod_cost = 10; + eni_inlining_weights.switch_cost = 1; + eni_inlining_weights.omp_cost = 40; + + eni_size_weights.call_cost = 1; + eni_size_weights.div_mod_cost = 1; + eni_size_weights.switch_cost = 10; + eni_size_weights.omp_cost = 40; + + /* Estimating time for call is difficult, since we have no idea what the + called function does. In the current uses of eni_time_weights, + underestimating the cost does less harm than overestimating it, so + we choose a rather small walue here. */ + eni_time_weights.call_cost = 10; + eni_time_weights.div_mod_cost = 10; + eni_time_weights.switch_cost = 4; + eni_time_weights.omp_cost = 40; } typedef struct function *function_p; diff --git a/gcc/tree-inline.h b/gcc/tree-inline.h index 102d590a4d3..998494e1aaa 100644 --- a/gcc/tree-inline.h +++ b/gcc/tree-inline.h @@ -93,6 +93,37 @@ typedef struct copy_body_data struct pointer_set_t *statements_to_fold; } copy_body_data; +/* Weights of constructions for estimate_num_insns. */ + +typedef struct eni_weights_d +{ + /* Cost per call. */ + unsigned call_cost; + + /* Cost of "expensive" div and mod operations. */ + unsigned div_mod_cost; + + /* Cost of switch statement. */ + unsigned switch_cost; + + /* Cost for omp construct. */ + unsigned omp_cost; +} eni_weights; + +/* Weights that estimate_num_insns uses for heuristics in inlining. */ + +extern eni_weights eni_inlining_weights; + +/* Weights that estimate_num_insns uses to estimate the size of the + produced code. */ + +extern eni_weights eni_size_weights; + +/* Weights that estimate_num_insns uses to estimate the time necessary + to execute the produced code. */ + +extern eni_weights eni_time_weights; + /* Function prototypes. */ extern tree copy_body_r (tree *, int *, void *); @@ -106,7 +137,7 @@ void save_body (tree, tree *, tree *); int estimate_move_cost (tree type); void push_cfun (struct function *new_cfun); void pop_cfun (void); -int estimate_num_insns (tree expr); +int estimate_num_insns (tree expr, eni_weights *); bool tree_versionable_function_p (tree); void tree_function_versioning (tree, tree, varray_type, bool); diff --git a/gcc/tree-ssa-loop-ch.c b/gcc/tree-ssa-loop-ch.c index e1d5af8a04c..3033c9082ef 100644 --- a/gcc/tree-ssa-loop-ch.c +++ b/gcc/tree-ssa-loop-ch.c @@ -87,7 +87,7 @@ should_duplicate_loop_header_p (basic_block header, struct loop *loop, if (get_call_expr_in (last)) return false; - *limit -= estimate_num_insns (last); + *limit -= estimate_num_insns (last, &eni_size_weights); if (*limit < 0) return false; } diff --git a/gcc/tree-ssa-loop-ivcanon.c b/gcc/tree-ssa-loop-ivcanon.c index fa92a689809..acc8cf30ab5 100644 --- a/gcc/tree-ssa-loop-ivcanon.c +++ b/gcc/tree-ssa-loop-ivcanon.c @@ -111,10 +111,10 @@ create_canonical_iv (struct loop *loop, edge exit, tree niter) update_stmt (cond); } -/* Computes an estimated number of insns in LOOP. */ +/* Computes an estimated number of insns in LOOP, weighted by WEIGHTS. */ unsigned -tree_num_loop_insns (struct loop *loop) +tree_num_loop_insns (struct loop *loop, eni_weights *weights) { basic_block *body = get_loop_body (loop); block_stmt_iterator bsi; @@ -122,7 +122,7 @@ tree_num_loop_insns (struct loop *loop) for (i = 0; i < loop->num_nodes; i++) for (bsi = bsi_start (body[i]); !bsi_end_p (bsi); bsi_next (&bsi)) - size += estimate_num_insns (bsi_stmt (bsi)); + size += estimate_num_insns (bsi_stmt (bsi), weights); free (body); return size; @@ -182,7 +182,7 @@ try_unroll_loop_completely (struct loop *loop, if (ul == UL_SINGLE_ITER) return false; - ninsns = tree_num_loop_insns (loop); + ninsns = tree_num_loop_insns (loop, &eni_size_weights); if (n_unroll * ninsns > (unsigned) PARAM_VALUE (PARAM_MAX_COMPLETELY_PEELED_INSNS)) diff --git a/gcc/tree-ssa-loop-manip.c b/gcc/tree-ssa-loop-manip.c index 580692104d9..d3d3e7e6ab5 100644 --- a/gcc/tree-ssa-loop-manip.c +++ b/gcc/tree-ssa-loop-manip.c @@ -37,6 +37,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "cfglayout.h" #include "tree-scalar-evolution.h" #include "params.h" +#include "tree-inline.h" /* Creates an induction variable with value BASE + STEP * iteration in LOOP. It is expected that neither BASE nor STEP are shared with other expressions @@ -640,7 +641,7 @@ can_unroll_loop_p (struct loop *loop, unsigned factor, return false; /* The final loop should be small enough. */ - if (tree_num_loop_insns (loop) * factor + if (tree_num_loop_insns (loop, &eni_size_weights) * factor > (unsigned) PARAM_VALUE (PARAM_MAX_UNROLLED_INSNS)) return false; diff --git a/gcc/tree-ssa-loop-prefetch.c b/gcc/tree-ssa-loop-prefetch.c index 198c2342ac1..02093d40d2d 100644 --- a/gcc/tree-ssa-loop-prefetch.c +++ b/gcc/tree-ssa-loop-prefetch.c @@ -45,6 +45,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "toplev.h" #include "params.h" #include "langhooks.h" +#include "tree-inline.h" /* This pass inserts prefetch instructions to optimize cache usage during accesses to arrays in loops. It processes loops sequentially and: @@ -954,7 +955,7 @@ loop_prefetch_arrays (struct loop *loop) /* FIXME: We should use not size of the loop, but the average number of instructions executed per iteration of the loop. */ - ninsns = tree_num_loop_insns (loop); + ninsns = tree_num_loop_insns (loop, &eni_time_weights); ahead = (PREFETCH_LATENCY + ninsns - 1) / ninsns; unroll_factor = determine_unroll_factor (loop, refs, ninsns, &desc); if (dump_file && (dump_flags & TDF_DETAILS)) diff --git a/gcc/tree-ssa-loop-unswitch.c b/gcc/tree-ssa-loop-unswitch.c index 7a329c96487..c646ef395c7 100644 --- a/gcc/tree-ssa-loop-unswitch.c +++ b/gcc/tree-ssa-loop-unswitch.c @@ -36,6 +36,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "domwalk.h" #include "params.h" #include "tree-pass.h" +#include "tree-inline.h" /* This file implements the loop unswitching, i.e. transformation of loops like @@ -191,7 +192,7 @@ tree_unswitch_single_loop (struct loop *loop, int num) } /* The loop should not be too large, to limit code growth. */ - if (tree_num_loop_insns (loop) + if (tree_num_loop_insns (loop, &eni_size_weights) > (unsigned) PARAM_VALUE (PARAM_MAX_UNSWITCH_INSNS)) { if (dump_file && (dump_flags & TDF_DETAILS)) diff --git a/gcc/tree.h b/gcc/tree.h index 5f45c94f938..f796a522b32 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -4741,4 +4741,8 @@ extern unsigned HOST_WIDE_INT compute_builtin_object_size (tree, int); /* In expr.c. */ extern unsigned HOST_WIDE_INT highest_pow2_factor (tree); +/* In tree-inline.c. */ + +void init_inline_once (void); + #endif /* GCC_TREE_H */