Hi, this implements use of value ranges in ipa-predicates so inliner know when some tests are going to be removed (especially NULL pointer checks).
Bootstrapped/regtested x86_64-linux. Martin, I would apprechiate if you look on the patch. Honza * ipa-cp.c (ipa_vr_operation_and_type_effects): Move up in file. (ipa_value_range_from_jfunc): New function. * ipa-fnsummary.c (evaluate_conditions_for_known_args): Add known_value_ranges parameter; use it to evalulate conditions. (evaluate_properties_for_edge): Compute known value ranges. (ipa_fn_summary_t::duplicate): Update use of evaluate_conditions_for_known_args. (estimate_ipcp_clone_size_and_time): Likewise. (ipa_merge_fn_summary_after_inlining): Likewise. * ipa-prop.h (ipa_value_range_from_jfunc): Declare. * gcc.dg/ipa/inline-9.c: New testcase. Index: ipa-cp.c =================================================================== --- ipa-cp.c (revision 278094) +++ ipa-cp.c (working copy) @@ -1459,6 +1459,87 @@ ipa_context_from_jfunc (ipa_node_params return ctx; } +/* Emulate effects of unary OPERATION and/or conversion from SRC_TYPE to + DST_TYPE on value range in SRC_VR and store it to DST_VR. Return true if + the result is a range or an anti-range. */ + +static bool +ipa_vr_operation_and_type_effects (value_range *dst_vr, + value_range *src_vr, + enum tree_code operation, + tree dst_type, tree src_type) +{ + range_fold_unary_expr (dst_vr, operation, dst_type, src_vr, src_type); + if (dst_vr->varying_p () || dst_vr->undefined_p ()) + return false; + return true; +} + +/* Determine value_range of JFUNC given that INFO describes the caller node or + the one it is inlined to, CS is the call graph edge corresponding to JFUNC + and PARM_TYPE of the parameter. */ + +value_range +ipa_value_range_from_jfunc (ipa_node_params *info, cgraph_edge *cs, + ipa_jump_func *jfunc, tree parm_type) +{ + value_range vr; + return vr; + if (jfunc->m_vr) + ipa_vr_operation_and_type_effects (&vr, + jfunc->m_vr, + NOP_EXPR, parm_type, + jfunc->m_vr->type ()); + if (vr.singleton_p ()) + return vr; + if (jfunc->type == IPA_JF_PASS_THROUGH) + { + int idx; + ipcp_transformation *sum + = ipcp_get_transformation_summary (cs->caller->inlined_to + ? cs->caller->inlined_to + : cs->caller); + if (!sum || !sum->m_vr) + return vr; + + idx = ipa_get_jf_pass_through_formal_id (jfunc); + + if (!(*sum->m_vr)[idx].known) + return vr; + tree vr_type = ipa_get_type (info, idx); + value_range srcvr ((*sum->m_vr)[idx].type, + wide_int_to_tree (vr_type, (*sum->m_vr)[idx].min), + wide_int_to_tree (vr_type, (*sum->m_vr)[idx].max)); + + enum tree_code operation = ipa_get_jf_pass_through_operation (jfunc); + + if (TREE_CODE_CLASS (operation) == tcc_unary) + { + value_range res; + + if (ipa_vr_operation_and_type_effects (&res, + &srcvr, + operation, parm_type, + vr_type)) + vr.intersect (res); + } + else + { + value_range op_res, res; + tree op = ipa_get_jf_pass_through_operand (jfunc); + value_range op_vr (op, op); + + range_fold_binary_expr (&op_res, operation, vr_type, &srcvr, &op_vr); + if (ipa_vr_operation_and_type_effects (&res, + &op_res, + NOP_EXPR, parm_type, + vr_type)) + vr.intersect (res); + } + } + return vr; +} + /* If checking is enabled, verify that no lattice is in the TOP state, i.e. not bottom, not containing a variable component and without any known value at the same time. */ @@ -1936,22 +2017,6 @@ propagate_bits_across_jump_function (cgr return dest_lattice->set_to_bottom (); } -/* Emulate effects of unary OPERATION and/or conversion from SRC_TYPE to - DST_TYPE on value range in SRC_VR and store it to DST_VR. Return true if - the result is a range or an anti-range. */ - -static bool -ipa_vr_operation_and_type_effects (value_range *dst_vr, - value_range *src_vr, - enum tree_code operation, - tree dst_type, tree src_type) -{ - range_fold_unary_expr (dst_vr, operation, dst_type, src_vr, src_type); - if (dst_vr->varying_p () || dst_vr->undefined_p ()) - return false; - return true; -} - /* Propagate value range across jump function JFUNC that is associated with edge CS with param of callee of PARAM_TYPE and update DEST_PLATS accordingly. */ Index: ipa-fnsummary.c =================================================================== --- ipa-fnsummary.c (revision 278094) +++ ipa-fnsummary.c (working copy) @@ -316,6 +316,7 @@ static void evaluate_conditions_for_known_args (struct cgraph_node *node, bool inline_p, vec<tree> known_vals, + vec<value_range> known_value_ranges, vec<ipa_agg_jump_function_p> known_aggs, clause_t *ret_clause, @@ -372,7 +373,9 @@ evaluate_conditions_for_known_args (stru val = NULL_TREE; } - if (!val) + if (!val + && (c->code == predicate::changed + || c->code == predicate::is_not_constant)) { clause |= 1 << (i + predicate::first_dynamic_condition); nonspec_clause |= 1 << (i + predicate::first_dynamic_condition); @@ -384,48 +387,101 @@ evaluate_conditions_for_known_args (stru continue; } - if (TYPE_SIZE (c->type) != TYPE_SIZE (TREE_TYPE (val))) - { - clause |= 1 << (i + predicate::first_dynamic_condition); - nonspec_clause |= 1 << (i + predicate::first_dynamic_condition); - continue; - } if (c->code == predicate::is_not_constant) { nonspec_clause |= 1 << (i + predicate::first_dynamic_condition); continue; } - val = fold_unary (VIEW_CONVERT_EXPR, c->type, val); - for (j = 0; vec_safe_iterate (c->param_ops, j, &op); j++) + if (val && TYPE_SIZE (c->type) == TYPE_SIZE (TREE_TYPE (val))) { - if (!val) - break; - if (!op->val[0]) - val = fold_unary (op->code, op->type, val); - else if (!op->val[1]) - val = fold_binary (op->code, op->type, - op->index ? op->val[0] : val, - op->index ? val : op->val[0]); - else if (op->index == 0) - val = fold_ternary (op->code, op->type, - val, op->val[0], op->val[1]); - else if (op->index == 1) - val = fold_ternary (op->code, op->type, - op->val[0], val, op->val[1]); - else if (op->index == 2) - val = fold_ternary (op->code, op->type, - op->val[0], op->val[1], val); - else - val = NULL_TREE; + if (c->type != TREE_TYPE (val)) + val = fold_unary (VIEW_CONVERT_EXPR, c->type, val); + for (j = 0; vec_safe_iterate (c->param_ops, j, &op); j++) + { + if (!val) + break; + if (!op->val[0]) + val = fold_unary (op->code, op->type, val); + else if (!op->val[1]) + val = fold_binary (op->code, op->type, + op->index ? op->val[0] : val, + op->index ? val : op->val[0]); + else if (op->index == 0) + val = fold_ternary (op->code, op->type, + val, op->val[0], op->val[1]); + else if (op->index == 1) + val = fold_ternary (op->code, op->type, + op->val[0], val, op->val[1]); + else if (op->index == 2) + val = fold_ternary (op->code, op->type, + op->val[0], op->val[1], val); + else + val = NULL_TREE; + } + + res = val + ? fold_binary_to_constant (c->code, boolean_type_node, val, c->val) + : NULL; + + if (res && integer_zerop (res)) + continue; + if (res && integer_onep (res)) + { + clause |= 1 << (i + predicate::first_dynamic_condition); + nonspec_clause |= 1 << (i + predicate::first_dynamic_condition); + continue; + } } + if (c->operand_num < (int) known_value_ranges.length () + && !c->agg_contents + && !known_value_ranges[c->operand_num].undefined_p () + && !known_value_ranges[c->operand_num].varying_p () + && TYPE_SIZE (c->type) + == TYPE_SIZE (known_value_ranges[c->operand_num].type ()) + && (!val || TREE_CODE (val) != INTEGER_CST)) + { + value_range vr = known_value_ranges[c->operand_num]; + if (!useless_type_conversion_p (c->type, vr.type ())) + { + value_range res; + range_fold_unary_expr (&res, NOP_EXPR, + c->type, &vr, vr.type ()); + vr = res; + } + tree type = c->type; - res = val - ? fold_binary_to_constant (c->code, boolean_type_node, val, c->val) - : NULL; + for (j = 0; vec_safe_iterate (c->param_ops, j, &op); j++) + { + if (vr.varying_p () || vr.undefined_p ()) + break; - if (res && integer_zerop (res)) - continue; + value_range res; + if (!op->val[0]) + range_fold_unary_expr (&res, op->code, op->type, &vr, type); + else if (!op->val[1]) + { + value_range op0 (VR_RANGE, op->val[0], op->val[0]); + range_fold_binary_expr (&res, op->code, op->type, + op->index ? &op0 : &vr, + op->index ? &vr : &op0); + } + else + gcc_unreachable (); + type = op->type; + vr = res; + } + if (!vr.varying_p () && !vr.undefined_p ()) + { + value_range res; + value_range val_vr (VR_RANGE, c->val, c->val); + range_fold_binary_expr (&res, c->code, boolean_type_node, + &vr, + &val_vr); + if (res.zero_p ()) + continue; + } + } clause |= 1 << (i + predicate::first_dynamic_condition); nonspec_clause |= 1 << (i + predicate::first_dynamic_condition); @@ -450,6 +506,7 @@ evaluate_properties_for_edge (struct cgr struct cgraph_node *callee = e->callee->ultimate_alias_target (); class ipa_fn_summary *info = ipa_fn_summaries->get (callee); vec<tree> known_vals = vNULL; + auto_vec<value_range, 32> known_value_ranges; vec<ipa_agg_jump_function_p> known_aggs = vNULL; class ipa_edge_args *args; @@ -477,6 +534,8 @@ evaluate_properties_for_edge (struct cgr if (count && (info->conds || known_vals_ptr)) known_vals.safe_grow_cleared (count); + if (count && info->conds) + known_value_ranges.safe_grow_cleared (count); if (count && (info->conds || known_aggs_ptr)) known_aggs.safe_grow_cleared (count); if (count && known_contexts_ptr) @@ -505,14 +564,18 @@ evaluate_properties_for_edge (struct cgr else if (inline_p && !es->param[i].change_prob) known_vals[i] = error_mark_node; - if (known_contexts_ptr) - (*known_contexts_ptr)[i] - = ipa_context_from_jfunc (caller_parms_info, e, i, jf); - /* TODO: When IPA-CP starts propagating and merging aggregate jump - functions, use its knowledge of the caller too, just like the - scalar case above. */ - known_aggs[i] = &jf->agg; - } + if (known_contexts_ptr) + (*known_contexts_ptr)[i] + = ipa_context_from_jfunc (caller_parms_info, e, i, jf); + /* TODO: When IPA-CP starts propagating and merging aggregate jump + functions, use its knowledge of the caller too, just like the + scalar case above. */ + known_aggs[i] = &jf->agg; + if (info->conds) + known_value_ranges[i] + = ipa_value_range_from_jfunc (caller_parms_info, e, jf, + ipa_get_type (callee_pi, i)); + } else gcc_assert (callee->thunk.thunk_p); } @@ -534,7 +597,9 @@ evaluate_properties_for_edge (struct cgr } evaluate_conditions_for_known_args (callee, inline_p, - known_vals, known_aggs, clause_ptr, + known_vals, + known_value_ranges, + known_aggs, clause_ptr, nonspec_clause_ptr); if (known_vals_ptr) @@ -657,6 +722,7 @@ ipa_fn_summary_t::duplicate (cgraph_node evaluate_conditions_for_known_args (dst, false, known_vals, vNULL, + vNULL, &possible_truths, /* We are going to specialize, so ignore nonspec truths. */ @@ -3355,8 +3424,9 @@ estimate_ipcp_clone_size_and_time (struc { clause_t clause, nonspec_clause; - evaluate_conditions_for_known_args (node, false, known_vals, known_aggs, - &clause, &nonspec_clause); + /* TODO: Also pass known value ranges. */ + evaluate_conditions_for_known_args (node, false, known_vals, vNULL, + known_aggs, &clause, &nonspec_clause); ipa_call_context ctx (node, clause, nonspec_clause, known_vals, known_contexts, known_aggs, vNULL); @@ -3576,7 +3646,8 @@ ipa_merge_fn_summary_after_inlining (str info->fp_expressions |= callee_info->fp_expressions; if (callee_info->conds) - evaluate_properties_for_edge (edge, true, &clause, NULL, NULL, NULL, NULL); + evaluate_properties_for_edge (edge, true, &clause, + NULL, NULL, NULL, NULL); if (ipa_node_params_sum && callee_info->conds) { class ipa_edge_args *args = IPA_EDGE_REF (edge); Index: ipa-prop.h =================================================================== --- ipa-prop.h (revision 278094) +++ ipa-prop.h (working copy) @@ -918,6 +918,8 @@ ipa_polymorphic_call_context ipa_context cgraph_edge *, int, ipa_jump_func *); +value_range ipa_value_range_from_jfunc (ipa_node_params *, cgraph_edge *, + ipa_jump_func *, tree); void ipa_dump_param (FILE *, class ipa_node_params *info, int i); void ipa_release_body_info (struct ipa_func_body_info *); tree ipa_get_callee_param_type (struct cgraph_edge *e, int i); Index: testsuite/gcc.dg/ipa/inline-9.c =================================================================== --- testsuite/gcc.dg/ipa/inline-9.c (nonexistent) +++ testsuite/gcc.dg/ipa/inline-9.c (working copy) @@ -0,0 +1,21 @@ +/* { dg-options "-Os -fdump-ipa-inline" } */ +int test(int a) +{ + if (a>100) + { + foo(); + foo(); + foo(); + foo(); + foo(); + foo(); + foo(); + foo(); + } +} +main() +{ + for (int i=0;i<100;i++) + test(i); +} +/* { dg-final { scan-tree-dump "Inlined 1 calls" "inline" } } */