Hi, for Fortran one of common reason to inline is because array descriptor is known and defines loop stride. This patch makes ipa-inline-analysis to notice these cases.
Bootstrapped/regtested x86_64-linux, will commit it tomorrow if there are no complains. Honza * ipa-inline-analysis.c (dump_inline_hints): Dump loop stride. (set_hint_predicate): New function. (reset_inline_summary): Reset loop stride. (remap_predicate_after_duplication): New function. (remap_hint_predicate_after_duplication): New function. (inline_node_duplication_hook): Update. (dump_inline_summary): Dump stride summaries. (estimate_function_body_sizes): Compute strides. (remap_hint_predicate): New function. (inline_merge_summary): Use it. (inline_read_section): Read stride. (inline_write_summary): Write stride. * ipa-inline.c (want_inline_small_function_p): Handle strides. (edge_badness): Likewise. * ipa-inline.h (inline_hints_vals): Add stride hint. (inline_summary): Update stride. * gcc.dg/ipa/inlinehint-2.c: New testcase. Index: ipa-inline-analysis.c =================================================================== *** ipa-inline-analysis.c (revision 191228) --- ipa-inline-analysis.c (working copy) *************** dump_inline_hints (FILE *f, inline_hints *** 634,639 **** --- 634,644 ---- hints &= ~INLINE_HINT_loop_iterations; fprintf (f, " loop_iterations"); } + if (hints & INLINE_HINT_loop_stride) + { + hints &= ~INLINE_HINT_loop_stride; + fprintf (f, " loop_stride"); + } gcc_assert (!hints); } *************** edge_set_predicate (struct cgraph_edge * *** 719,724 **** --- 724,749 ---- } } + /* Set predicate for hint *P. */ + + static void + set_hint_predicate (struct predicate **p, struct predicate new_predicate) + { + if (false_predicate_p (&new_predicate) + || true_predicate_p (&new_predicate)) + { + if (*p) + pool_free (edge_predicate_pool, *p); + *p = NULL; + } + else + { + if (!*p) + *p = (struct predicate *)pool_alloc (edge_predicate_pool); + **p = new_predicate; + } + } + /* KNOWN_VALS is partial mapping of parameters of NODE to constant values. KNOWN_AGGS is a vector of aggreggate jump functions for each parameter. *************** reset_inline_summary (struct cgraph_node *** 953,958 **** --- 978,988 ---- pool_free (edge_predicate_pool, info->loop_iterations); info->loop_iterations = NULL; } + if (info->loop_stride) + { + pool_free (edge_predicate_pool, info->loop_stride); + info->loop_stride = NULL; + } VEC_free (condition, gc, info->conds); VEC_free (size_time_entry,gc, info->entry); for (e = node->callees; e; e = e->next_callee) *************** inline_node_removal_hook (struct cgraph_ *** 975,980 **** --- 1005,1056 ---- memset (info, 0, sizeof (inline_summary_t)); } + /* Remap predicate P of former function to be predicate of duplicated functoin. + POSSIBLE_TRUTHS is clause of possible truths in the duplicated node, + INFO is inline summary of the duplicated node. */ + + static struct predicate + remap_predicate_after_duplication (struct predicate *p, + clause_t possible_truths, + struct inline_summary *info) + { + struct predicate new_predicate = true_predicate (); + int j; + for (j = 0; p->clause[j]; j++) + if (!(possible_truths & p->clause[j])) + { + new_predicate = false_predicate (); + break; + } + else + add_clause (info->conds, &new_predicate, + possible_truths & p->clause[j]); + return new_predicate; + } + + /* Same as remap_predicate_after_duplication but handle hint predicate *P. + Additionally care about allocating new memory slot for updated predicate + and set it to NULL when it becomes true or false (and thus uninteresting). + */ + + static void + remap_hint_predicate_after_duplication (struct predicate **p, + clause_t possible_truths, + struct inline_summary *info) + { + struct predicate new_predicate; + + if (!*p) + return; + + new_predicate = remap_predicate_after_duplication (*p, + possible_truths, + info); + /* We do not want to free previous predicate; it is used by node origin. */ + *p = NULL; + set_hint_predicate (p, new_predicate); + } + /* Hook that is called by cgraph.c when a node is duplicated. */ *************** inline_node_duplication_hook (struct cgr *** 1042,1057 **** to be true. */ for (i = 0; VEC_iterate (size_time_entry, entry, i, e); i++) { ! struct predicate new_predicate = true_predicate (); ! for (j = 0; e->predicate.clause[j]; j++) ! if (!(possible_truths & e->predicate.clause[j])) ! { ! new_predicate = false_predicate (); ! break; ! } ! else ! add_clause (info->conds, &new_predicate, ! possible_truths & e->predicate.clause[j]); if (false_predicate_p (&new_predicate)) { optimized_out_size += e->size; --- 1118,1127 ---- to be true. */ for (i = 0; VEC_iterate (size_time_entry, entry, i, e); i++) { ! struct predicate new_predicate; ! new_predicate = remap_predicate_after_duplication (&e->predicate, ! possible_truths, ! info); if (false_predicate_p (&new_predicate)) { optimized_out_size += e->size; *************** inline_node_duplication_hook (struct cgr *** 1065,1086 **** Also copy constantness arrays. */ for (edge = dst->callees; edge; edge = edge->next_callee) { ! struct predicate new_predicate = true_predicate (); struct inline_edge_summary *es = inline_edge_summary (edge); if (!edge->inline_failed) inlined_to_p = true; if (!es->predicate) continue; ! for (j = 0; es->predicate->clause[j]; j++) ! if (!(possible_truths & es->predicate->clause[j])) ! { ! new_predicate = false_predicate (); ! break; ! } ! else ! add_clause (info->conds, &new_predicate, ! possible_truths & es->predicate->clause[j]); if (false_predicate_p (&new_predicate) && !false_predicate_p (es->predicate)) { --- 1135,1150 ---- Also copy constantness arrays. */ for (edge = dst->callees; edge; edge = edge->next_callee) { ! struct predicate new_predicate; struct inline_edge_summary *es = inline_edge_summary (edge); if (!edge->inline_failed) inlined_to_p = true; if (!es->predicate) continue; ! new_predicate = remap_predicate_after_duplication (es->predicate, ! possible_truths, ! info); if (false_predicate_p (&new_predicate) && !false_predicate_p (es->predicate)) { *************** inline_node_duplication_hook (struct cgr *** 1097,1118 **** Also copy constantness arrays. */ for (edge = dst->indirect_calls; edge; edge = edge->next_callee) { ! struct predicate new_predicate = true_predicate (); struct inline_edge_summary *es = inline_edge_summary (edge); ! if (!edge->inline_failed) ! inlined_to_p = true; if (!es->predicate) continue; ! for (j = 0; es->predicate->clause[j]; j++) ! if (!(possible_truths & es->predicate->clause[j])) ! { ! new_predicate = false_predicate (); ! break; ! } ! else ! add_clause (info->conds, &new_predicate, ! possible_truths & es->predicate->clause[j]); if (false_predicate_p (&new_predicate) && !false_predicate_p (es->predicate)) { --- 1161,1175 ---- Also copy constantness arrays. */ for (edge = dst->indirect_calls; edge; edge = edge->next_callee) { ! struct predicate new_predicate; struct inline_edge_summary *es = inline_edge_summary (edge); ! gcc_checking_assert (edge->inline_failed); if (!es->predicate) continue; ! new_predicate = remap_predicate_after_duplication (es->predicate, ! possible_truths, ! info); if (false_predicate_p (&new_predicate) && !false_predicate_p (es->predicate)) { *************** inline_node_duplication_hook (struct cgr *** 1124,1151 **** } edge_set_predicate (edge, &new_predicate); } ! if (info->loop_iterations) ! { ! struct predicate new_predicate = true_predicate (); ! ! for (j = 0; info->loop_iterations->clause[j]; j++) ! if (!(possible_truths & info->loop_iterations->clause[j])) ! { ! new_predicate = false_predicate (); ! break; ! } ! else ! add_clause (info->conds, &new_predicate, ! possible_truths & info->loop_iterations->clause[j]); ! if (false_predicate_p (&new_predicate) ! || true_predicate_p (&new_predicate)) ! info->loop_iterations = NULL; ! else ! { ! info->loop_iterations = (struct predicate *)pool_alloc (edge_predicate_pool); ! *info->loop_iterations = new_predicate; ! } ! } /* If inliner or someone after inliner will ever start producing non-trivial clones, we will get trouble with lack of information --- 1181,1192 ---- } edge_set_predicate (edge, &new_predicate); } ! remap_hint_predicate_after_duplication (&info->loop_iterations, ! possible_truths, ! info); ! remap_hint_predicate_after_duplication (&info->loop_stride, ! possible_truths, ! info); /* If inliner or someone after inliner will ever start producing non-trivial clones, we will get trouble with lack of information *************** inline_node_duplication_hook (struct cgr *** 1175,1182 **** if (info->loop_iterations) { predicate p = *info->loop_iterations; ! info->loop_iterations = (struct predicate *)pool_alloc (edge_predicate_pool); ! *info->loop_iterations = p; } } } --- 1216,1229 ---- if (info->loop_iterations) { predicate p = *info->loop_iterations; ! info->loop_iterations = NULL; ! set_hint_predicate (&info->loop_iterations, p); ! } ! if (info->loop_stride) ! { ! predicate p = *info->loop_stride; ! info->loop_stride = NULL; ! set_hint_predicate (&info->loop_stride, p); } } } *************** dump_inline_summary (FILE * f, struct cg *** 1355,1360 **** --- 1402,1412 ---- fprintf (f, " loop iterations:"); dump_predicate (f, s->conds, s->loop_iterations); } + if (s->loop_stride) + { + fprintf (f, " loop stride:"); + dump_predicate (f, s->conds, s->loop_stride); + } fprintf (f, " calls:\n"); dump_inline_edge_summary (f, 4, node, s); fprintf (f, "\n"); *************** estimate_function_body_sizes (struct cgr *** 2390,2395 **** --- 2442,2448 ---- struct loop *loop; loop_iterator li; predicate loop_iterations = true_predicate (); + predicate loop_stride = true_predicate (); if (dump_file && (dump_flags & TDF_DETAILS)) flow_loops_dump (dump_file, NULL, 0); *************** estimate_function_body_sizes (struct cgr *** 2398,2405 **** { VEC (edge, heap) *exits; edge ex; ! unsigned int j; struct tree_niter_desc niter_desc; exits = get_loop_exit_edges (loop); FOR_EACH_VEC_ELT (edge, exits, j, ex) --- 2451,2459 ---- { VEC (edge, heap) *exits; edge ex; ! unsigned int j, i; struct tree_niter_desc niter_desc; + basic_block *body = get_loop_body (loop); exits = get_loop_exit_edges (loop); FOR_EACH_VEC_ELT (edge, exits, j, ex) *************** estimate_function_body_sizes (struct cgr *** 2416,2427 **** loop_iterations = and_predicates (info->conds, &loop_iterations, &will_be_nonconstant); } VEC_free (edge, heap, exits); } ! if (!true_predicate_p (&loop_iterations)) ! { ! inline_summary (node)->loop_iterations = (struct predicate *)pool_alloc (edge_predicate_pool); ! *inline_summary (node)->loop_iterations = loop_iterations; ! } scev_finalize (); } inline_summary (node)->self_time = time; --- 2470,2508 ---- loop_iterations = and_predicates (info->conds, &loop_iterations, &will_be_nonconstant); } VEC_free (edge, heap, exits); + + for (i = 0; i < loop->num_nodes; i++) + { + gimple_stmt_iterator gsi; + for (gsi = gsi_start_bb (body[i]); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple stmt = gsi_stmt (gsi); + affine_iv iv; + ssa_op_iter iter; + tree use; + + FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE) + { + predicate will_be_nonconstant; + + if (!simple_iv (loop, loop_containing_stmt (stmt), use, &iv, true) + || is_gimple_min_invariant (iv.step)) + continue; + will_be_nonconstant + = will_be_nonconstant_expr_predicate (parms_info, info, + iv.step, nonconstant_names); + if (!true_predicate_p (&will_be_nonconstant) + && !false_predicate_p (&will_be_nonconstant)) + /* This is slightly inprecise. We may want to represent each loop with + independent predicate. */ + loop_stride = and_predicates (info->conds, &loop_stride, &will_be_nonconstant); + } + } + } + free (body); } ! set_hint_predicate (&inline_summary (node)->loop_iterations, loop_iterations); ! set_hint_predicate (&inline_summary (node)->loop_stride, loop_stride); scev_finalize (); } inline_summary (node)->self_time = time; *************** estimate_node_size_and_time (struct cgra *** 2715,2720 **** --- 2796,2804 ---- if (info->loop_iterations && !evaluate_predicate (info->loop_iterations, possible_truths)) hints |=INLINE_HINT_loop_iterations; + if (info->loop_stride + && !evaluate_predicate (info->loop_stride, possible_truths)) + hints |=INLINE_HINT_loop_stride; if (time > MAX_TIME * INLINE_TIME_SCALE) time = MAX_TIME * INLINE_TIME_SCALE; *************** remap_edge_summaries (struct cgraph_edg *** 3011,3016 **** --- 3095,3131 ---- } } + /* Same as remap_predicate, but set result into hint *HINT. */ + + static void + remap_hint_predicate (struct inline_summary *info, + struct inline_summary *callee_info, + struct predicate **hint, + VEC (int, heap) *operand_map, + VEC (int, heap) *offset_map, + clause_t possible_truths, + struct predicate *toplev_predicate) + { + predicate p; + + if (!*hint) + return; + p = remap_predicate (info, callee_info, + *hint, + operand_map, offset_map, + possible_truths, + toplev_predicate); + if (!false_predicate_p (&p) + && !true_predicate_p (&p)) + { + if (!*hint) + set_hint_predicate (hint, p); + else + **hint = and_predicates (info->conds, + *hint, + &p); + } + } /* We inlined EDGE. Update summary of the function we inlined into. */ *************** inline_merge_summary (struct cgraph_edge *** 3102,3129 **** } remap_edge_summaries (edge, edge->callee, info, callee_info, operand_map, offset_map, clause, &toplev_predicate); ! if (callee_info->loop_iterations) ! { ! predicate p = remap_predicate (info, callee_info, ! callee_info->loop_iterations, ! operand_map, offset_map, ! clause, ! &toplev_predicate); ! if (!false_predicate_p (&p) ! && !true_predicate_p (&p)) ! { ! if (!info->loop_iterations) ! { ! info->loop_iterations ! = (struct predicate *)pool_alloc (edge_predicate_pool); ! *info->loop_iterations = p; ! } ! else ! *info->loop_iterations = and_predicates (info->conds, ! info->loop_iterations, ! &p); ! } ! } inline_update_callee_summaries (edge->callee, inline_edge_summary (edge)->loop_depth); --- 3217,3230 ---- } remap_edge_summaries (edge, edge->callee, info, callee_info, operand_map, offset_map, clause, &toplev_predicate); ! remap_hint_predicate (info, callee_info, ! &callee_info->loop_iterations, ! operand_map, offset_map, ! clause, &toplev_predicate); ! remap_hint_predicate (info, callee_info, ! &callee_info->loop_stride, ! operand_map, offset_map, ! clause, &toplev_predicate); inline_update_callee_summaries (edge->callee, inline_edge_summary (edge)->loop_depth); *************** inline_read_section (struct lto_file_dec *** 3595,3605 **** } p = read_predicate (&ib); ! if (!true_predicate_p (&p)) ! { ! info->loop_iterations = (struct predicate *)pool_alloc (edge_predicate_pool); ! *info->loop_iterations = p; ! } for (e = node->callees; e; e = e->next_callee) read_inline_edge_summary (&ib, e); for (e = node->indirect_calls; e; e = e->next_callee) --- 3696,3704 ---- } p = read_predicate (&ib); ! set_hint_predicate (&info->loop_iterations, p); ! p = read_predicate (&ib); ! set_hint_predicate (&info->loop_stride, p); for (e = node->callees; e; e = e->next_callee) read_inline_edge_summary (&ib, e); for (e = node->indirect_calls; e; e = e->next_callee) *************** inline_write_summary (void) *** 3747,3752 **** --- 3846,3852 ---- write_predicate (ob, &e->predicate); } write_predicate (ob, info->loop_iterations); + write_predicate (ob, info->loop_stride); for (edge = node->callees; edge; edge = edge->next_callee) write_inline_edge_summary (ob, edge); for (edge = node->indirect_calls; edge; edge = edge->next_callee) Index: ipa-inline.c =================================================================== *** ipa-inline.c (revision 191228) --- ipa-inline.c (working copy) *************** want_inline_small_function_p (struct cgr *** 481,487 **** else if (DECL_DECLARED_INLINE_P (callee->symbol.decl) && growth >= MAX_INLINE_INSNS_SINGLE && !(hints & (INLINE_HINT_indirect_call ! | INLINE_HINT_loop_iterations))) { e->inline_failed = CIF_MAX_INLINE_INSNS_SINGLE_LIMIT; want_inline = false; --- 481,488 ---- else if (DECL_DECLARED_INLINE_P (callee->symbol.decl) && growth >= MAX_INLINE_INSNS_SINGLE && !(hints & (INLINE_HINT_indirect_call ! | INLINE_HINT_loop_iterations ! | INLINE_HINT_loop_stride))) { e->inline_failed = CIF_MAX_INLINE_INSNS_SINGLE_LIMIT; want_inline = false; *************** want_inline_small_function_p (struct cgr *** 533,539 **** inlining given function is very profitable. */ else if (!DECL_DECLARED_INLINE_P (callee->symbol.decl) && growth >= ((hints & (INLINE_HINT_indirect_call ! | INLINE_HINT_loop_iterations)) ? MAX (MAX_INLINE_INSNS_AUTO, MAX_INLINE_INSNS_SINGLE) : MAX_INLINE_INSNS_AUTO)) --- 534,541 ---- inlining given function is very profitable. */ else if (!DECL_DECLARED_INLINE_P (callee->symbol.decl) && growth >= ((hints & (INLINE_HINT_indirect_call ! | INLINE_HINT_loop_iterations ! | INLINE_HINT_loop_stride)) ? MAX (MAX_INLINE_INSNS_AUTO, MAX_INLINE_INSNS_SINGLE) : MAX_INLINE_INSNS_AUTO)) *************** edge_badness (struct cgraph_edge *edge, *** 866,872 **** fprintf (dump_file, "Badness overflow\n"); } if (hints & (INLINE_HINT_indirect_call ! | INLINE_HINT_loop_iterations)) badness /= 8; if (dump) { --- 868,875 ---- fprintf (dump_file, "Badness overflow\n"); } if (hints & (INLINE_HINT_indirect_call ! | INLINE_HINT_loop_iterations ! | INLINE_HINT_loop_stride)) badness /= 8; if (dump) { Index: ipa-inline.h =================================================================== *** ipa-inline.h (revision 191228) --- ipa-inline.h (working copy) *************** typedef struct GTY(()) condition *** 46,52 **** They are represtented as bitmap of the following values. */ enum inline_hints_vals { INLINE_HINT_indirect_call = 1, ! INLINE_HINT_loop_iterations = 2 }; typedef int inline_hints; --- 46,53 ---- They are represtented as bitmap of the following values. */ enum inline_hints_vals { INLINE_HINT_indirect_call = 1, ! INLINE_HINT_loop_iterations = 2, ! INLINE_HINT_loop_stride = 4 }; typedef int inline_hints; *************** struct GTY(()) inline_summary *** 120,128 **** conditions conds; VEC(size_time_entry,gc) *entry; ! /* Predicate on when some loop in the function sbecomes to have known bounds. */ struct predicate * GTY((skip)) loop_iterations; }; --- 121,132 ---- conditions conds; VEC(size_time_entry,gc) *entry; ! /* Predicate on when some loop in the function becomes to have known bounds. */ struct predicate * GTY((skip)) loop_iterations; + /* Predicate on when some loop in the function becomes to have known + stride. */ + struct predicate * GTY((skip)) loop_stride; }; Index: testsuite/gcc.dg/ipa/inlinehint-2.c =================================================================== *** testsuite/gcc.dg/ipa/inlinehint-2.c (revision 0) --- testsuite/gcc.dg/ipa/inlinehint-2.c (working copy) *************** *** 0 **** --- 1,13 ---- + /* { dg-options "-O3 -c -fdump-ipa-inline-details -fno-early-inlining -fno-ipa-cp" } */ + t(int s, void **p) + { + int i; + for (i;i<10000;i+=s) + p[i]=0; + } + m(void **p) + { + t (10); + } + /* { dg-final { scan-ipa-dump "loop_stride" "inline" } } */ + /* { dg-final { cleanup-ipa-dump "inline" } } */