/* Variable tracking routines for the GNU compiler.
- Copyright (C) 2002-2019 Free Software Foundation, Inc.
+ Copyright (C) 2002-2022 Free Software Foundation, Inc.
This file is part of GCC.
#include "tree-dfa.h"
#include "tree-ssa.h"
#include "cselib.h"
-#include "params.h"
#include "tree-pretty-print.h"
#include "rtl-iter.h"
#include "fibonacci_heap.h"
#include "print-rtl.h"
+#include "function-abi.h"
typedef fibonacci_heap <long, basic_block_def> bb_heap_t;
-typedef fibonacci_node <long, basic_block_def> bb_heap_node_t;
/* var-tracking.c assumes that tree code with the same value as VALUE rtx code
has no chance to appear in REG_EXPR/MEM_EXPRs and isn't a decl.
int entryvals;
};
+/* Type for dependencies actively used when expand FROM into cur_loc. */
+typedef vec<loc_exp_dep, va_heap, vl_embed> deps_vec;
+
/* This data structure is allocated for one-part variables at the time
of emitting notes. */
struct onepart_aux
/* The depth of the cur_loc expression. */
expand_depth depth;
/* Dependencies actively used when expand FROM into cur_loc. */
- vec<loc_exp_dep, va_heap, vl_embed> deps;
+ deps_vec deps;
};
/* Structure describing one part of variable. */
: NULL)
#define VAR_LOC_FROM(var) (VAR_LOC_1PAUX (var)->from)
#define VAR_LOC_DEPTH(var) (VAR_LOC_1PAUX (var)->depth)
-#define VAR_LOC_DEP_VEC(var) (VAR_LOC_1PAUX (var) \
- ? &VAR_LOC_1PAUX (var)->deps \
- : NULL)
+#define VAR_LOC_DEP_VEC(var) var_loc_dep_vec (var)
+
+/* Implements the VAR_LOC_DEP_VEC above as a function to work around
+ a bogus -Wnonnull (PR c/95554). */
+static inline deps_vec*
+var_loc_dep_vec (variable *var)
+{
+ return VAR_LOC_1PAUX (var) ? &VAR_LOC_1PAUX (var)->deps : NULL;
+}
typedef unsigned int dvuid;
/* Data for adjust_mems callback. */
-struct adjust_mem_data
+class adjust_mem_data
{
+public:
bool store;
machine_mode mem_mode;
HOST_WIDE_INT stack_adjust;
static rtx
adjust_mems (rtx loc, const_rtx old_rtx, void *data)
{
- struct adjust_mem_data *amd = (struct adjust_mem_data *) data;
+ class adjust_mem_data *amd = (class adjust_mem_data *) data;
rtx mem, addr = loc, tem;
machine_mode mem_mode_save;
bool store_save;
amd.stack_adjust = -VTI (bb)->out.stack_adjust;
amd.store = true;
- note_stores (PATTERN (insn), adjust_mem_stores, &amd);
+ note_stores (insn, adjust_mem_stores, &amd);
amd.store = false;
if (GET_CODE (PATTERN (insn)) == PARALLEL
{
case PC:
case SCRATCH:
- case CC0:
case ASM_INPUT:
case ASM_OPERANDS:
return true;
{
unsigned int r;
hard_reg_set_iterator hrsi;
- HARD_REG_SET invalidated_regs;
- get_call_reg_set_usage (call_insn, &invalidated_regs,
- regs_invalidated_by_call);
+ HARD_REG_SET callee_clobbers
+ = insn_callee_abi (call_insn).full_reg_clobbers ();
- EXECUTE_IF_SET_IN_HARD_REG_SET (invalidated_regs, 0, r, hrsi)
+ EXECUTE_IF_SET_IN_HARD_REG_SET (callee_clobbers, 0, r, hrsi)
var_regno_delete (set, r);
if (MAY_HAVE_DEBUG_BIND_INSNS)
case DEBUG_EXPR:
case PC:
case SCRATCH:
- case CC0:
case ASM_INPUT:
case ASM_OPERANDS:
return true;
compile time for ridiculously complex expressions, although they're
seldom useful, and they may often have to be discarded as not
representable anyway. */
-#define EXPR_USE_DEPTH (PARAM_VALUE (PARAM_MAX_VARTRACK_EXPR_DEPTH))
+#define EXPR_USE_DEPTH (param_max_vartrack_expr_depth)
/* Attempt to reverse the EXPR operation in the debug info and record
it in the cselib table. Say for reg1 = reg2 + 6 even when reg2 is
&& (GET_CODE (l->loc) != CONST || !references_value_p (l->loc, 0)))
return;
/* Avoid creating too large locs lists. */
- else if (count == PARAM_VALUE (PARAM_MAX_VARTRACK_REVERSE_OP_SIZE))
+ else if (count == param_max_vartrack_reverse_op_size)
return;
switch (GET_CODE (src))
}
if (loc == stack_pointer_rtx
- && maybe_ne (hard_frame_pointer_adjustment, -1)
+ && (maybe_ne (hard_frame_pointer_adjustment, -1)
+ || (!frame_pointer_needed && !ACCUMULATE_OUTGOING_ARGS))
&& preserve)
cselib_set_value_sp_based (v);
+ /* Don't record MO_VAL_SET for VALUEs that can be described using
+ cfa_base_rtx or cfa_base_rtx + CONST_INT, cselib already knows
+ all the needed equivalences and they shouldn't change depending
+ on which register holds that VALUE in some instruction. */
+ if (!frame_pointer_needed
+ && cfa_base_rtx
+ && cselib_sp_derived_value_p (v)
+ && loc == stack_pointer_rtx)
+ {
+ if (preserve)
+ preserve_value (v);
+ return;
+ }
+
nloc = replace_expr_with_values (oloc);
if (nloc)
oloc = nloc;
&& targetm.calls.struct_value_rtx (type, 0) == 0)
{
tree struct_addr = build_pointer_type (TREE_TYPE (type));
- machine_mode mode = TYPE_MODE (struct_addr);
+ function_arg_info arg (struct_addr, /*named=*/true);
rtx reg;
INIT_CUMULATIVE_ARGS (args_so_far_v, type, NULL_RTX, fndecl,
nargs + 1);
- reg = targetm.calls.function_arg (args_so_far, mode,
- struct_addr, true);
- targetm.calls.function_arg_advance (args_so_far, mode,
- struct_addr, true);
+ reg = targetm.calls.function_arg (args_so_far, arg);
+ targetm.calls.function_arg_advance (args_so_far, arg);
if (reg == NULL_RTX)
{
for (; link; link = XEXP (link, 1))
nargs);
if (obj_type_ref && TYPE_ARG_TYPES (type) != void_list_node)
{
- machine_mode mode;
t = TYPE_ARG_TYPES (type);
- mode = TYPE_MODE (TREE_VALUE (t));
- this_arg = targetm.calls.function_arg (args_so_far, mode,
- TREE_VALUE (t), true);
+ function_arg_info arg (TREE_VALUE (t), /*named=*/true);
+ this_arg = targetm.calls.function_arg (args_so_far, arg);
if (this_arg && !REG_P (this_arg))
this_arg = NULL_RTX;
else if (this_arg == NULL_RTX)
if (!frame_pointer_needed)
{
- struct adjust_mem_data amd;
+ class adjust_mem_data amd;
amd.mem_mode = VOIDmode;
amd.stack_adjust = -VTI (bb)->out.stack_adjust;
amd.store = true;
}
if (t && t != void_list_node)
{
- tree argtype = TREE_VALUE (t);
- machine_mode mode = TYPE_MODE (argtype);
rtx reg;
- if (pass_by_reference (&args_so_far_v, mode, argtype, true))
- {
- argtype = build_pointer_type (argtype);
- mode = TYPE_MODE (argtype);
- }
- reg = targetm.calls.function_arg (args_so_far, mode,
- argtype, true);
- if (TREE_CODE (argtype) == REFERENCE_TYPE
- && INTEGRAL_TYPE_P (TREE_TYPE (argtype))
+ function_arg_info arg (TREE_VALUE (t), /*named=*/true);
+ apply_pass_by_reference_rules (&args_so_far_v, arg);
+ reg = targetm.calls.function_arg (args_so_far, arg);
+ if (TREE_CODE (arg.type) == REFERENCE_TYPE
+ && INTEGRAL_TYPE_P (TREE_TYPE (arg.type))
&& reg
&& REG_P (reg)
- && GET_MODE (reg) == mode
- && (GET_MODE_CLASS (mode) == MODE_INT
- || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT)
+ && GET_MODE (reg) == arg.mode
+ && (GET_MODE_CLASS (arg.mode) == MODE_INT
+ || GET_MODE_CLASS (arg.mode) == MODE_PARTIAL_INT)
&& REG_P (x)
&& REGNO (x) == REGNO (reg)
- && GET_MODE (x) == mode
+ && GET_MODE (x) == arg.mode
&& item)
{
machine_mode indmode
- = TYPE_MODE (TREE_TYPE (argtype));
+ = TYPE_MODE (TREE_TYPE (arg.type));
rtx mem = gen_rtx_MEM (indmode, x);
cselib_val *val = cselib_lookup (mem, indmode, 0, VOIDmode);
if (val && cselib_preserved_value_p (val))
}
}
}
- targetm.calls.function_arg_advance (args_so_far, mode,
- argtype, true);
+ targetm.calls.function_arg_advance (args_so_far, arg);
t = TREE_CHAIN (t);
}
}
insert notes before it without worrying about any
notes that MO_USEs might emit after the insn. */
cui.store_p = true;
- note_stores (PATTERN (insn), add_stores, &cui);
+ note_stores (insn, add_stores, &cui);
n2 = VTI (bb)->mos.length () - 1;
mos = VTI (bb)->mos.address ();
int *rc_order;
int i;
int htabsz = 0;
- int htabmax = PARAM_VALUE (PARAM_MAX_VARTRACK_SIZE);
+ int htabmax = param_max_vartrack_size;
bool success = true;
+ unsigned int n_blocks_processed = 0;
timevar_push (TV_VAR_TRACKING_DATAFLOW);
/* Compute reverse completion order of depth first search of the CFG
so that the data-flow runs faster. */
rc_order = XNEWVEC (int, n_basic_blocks_for_fn (cfun) - NUM_FIXED_BLOCKS);
bb_order = XNEWVEC (int, last_basic_block_for_fn (cfun));
- pre_and_rev_post_order_compute (NULL, rc_order, false);
- for (i = 0; i < n_basic_blocks_for_fn (cfun) - NUM_FIXED_BLOCKS; i++)
+ auto_bitmap exit_bbs;
+ bitmap_set_bit (exit_bbs, EXIT_BLOCK);
+ auto_vec<std::pair<int, int> > toplevel_scc_extents;
+ int n = rev_post_order_and_mark_dfs_back_seme
+ (cfun, single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun)), exit_bbs, true,
+ rc_order, &toplevel_scc_extents);
+ for (i = 0; i < n; i++)
bb_order[rc_order[i]] = i;
- free (rc_order);
- auto_sbitmap visited (last_basic_block_for_fn (cfun));
in_worklist = sbitmap_alloc (last_basic_block_for_fn (cfun));
in_pending = sbitmap_alloc (last_basic_block_for_fn (cfun));
bitmap_clear (in_worklist);
+ bitmap_clear (in_pending);
+
+ /* We're performing the dataflow iteration independently over the
+ toplevel SCCs plus leading non-cyclic entry blocks and separately
+ over the tail. That ensures best memory locality and the least
+ number of visited blocks. */
+ unsigned extent = 0;
+ int curr_start = -1;
+ int curr_end = -1;
+ do
+ {
+ curr_start = curr_end + 1;
+ if (toplevel_scc_extents.length () <= extent)
+ curr_end = n - 1;
+ else
+ curr_end = toplevel_scc_extents[extent++].second;
- FOR_EACH_BB_FN (bb, cfun)
- pending->insert (bb_order[bb->index], bb);
- bitmap_ones (in_pending);
-
- while (success && !pending->empty ())
- {
- std::swap (worklist, pending);
- std::swap (in_worklist, in_pending);
-
- bitmap_clear (visited);
+ for (int i = curr_start; i <= curr_end; ++i)
+ {
+ pending->insert (i, BASIC_BLOCK_FOR_FN (cfun, rc_order[i]));
+ bitmap_set_bit (in_pending, rc_order[i]);
+ }
- while (!worklist->empty ())
+ while (success && !pending->empty ())
{
- bb = worklist->extract_min ();
- bitmap_clear_bit (in_worklist, bb->index);
- gcc_assert (!bitmap_bit_p (visited, bb->index));
- if (!bitmap_bit_p (visited, bb->index))
+ std::swap (worklist, pending);
+ std::swap (in_worklist, in_pending);
+
+ while (!worklist->empty ())
{
bool changed;
edge_iterator ei;
int oldinsz, oldoutsz;
- bitmap_set_bit (visited, bb->index);
+ bb = worklist->extract_min ();
+ bitmap_clear_bit (in_worklist, bb->index);
if (VTI (bb)->in.vars)
{
- htabsz
- -= shared_hash_htab (VTI (bb)->in.vars)->size ()
- + shared_hash_htab (VTI (bb)->out.vars)->size ();
+ htabsz -= (shared_hash_htab (VTI (bb)->in.vars)->size ()
+ + shared_hash_htab (VTI (bb)->out.vars)->size ());
oldinsz = shared_hash_htab (VTI (bb)->in.vars)->elements ();
- oldoutsz
- = shared_hash_htab (VTI (bb)->out.vars)->elements ();
+ oldoutsz = shared_hash_htab (VTI (bb)->out.vars)->elements ();
}
else
oldinsz = oldoutsz = 0;
}
changed = compute_bb_dataflow (bb);
- htabsz += shared_hash_htab (VTI (bb)->in.vars)->size ()
- + shared_hash_htab (VTI (bb)->out.vars)->size ();
+ n_blocks_processed++;
+ htabsz += (shared_hash_htab (VTI (bb)->in.vars)->size ()
+ + shared_hash_htab (VTI (bb)->out.vars)->size ());
if (htabmax && htabsz > htabmax)
{
if (e->dest == EXIT_BLOCK_PTR_FOR_FN (cfun))
continue;
- if (bitmap_bit_p (visited, e->dest->index))
+ /* Iterate to an earlier block in RPO in the next
+ round, iterate to the same block immediately. */
+ if (bb_order[e->dest->index] < bb_order[bb->index])
{
+ gcc_assert (bb_order[e->dest->index] >= curr_start);
if (!bitmap_bit_p (in_pending, e->dest->index))
{
/* Send E->DEST to next round. */
e->dest);
}
}
- else if (!bitmap_bit_p (in_worklist, e->dest->index))
+ else if (bb_order[e->dest->index] <= curr_end
+ && !bitmap_bit_p (in_worklist, e->dest->index))
{
- /* Add E->DEST to current round. */
+ /* Add E->DEST to current round or delay
+ processing if it is in the next SCC. */
bitmap_set_bit (in_worklist, e->dest->index);
worklist->insert (bb_order[e->dest->index],
e->dest);
if (dump_file)
fprintf (dump_file,
- "BB %i: in %i (was %i), out %i (was %i), rem %i + %i, tsz %i\n",
- bb->index,
+ "BB %i: in %i (was %i), out %i (was %i), rem %i + %i, "
+ "tsz %i\n", bb->index,
(int)shared_hash_htab (VTI (bb)->in.vars)->size (),
oldinsz,
(int)shared_hash_htab (VTI (bb)->out.vars)->size (),
}
}
}
+ while (curr_end != n - 1);
+
+ statistics_counter_event (cfun, "compute_bb_dataflow times",
+ n_blocks_processed);
if (success && MAY_HAVE_DEBUG_BIND_INSNS)
FOR_EACH_BB_FN (bb, cfun)
gcc_assert (VTI (bb)->flooded);
+ free (rc_order);
free (bb_order);
delete worklist;
delete pending;
/* Structure for passing some other parameters to function
vt_expand_loc_callback. */
-struct expand_loc_callback_data
+class expand_loc_callback_data
{
+public:
/* The variables and values active at this point. */
variable_table_type *vars;
return;
allocsize = offsetof (struct onepart_aux, deps)
- + vec<loc_exp_dep, va_heap, vl_embed>::embedded_size (count);
+ + deps_vec::embedded_size (count);
if (VAR_LOC_1PAUX (var))
{
vt_expand_var_loc_chain (variable *var, bitmap regs, void *data,
bool *pendrecp)
{
- struct expand_loc_callback_data *elcd
- = (struct expand_loc_callback_data *) data;
+ class expand_loc_callback_data *elcd
+ = (class expand_loc_callback_data *) data;
location_chain *loc, *next;
rtx result = NULL;
int first_child, result_first_child, last_child;
int max_depth ATTRIBUTE_UNUSED,
void *data)
{
- struct expand_loc_callback_data *elcd
- = (struct expand_loc_callback_data *) data;
+ class expand_loc_callback_data *elcd
+ = (class expand_loc_callback_data *) data;
decl_or_value dv;
variable *var;
rtx result, subreg;
/* Invalid SUBREGs are ok in debug info. ??? We could try
alternate expansions for the VALUE as well. */
- if (!result)
+ if (!result && GET_MODE (subreg) != VOIDmode)
result = gen_rtx_raw_SUBREG (GET_MODE (x), subreg, SUBREG_BYTE (x));
return result;
static rtx
vt_expand_loc (rtx loc, variable_table_type *vars)
{
- struct expand_loc_callback_data data;
+ class expand_loc_callback_data data;
rtx result;
if (!MAY_HAVE_DEBUG_BIND_INSNS)
static rtx
vt_expand_1pvar (variable *var, variable_table_type *vars)
{
- struct expand_loc_callback_data data;
+ class expand_loc_callback_data data;
rtx loc;
gcc_checking_assert (var->onepart && var->n_var_parts == 1);
preserve_value (val);
if (reg != hard_frame_pointer_rtx && fixed_regs[REGNO (reg)])
cselib_preserve_cfa_base_value (val, REGNO (reg));
- expr = plus_constant (GET_MODE (stack_pointer_rtx),
- stack_pointer_rtx, -ofst);
- cselib_add_permanent_equiv (val, expr, get_insns ());
-
if (ofst)
{
- val = cselib_lookup_from_insn (stack_pointer_rtx,
- GET_MODE (stack_pointer_rtx), 1,
- VOIDmode, get_insns ());
- preserve_value (val);
+ cselib_val *valsp
+ = cselib_lookup_from_insn (stack_pointer_rtx,
+ GET_MODE (stack_pointer_rtx), 1,
+ VOIDmode, get_insns ());
+ preserve_value (valsp);
expr = plus_constant (GET_MODE (reg), reg, ofst);
- cselib_add_permanent_equiv (val, expr, get_insns ());
+ /* This cselib_add_permanent_equiv call needs to be done before
+ the other cselib_add_permanent_equiv a few lines later,
+ because after that one is done, cselib_lookup on this expr
+ will due to the cselib SP_DERIVED_VALUE_P optimizations
+ return valsp and so no permanent equivalency will be added. */
+ cselib_add_permanent_equiv (valsp, expr, get_insns ());
}
+
+ expr = plus_constant (GET_MODE (stack_pointer_rtx),
+ stack_pointer_rtx, -ofst);
+ cselib_add_permanent_equiv (val, expr, get_insns ());
}
/* In order to factor out the adjustments made to the stack pointer or to
vt_add_function_parameters ();
+ bool record_sp_value = false;
FOR_EACH_BB_FN (bb, cfun)
{
rtx_insn *insn;
- HOST_WIDE_INT pre, post = 0;
basic_block first_bb, last_bb;
if (MAY_HAVE_DEBUG_BIND_INSNS)
cselib_get_next_uid ());
}
+ if (MAY_HAVE_DEBUG_BIND_INSNS
+ && cfa_base_rtx
+ && !frame_pointer_needed
+ && record_sp_value)
+ cselib_record_sp_cfa_base_equiv (-cfa_base_offset
+ - VTI (bb)->in.stack_adjust,
+ BB_HEAD (bb));
+ record_sp_value = true;
+
first_bb = bb;
for (;;)
{
{
if (INSN_P (insn))
{
+ HOST_WIDE_INT pre = 0, post = 0;
+
if (!frame_pointer_needed)
{
insn_stack_adjust_offset_pre_post (insn, &pre, &post);
cselib_hook_called = false;
adjust_insn (bb, insn);
- if (!frame_pointer_needed && pre)
+ if (pre)
VTI (bb)->out.stack_adjust += pre;
if (DEBUG_MARKER_INSN_P (insn))
add_with_sets (insn, 0, 0);
cancel_changes (0);
- if (!frame_pointer_needed && post)
+ if (post)
{
micro_operation mo;
mo.type = MO_ADJUST;
return 0;
}
- mark_dfs_back_edges ();
if (!vt_initialize ())
{
vt_finalize ();