diff options
author | Martin Jambor <mjambor@suse.cz> | 2021-07-27 10:02:38 +0200 |
---|---|---|
committer | Martin Jambor <mjambor@suse.cz> | 2021-07-27 10:03:17 +0200 |
commit | 13586172d0b70c9d7ca464fc5a2a46a8532b06d7 (patch) | |
tree | d9b3cd1eab248ec0adbf6334a9fa4b8663107b06 /gcc/ipa-prop.c | |
parent | a21bd3cebd6f54af70a37c18b8fbeae933fb6515 (diff) | |
download | gcc-13586172d0b70c9d7ca464fc5a2a46a8532b06d7.zip gcc-13586172d0b70c9d7ca464fc5a2a46a8532b06d7.tar.gz gcc-13586172d0b70c9d7ca464fc5a2a46a8532b06d7.tar.bz2 |
ipa: Adjust references to identify read-only globals
this patch has been motivated by SPEC 2017's 544.nab_r in which there is
a static variable which is never written to and so zero throughout the
run-time of the benchmark. However, it is passed by reference to a
function in which it is read and (after some multiplications) passed
into __builtin_exp which in turn unnecessarily consumes almost 10% of
the total benchmark run-time. The situation is illustrated by the added
testcase remref-3.c.
The patch adds a flag to ipa-prop descriptor of each parameter to mark
such parameters. IPA-CP and inling then take the effort to remove
IPA_REF_ADDR references in the caller and only add IPA_REF_LOAD
reference to the clone/overall inlined function. This is sufficient
for subsequent symbol table analysis code to identify the read-only
variable as such and optimize the code.
There are two changes from the RFC version posted to the list earlier.
First, three missing calls to get_base_address were added (there was
another one in an assert). Second, references are not stripped off
the callers if the cloned function cannot change the signature. The
second change reveals a real shortcoming stemming from the fact we
cannot adjust function prototypes with fnspecs. But that is a more
general problem.
gcc/ChangeLog:
2021-07-20 Martin Jambor <mjambor@suse.cz>
* cgraph.h (ipa_replace_map): New field force_load_ref.
* ipa-prop.h (ipa_param_descriptor): Reduce precision of move_cost,
aded new flag load_dereferenced, adjusted comments.
(ipa_get_param_dereferenced): New function.
(ipa_set_param_dereferenced): Likewise.
* cgraphclones.c (cgraph_node::create_virtual_clone): Follow it.
* ipa-cp.c: Include gimple.h.
(ipcp_discover_new_direct_edges): Take into account dereferenced flag.
(get_replacement_map): New parameter force_load_ref, set the
appropriate flag in ipa_replace_map if set.
(struct symbol_and_index_together): New type.
(adjust_refs_in_act_callers): New function.
(adjust_references_in_caller): Likewise.
(create_specialized_node): When appropriate, call
adjust_references_in_caller and force only load references.
* ipa-prop.c (load_from_dereferenced_name): New function.
(ipa_analyze_controlled_uses): Also detect loads from a
dereference, harden testing of call statements.
(ipa_write_node_info): Stream the dereferenced flag.
(ipa_read_node_info): Likewise.
(ipa_set_jf_constant): Also create refdesc when jump function
references a variable.
(cgraph_node_for_jfunc): Rename to symtab_node_for_jfunc, work
also on references of variables and return a symtab_node. Adjust
all callers.
(propagate_controlled_uses): Also remove references to VAR_DECLs.
gcc/testsuite/ChangeLog:
2021-06-29 Martin Jambor <mjambor@suse.cz>
* gcc.dg/ipa/remref-3.c: New test.
* gcc.dg/ipa/remref-4.c: Likewise.
* gcc.dg/ipa/remref-5.c: Likewise.
* gcc.dg/ipa/remref-6.c: Likewise.
Diffstat (limited to 'gcc/ipa-prop.c')
-rw-r--r-- | gcc/ipa-prop.c | 166 |
1 files changed, 130 insertions, 36 deletions
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c index 43f46a5..0afd05e 100644 --- a/gcc/ipa-prop.c +++ b/gcc/ipa-prop.c @@ -544,7 +544,9 @@ ipa_set_jf_constant (struct ipa_jump_func *jfunc, tree constant, jfunc->value.constant.value = unshare_expr_without_location (constant); if (TREE_CODE (constant) == ADDR_EXPR - && TREE_CODE (TREE_OPERAND (constant, 0)) == FUNCTION_DECL) + && (TREE_CODE (TREE_OPERAND (constant, 0)) == FUNCTION_DECL + || (TREE_CODE (TREE_OPERAND (constant, 0)) == VAR_DECL + && TREE_STATIC (TREE_OPERAND (constant, 0))))) { struct ipa_cst_ref_desc *rdesc; @@ -2876,6 +2878,16 @@ ipa_analyze_params_uses_in_bb (struct ipa_func_body_info *fbi, basic_block bb) visit_ref_for_mod_analysis); } +/* Return true EXPR is a load from a dereference of SSA_NAME NAME. */ + +static bool +load_from_dereferenced_name (tree expr, tree name) +{ + tree base = get_base_address (expr); + return (TREE_CODE (base) == MEM_REF + && TREE_OPERAND (base, 0) == name); +} + /* Calculate controlled uses of parameters of NODE. */ static void @@ -2886,7 +2898,8 @@ ipa_analyze_controlled_uses (struct cgraph_node *node) for (int i = 0; i < ipa_get_param_count (info); i++) { tree parm = ipa_get_param (info, i); - int controlled_uses = 0; + int call_uses = 0; + bool load_dereferenced = false; /* For SSA regs see if parameter is used. For non-SSA we compute the flag during modification analysis. */ @@ -2897,27 +2910,77 @@ ipa_analyze_controlled_uses (struct cgraph_node *node) if (ddef && !has_zero_uses (ddef)) { imm_use_iterator imm_iter; - use_operand_p use_p; + gimple *stmt; ipa_set_param_used (info, i, true); - FOR_EACH_IMM_USE_FAST (use_p, imm_iter, ddef) - if (!is_gimple_call (USE_STMT (use_p))) - { - if (!is_gimple_debug (USE_STMT (use_p))) - { - controlled_uses = IPA_UNDESCRIBED_USE; - break; - } - } - else - controlled_uses++; + FOR_EACH_IMM_USE_STMT (stmt, imm_iter, ddef) + { + if (is_gimple_debug (stmt)) + continue; + + int all_stmt_uses = 0; + use_operand_p use_p; + FOR_EACH_IMM_USE_ON_STMT (use_p, imm_iter) + all_stmt_uses++; + + if (is_gimple_call (stmt)) + { + if (gimple_call_internal_p (stmt)) + { + call_uses = IPA_UNDESCRIBED_USE; + break; + } + int recognized_stmt_uses; + if (gimple_call_fn (stmt) == ddef) + recognized_stmt_uses = 1; + else + recognized_stmt_uses = 0; + unsigned arg_count = gimple_call_num_args (stmt); + for (unsigned i = 0; i < arg_count; i++) + { + tree arg = gimple_call_arg (stmt, i); + if (arg == ddef) + recognized_stmt_uses++; + else if (load_from_dereferenced_name (arg, ddef)) + { + load_dereferenced = true; + recognized_stmt_uses++; + } + } + + if (recognized_stmt_uses != all_stmt_uses) + { + call_uses = IPA_UNDESCRIBED_USE; + break; + } + if (call_uses >= 0) + call_uses += all_stmt_uses; + } + else if (gimple_assign_single_p (stmt)) + { + tree rhs = gimple_assign_rhs1 (stmt); + if (all_stmt_uses != 1 + || !load_from_dereferenced_name (rhs, ddef)) + { + call_uses = IPA_UNDESCRIBED_USE; + break; + } + load_dereferenced = true; + } + else + { + call_uses = IPA_UNDESCRIBED_USE; + break; + } + } } else - controlled_uses = 0; + call_uses = 0; } else - controlled_uses = IPA_UNDESCRIBED_USE; - ipa_set_controlled_uses (info, i, controlled_uses); + call_uses = IPA_UNDESCRIBED_USE; + ipa_set_controlled_uses (info, i, call_uses); + ipa_set_param_load_dereferenced (info, i, load_dereferenced); } } @@ -3640,16 +3703,17 @@ jfunc_rdesc_usable (struct ipa_jump_func *jfunc) declaration, return the associated call graph node. Otherwise return NULL. */ -static cgraph_node * -cgraph_node_for_jfunc (struct ipa_jump_func *jfunc) +static symtab_node * +symtab_node_for_jfunc (struct ipa_jump_func *jfunc) { gcc_checking_assert (jfunc->type == IPA_JF_CONST); tree cst = ipa_get_jf_constant (jfunc); if (TREE_CODE (cst) != ADDR_EXPR - || TREE_CODE (TREE_OPERAND (cst, 0)) != FUNCTION_DECL) + || (TREE_CODE (TREE_OPERAND (cst, 0)) != FUNCTION_DECL + && TREE_CODE (TREE_OPERAND (cst, 0)) != VAR_DECL)) return NULL; - return cgraph_node::get (TREE_OPERAND (cst, 0)); + return symtab_node::get (TREE_OPERAND (cst, 0)); } @@ -3666,7 +3730,7 @@ try_decrement_rdesc_refcount (struct ipa_jump_func *jfunc) && (rdesc = jfunc_rdesc_usable (jfunc)) && --rdesc->refcount == 0) { - symtab_node *symbol = cgraph_node_for_jfunc (jfunc); + symtab_node *symbol = symtab_node_for_jfunc (jfunc); if (!symbol) return false; @@ -3721,8 +3785,8 @@ try_make_edge_direct_simple_call (struct cgraph_edge *ie, gcc_checking_assert (cs->callee && (cs != ie || jfunc->type != IPA_JF_CONST - || !cgraph_node_for_jfunc (jfunc) - || cs->callee == cgraph_node_for_jfunc (jfunc))); + || !symtab_node_for_jfunc (jfunc) + || cs->callee == symtab_node_for_jfunc (jfunc))); ok = try_decrement_rdesc_refcount (jfunc); gcc_checking_assert (ok); } @@ -4085,7 +4149,15 @@ propagate_controlled_uses (struct cgraph_edge *cs) == NOP_EXPR || c == IPA_UNDESCRIBED_USE); c = combine_controlled_uses_counters (c, d); ipa_set_controlled_uses (new_root_info, src_idx, c); - if (c == 0 && new_root_info->ipcp_orig_node) + bool lderef = true; + if (c != IPA_UNDESCRIBED_USE) + { + lderef = (ipa_get_param_load_dereferenced (new_root_info, src_idx) + || ipa_get_param_load_dereferenced (old_root_info, i)); + ipa_set_param_load_dereferenced (new_root_info, src_idx, lderef); + } + + if (c == 0 && !lderef && new_root_info->ipcp_orig_node) { struct cgraph_node *n; struct ipa_ref *ref; @@ -4114,17 +4186,27 @@ propagate_controlled_uses (struct cgraph_edge *cs) if (rdesc->refcount == 0) { tree cst = ipa_get_jf_constant (jf); - struct cgraph_node *n; gcc_checking_assert (TREE_CODE (cst) == ADDR_EXPR - && TREE_CODE (TREE_OPERAND (cst, 0)) - == FUNCTION_DECL); - n = cgraph_node::get (TREE_OPERAND (cst, 0)); + && ((TREE_CODE (TREE_OPERAND (cst, 0)) + == FUNCTION_DECL) + || (TREE_CODE (TREE_OPERAND (cst, 0)) + == VAR_DECL))); + + symtab_node *n = symtab_node::get (TREE_OPERAND (cst, 0)); if (n) { struct cgraph_node *clone; - bool ok; - ok = remove_described_reference (n, rdesc); - gcc_checking_assert (ok); + bool removed = remove_described_reference (n, rdesc); + /* The reference might have been removed by IPA-CP. */ + if (removed + && ipa_get_param_load_dereferenced (old_root_info, i)) + { + new_root->create_reference (n, IPA_REF_LOAD, NULL); + if (dump_file) + fprintf (dump_file, "ipa-prop: ...replaced it with " + " LOAD one from %s to %s.\n", + new_root->dump_name (), n->dump_name ()); + } clone = cs->caller; while (clone->inlined_to @@ -4347,7 +4429,7 @@ ipa_edge_args_sum_t::duplicate (cgraph_edge *src, cgraph_edge *dst, else if (src->caller == dst->caller) { struct ipa_ref *ref; - symtab_node *n = cgraph_node_for_jfunc (src_jf); + symtab_node *n = symtab_node_for_jfunc (src_jf); gcc_checking_assert (n); ref = src->caller->find_reference (n, src->call_stmt, src->lto_stmt_uid); @@ -4574,7 +4656,9 @@ ipa_print_node_params (FILE *f, struct cgraph_node *node) if (c == IPA_UNDESCRIBED_USE) fprintf (f, " undescribed_use"); else - fprintf (f, " controlled_uses=%i", c); + fprintf (f, " controlled_uses=%i %s", c, + ipa_get_param_load_dereferenced (info, i) + ? "(load_dereferenced)" : ""); fprintf (f, "\n"); } } @@ -4969,7 +5053,13 @@ ipa_write_node_info (struct output_block *ob, struct cgraph_node *node) gcc_assert (!info->node_enqueued); gcc_assert (!info->ipcp_orig_node); for (j = 0; j < ipa_get_param_count (info); j++) - bp_pack_value (&bp, ipa_is_param_used (info, j), 1); + { + /* TODO: We could just not stream the bit in the undescribed case. */ + bool d = (ipa_get_controlled_uses (info, j) != IPA_UNDESCRIBED_USE) + ? ipa_get_param_load_dereferenced (info, j) : true; + bp_pack_value (&bp, d, 1); + bp_pack_value (&bp, ipa_is_param_used (info, j), 1); + } streamer_write_bitpack (&bp); for (j = 0; j < ipa_get_param_count (info); j++) { @@ -5095,10 +5185,14 @@ ipa_read_node_info (class lto_input_block *ib, struct cgraph_node *node, bp = streamer_read_bitpack (ib); for (k = 0; k < param_count; k++) { + bool load_dereferenced = bp_unpack_value (&bp, 1); bool used = bp_unpack_value (&bp, 1); if (prevails) - ipa_set_param_used (info, k, used); + { + ipa_set_param_load_dereferenced (info, k, load_dereferenced); + ipa_set_param_used (info, k, used); + } } for (k = 0; k < param_count; k++) { |