+
+void
+stringop_block_profile (tree stmt, unsigned int *expected_align,
+ HOST_WIDE_INT *expected_size)
+{
+ histogram_value histogram;
+ histogram = gimple_histogram_value_of_type (cfun, stmt, HIST_TYPE_AVERAGE);
+ if (!histogram)
+ *expected_size = -1;
+ else if (!histogram->hvalue.counters[1])
+ {
+ *expected_size = -1;
+ gimple_remove_histogram_value (cfun, stmt, histogram);
+ }
+ else
+ {
+ gcov_type size;
+ size = ((histogram->hvalue.counters[0]
+ + histogram->hvalue.counters[1] / 2)
+ / histogram->hvalue.counters[1]);
+ /* Even if we can hold bigger value in SIZE, INT_MAX
+ is safe "infinity" for code generation strategies. */
+ if (size > INT_MAX)
+ size = INT_MAX;
+ *expected_size = size;
+ gimple_remove_histogram_value (cfun, stmt, histogram);
+ }
+ histogram = gimple_histogram_value_of_type (cfun, stmt, HIST_TYPE_IOR);
+ if (!histogram)
+ *expected_align = 0;
+ else if (!histogram->hvalue.counters[0])
+ {
+ gimple_remove_histogram_value (cfun, stmt, histogram);
+ *expected_align = 0;
+ }
+ else
+ {
+ gcov_type count;
+ int alignment;
+
+ count = histogram->hvalue.counters[0];
+ alignment = 1;
+ while (!(count & alignment)
+ && (alignment * 2 * BITS_PER_UNIT))
+ alignment <<= 1;
+ *expected_align = alignment * BITS_PER_UNIT;
+ gimple_remove_histogram_value (cfun, stmt, histogram);
+ }
+}
+
+struct value_prof_hooks {
+ /* Find list of values for which we want to measure histograms. */
+ void (*find_values_to_profile) (histogram_values *);
+
+ /* Identify and exploit properties of values that are hard to analyze
+ statically. See value-prof.c for more detail. */
+ bool (*value_profile_transformations) (void);
+};
+\f
+/* Find values inside STMT for that we want to measure histograms for
+ division/modulo optimization. */
+static void
+tree_divmod_values_to_profile (tree stmt, histogram_values *values)
+{
+ tree assign, lhs, rhs, divisor, op0, type;
+ histogram_value hist;
+
+ if (TREE_CODE (stmt) == RETURN_EXPR)
+ assign = TREE_OPERAND (stmt, 0);
+ else
+ assign = stmt;
+
+ if (!assign
+ || TREE_CODE (assign) != GIMPLE_MODIFY_STMT)
+ return;
+ lhs = GIMPLE_STMT_OPERAND (assign, 0);
+ type = TREE_TYPE (lhs);
+ if (!INTEGRAL_TYPE_P (type))
+ return;
+
+ rhs = GIMPLE_STMT_OPERAND (assign, 1);
+ switch (TREE_CODE (rhs))
+ {
+ case TRUNC_DIV_EXPR:
+ case TRUNC_MOD_EXPR:
+ divisor = TREE_OPERAND (rhs, 1);
+ op0 = TREE_OPERAND (rhs, 0);
+
+ VEC_reserve (histogram_value, heap, *values, 3);
+
+ if (is_gimple_reg (divisor))
+ /* Check for the case where the divisor is the same value most
+ of the time. */
+ VEC_quick_push (histogram_value, *values,
+ gimple_alloc_histogram_value (cfun, HIST_TYPE_SINGLE_VALUE,
+ stmt, divisor));
+
+ /* For mod, check whether it is not often a noop (or replaceable by
+ a few subtractions). */
+ if (TREE_CODE (rhs) == TRUNC_MOD_EXPR
+ && TYPE_UNSIGNED (type))
+ {
+ tree val;
+ /* Check for a special case where the divisor is power of 2. */
+ VEC_quick_push (histogram_value, *values,
+ gimple_alloc_histogram_value (cfun, HIST_TYPE_POW2,
+ stmt, divisor));
+
+ val = build2 (TRUNC_DIV_EXPR, type, op0, divisor);
+ hist = gimple_alloc_histogram_value (cfun, HIST_TYPE_INTERVAL,
+ stmt, val);
+ hist->hdata.intvl.int_start = 0;
+ hist->hdata.intvl.steps = 2;
+ VEC_quick_push (histogram_value, *values, hist);
+ }
+ return;
+
+ default:
+ return;
+ }
+}
+
+/* Find calls inside STMT for that we want to measure histograms for
+ indirect/virtual call optimization. */
+
+static void
+tree_indirect_call_to_profile (tree stmt, histogram_values *values)
+{
+ tree call;
+ tree callee;
+
+ call = get_call_expr_in (stmt);
+
+ if (!call || TREE_CODE (call) != CALL_EXPR)
+ return;
+
+ callee = CALL_EXPR_FN (call);
+
+ if (TREE_CODE (callee) == ADDR_EXPR)
+ return;
+
+ VEC_reserve (histogram_value, heap, *values, 3);
+
+ VEC_quick_push (histogram_value, *values,
+ gimple_alloc_histogram_value (cfun, HIST_TYPE_INDIR_CALL,
+ stmt, callee));
+
+ return;
+}
+
+/* Find values inside STMT for that we want to measure histograms for
+ string operations. */
+static void
+tree_stringops_values_to_profile (tree stmt, histogram_values *values)
+{
+ tree call = get_call_expr_in (stmt);
+ tree fndecl;
+ tree blck_size;
+ tree dest;
+ enum built_in_function fcode;
+
+ if (!call)
+ return;
+ fndecl = get_callee_fndecl (call);
+ if (!fndecl)
+ return;
+ fcode = DECL_FUNCTION_CODE (fndecl);
+
+ if (!interesting_stringop_to_profile_p (fndecl, call))
+ return;
+
+ dest = CALL_EXPR_ARG (call, 0);
+ if (fcode == BUILT_IN_BZERO)
+ blck_size = CALL_EXPR_ARG (call, 1);
+ else
+ blck_size = CALL_EXPR_ARG (call, 2);
+
+ if (TREE_CODE (blck_size) != INTEGER_CST)
+ {
+ VEC_safe_push (histogram_value, heap, *values,
+ gimple_alloc_histogram_value (cfun, HIST_TYPE_SINGLE_VALUE,
+ stmt, blck_size));
+ VEC_safe_push (histogram_value, heap, *values,
+ gimple_alloc_histogram_value (cfun, HIST_TYPE_AVERAGE,
+ stmt, blck_size));
+ }
+ if (TREE_CODE (blck_size) != INTEGER_CST)
+ VEC_safe_push (histogram_value, heap, *values,
+ gimple_alloc_histogram_value (cfun, HIST_TYPE_IOR,
+ stmt, dest));
+}
+
+/* Find values inside STMT for that we want to measure histograms and adds
+ them to list VALUES. */
+
+static void
+tree_values_to_profile (tree stmt, histogram_values *values)
+{
+ if (flag_value_profile_transformations)
+ {
+ tree_divmod_values_to_profile (stmt, values);
+ tree_stringops_values_to_profile (stmt, values);
+ tree_indirect_call_to_profile (stmt, values);
+ }
+}
+
+static void
+tree_find_values_to_profile (histogram_values *values)
+{
+ basic_block bb;
+ block_stmt_iterator bsi;
+ unsigned i;
+ histogram_value hist = NULL;
+
+ *values = NULL;
+ FOR_EACH_BB (bb)
+ for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+ tree_values_to_profile (bsi_stmt (bsi), values);
+
+ for (i = 0; VEC_iterate (histogram_value, *values, i, hist); i++)
+ {
+ switch (hist->type)
+ {
+ case HIST_TYPE_INTERVAL:
+ hist->n_counters = hist->hdata.intvl.steps + 2;
+ break;
+
+ case HIST_TYPE_POW2:
+ hist->n_counters = 2;
+ break;
+
+ case HIST_TYPE_SINGLE_VALUE:
+ hist->n_counters = 3;
+ break;
+
+ case HIST_TYPE_CONST_DELTA:
+ hist->n_counters = 4;
+ break;
+
+ case HIST_TYPE_INDIR_CALL:
+ hist->n_counters = 3;
+ break;
+
+ case HIST_TYPE_AVERAGE:
+ hist->n_counters = 2;
+ break;
+
+ case HIST_TYPE_IOR:
+ hist->n_counters = 1;
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ if (dump_file)
+ {
+ fprintf (dump_file, "Stmt ");
+ print_generic_expr (dump_file, hist->hvalue.stmt, TDF_SLIM);
+ dump_histogram_value (dump_file, hist);
+ }
+ }
+}
+
+static struct value_prof_hooks tree_value_prof_hooks = {
+ tree_find_values_to_profile,
+ tree_value_profile_transformations
+};
+
+void
+tree_register_value_prof_hooks (void)
+{
+ gcc_assert (current_ir_type () == IR_GIMPLE);
+ value_prof_hooks = &tree_value_prof_hooks;
+}
+\f
+/* IR-independent entry points. */
+void
+find_values_to_profile (histogram_values *values)
+{
+ (value_prof_hooks->find_values_to_profile) (values);
+}
+
+bool
+value_profile_transformations (void)
+{
+ return (value_prof_hooks->value_profile_transformations) ();
+}
+\f
+