Commit 09dbcd96 by Alexandre Oliva Committed by Alexandre Oliva

re PR middle-end/49310 (Compile time hog in var-tracking emit)

PR debug/49310
* var-tracking.c (loc_exp_dep, onepart_aux): New structs.
(variable_part): Replace offset with union.
(enum onepart_enum, onepart_enum_t): New.
(variable_def): Drop cur_loc_changed, add onepart.
(value_chain_def, const_value_chain): Remove.
(VAR_PART_OFFSET, VAR_LOC_1PAUX): New macros, with checking.
(VAR_LOC_DEP_LST, VAR_LOC_DEP_LSTP): New macros.
(VAR_LOC_FROM, VAR_LOC_DEPTH, VAR_LOC_DEP_VEC): Likewise.
(value_chain_pool, value_chains): Remove.
(dropped_values): New.
(struct parm_reg): Only if HAVE_window_save.
(vt_stack_adjustments): Don't record register arguments.
(dv_as_rtx): New.
(dv_onepart_p): Return a onepart_enum_t.
(onepart_pool): New.
(dv_pool): Remove.
(dv_from_rtx): New.
(variable_htab_free): Release onepart aux data.  Reset flags.
(value_chain_htab_hash, value_chain_htab_eq): Remove.
(unshare_variable): Use onepart field.  Propagate onepart aux
data or offset.  Drop cur_loc_changed.
(val_store): Cope with NULL insn.  Rephrase dump output.  Check
for unsuitable locs.  Add FIXME on using cselib locs.
(val_reset): Remove FIXME of unfounded concerns.
(val_resolve): Check for unsuitable locs.  Add FIXME on using
cselib locs.
(variable_union): Use onepart field, adjust access to offset.
(NO_LOC_P): New.
(VALUE_CHANGED, DECL_CHANGED): Update doc.
(set_dv_changed): Clear NO_LOC_P when changed.
(find_loc_in_1pdv): Use onepart field.
(intersect_loc_chains): Likewise.
(unsuitable_loc): New.
(loc_cmp): Keep ENTRY_VALUEs at the end of the loc list.
(add_value_chain, add_value_chains): Remove.
(add_cselib_value_chains, remove_value_chain): Likewise.
(remove_value_chains, remove_cselib_value_chains): Likewise.
(canonicalize_loc_order_check): Use onepart.  Drop cur_loc_changed.
(canonicalize_values_star, canonicalize_vars_star): Use onepart.
(variable_merge_over_cur): Likewise.  Adjust access to offset.
Drop cur_loc_changed.
(variable_merge_over_src): Use onepart field.
(remove_duplicate_values): Likewise.
(variable_post_merge_new_vals): Likewise.
(find_mem_expr_in_1pdv): Likewise.
(dataflow_set_preserve_mem_locs): Likewise.  Drop cur_loc_changed
and value chains.
(dataflow_set_remove_mem_locs): Likewise.  Use VAR_LOC_FROM.
(variable_different_p): Use onepart field.  Move onepart test out
of the loop.
(argument_reg_set): Drop.
(add_uses, add_stores): Preserve but do not record in dynamic
tables equivalences for ENTRY_VALUEs and CFA_based addresses.
Avoid unsuitable address expressions.
(EXPR_DEPTH): Unlimit.
(EXPR_USE_DEPTH): Repurpose PARAM_MAX_VARTRACK_EXPR_DEPTH.
(prepare_call_arguments): Use DECL_RTL_IF_SET.
(dump_var): Adjust access to offset.
(variable_from_dropped, recover_dropped_1paux): New.
(variable_was_changed): Drop cur_loc_changed.  Use onepart.
Preserve onepart aux in empty_var.  Recover empty_var and onepart
aux from dropped_values.
(find_variable_location_part): Special-case onepart.  Adjust
access to offset.
(set_slot_part): Use onepart.  Drop cur_loc_changed.  Adjust
access to offset.  Initialize onepaux.  Drop value chains.
(delete_slot_part): Drop value chains.  Use VAR_LOC_FROM.
(VEC (variable, heap), VEC (rtx, stack)): Define.
(expand_loc_callback_data): Drop dummy, cur_loc_changed,
ignore_cur_loc.  Add expanding, pending, depth.
(loc_exp_dep_alloc, loc_exp_dep_clear): New.
(loc_exp_dep_insert, loc_exp_dep_set): New.
(notify_dependents_of_resolved_value): New.
(update_depth, vt_expand_var_loc_chain): New.
(vt_expand_loc_callback): Revamped.
(resolve_expansions_pending_recursion): New.
(INIT_ELCD, FINI_ELCD): New.
(vt_expand_loc): Use the new macros above.  Drop ignore_cur_loc
parameter, adjust all callers.
(vt_expand_loc_dummy): Drop.
(vt_expand_1pvar): New.
(emit_note_insn_var_location): Operate on non-debug decls only.
Revamp multi-part cur_loc recomputation and one-part expansion.
Drop cur_loc_changed.  Adjust access to offset.
(VEC (variable, heap)): Drop.
(changed_variables_stack, changed_values_stack): Drop.
(check_changed_vars_0, check_changed_vars_1): Remove.
(check_changed_vars_2, check_changed_vars_3): Remove.
(values_to_stack, remove_value_from_changed_variables): New.
(notify_dependents_of_changed_value, process_changed_values): New.
(emit_notes_for_changes): Revamp onepart updates.
(emit_notes_for_differences_1): Use onepart.  Drop cur_loc_changed
and value chains.  Propagate onepaux.  Recover empty_var and onepaux
from dropped_values.
(emit_notes_for_differences_2): Drop value chains.
(emit_notes_in_bb): Adjust.
(vt_emit_notes): Drop value chains, changed_variables_stack.
Initialize and release dropped_values.
(create_entry_value): Revamp.
(vt_add_function_parameter): Use new interface.
(note_register_arguments): Remove.
(vt_initialize): Drop value chains and register arguments.
(vt_finalize): Drop value chains.  Release windowed_parm_regs only
if HAVE_window_save.
* rtl.h: Document various pass-local uses of RTL flags.
* tree.h (DECL_RTL_KNOWN_SET): New.
* doc/invoke.texi (param max-vartrack-expr-depth): Update
description and default.

From-SVN: r180194
parent 2f47b8d3
2011-10-19 Alexandre Oliva <aoliva@redhat.com>
PR debug/49310
* var-tracking.c (loc_exp_dep, onepart_aux): New structs.
(variable_part): Replace offset with union.
(enum onepart_enum, onepart_enum_t): New.
(variable_def): Drop cur_loc_changed, add onepart.
(value_chain_def, const_value_chain): Remove.
(VAR_PART_OFFSET, VAR_LOC_1PAUX): New macros, with checking.
(VAR_LOC_DEP_LST, VAR_LOC_DEP_LSTP): New macros.
(VAR_LOC_FROM, VAR_LOC_DEPTH, VAR_LOC_DEP_VEC): Likewise.
(value_chain_pool, value_chains): Remove.
(dropped_values): New.
(struct parm_reg): Only if HAVE_window_save.
(vt_stack_adjustments): Don't record register arguments.
(dv_as_rtx): New.
(dv_onepart_p): Return a onepart_enum_t.
(onepart_pool): New.
(dv_pool): Remove.
(dv_from_rtx): New.
(variable_htab_free): Release onepart aux data. Reset flags.
(value_chain_htab_hash, value_chain_htab_eq): Remove.
(unshare_variable): Use onepart field. Propagate onepart aux
data or offset. Drop cur_loc_changed.
(val_store): Cope with NULL insn. Rephrase dump output. Check
for unsuitable locs. Add FIXME on using cselib locs.
(val_reset): Remove FIXME of unfounded concerns.
(val_resolve): Check for unsuitable locs. Add FIXME on using
cselib locs.
(variable_union): Use onepart field, adjust access to offset.
(NO_LOC_P): New.
(VALUE_CHANGED, DECL_CHANGED): Update doc.
(set_dv_changed): Clear NO_LOC_P when changed.
(find_loc_in_1pdv): Use onepart field.
(intersect_loc_chains): Likewise.
(unsuitable_loc): New.
(loc_cmp): Keep ENTRY_VALUEs at the end of the loc list.
(add_value_chain, add_value_chains): Remove.
(add_cselib_value_chains, remove_value_chain): Likewise.
(remove_value_chains, remove_cselib_value_chains): Likewise.
(canonicalize_loc_order_check): Use onepart. Drop cur_loc_changed.
(canonicalize_values_star, canonicalize_vars_star): Use onepart.
(variable_merge_over_cur): Likewise. Adjust access to offset.
Drop cur_loc_changed.
(variable_merge_over_src): Use onepart field.
(remove_duplicate_values): Likewise.
(variable_post_merge_new_vals): Likewise.
(find_mem_expr_in_1pdv): Likewise.
(dataflow_set_preserve_mem_locs): Likewise. Drop cur_loc_changed
and value chains.
(dataflow_set_remove_mem_locs): Likewise. Use VAR_LOC_FROM.
(variable_different_p): Use onepart field. Move onepart test out
of the loop.
(argument_reg_set): Drop.
(add_uses, add_stores): Preserve but do not record in dynamic
tables equivalences for ENTRY_VALUEs and CFA_based addresses.
Avoid unsuitable address expressions.
(EXPR_DEPTH): Unlimit.
(EXPR_USE_DEPTH): Repurpose PARAM_MAX_VARTRACK_EXPR_DEPTH.
(prepare_call_arguments): Use DECL_RTL_IF_SET.
(dump_var): Adjust access to offset.
(variable_from_dropped, recover_dropped_1paux): New.
(variable_was_changed): Drop cur_loc_changed. Use onepart.
Preserve onepart aux in empty_var. Recover empty_var and onepart
aux from dropped_values.
(find_variable_location_part): Special-case onepart. Adjust
access to offset.
(set_slot_part): Use onepart. Drop cur_loc_changed. Adjust
access to offset. Initialize onepaux. Drop value chains.
(delete_slot_part): Drop value chains. Use VAR_LOC_FROM.
(VEC (variable, heap), VEC (rtx, stack)): Define.
(expand_loc_callback_data): Drop dummy, cur_loc_changed,
ignore_cur_loc. Add expanding, pending, depth.
(loc_exp_dep_alloc, loc_exp_dep_clear): New.
(loc_exp_dep_insert, loc_exp_dep_set): New.
(notify_dependents_of_resolved_value): New.
(update_depth, vt_expand_var_loc_chain): New.
(vt_expand_loc_callback): Revamped.
(resolve_expansions_pending_recursion): New.
(INIT_ELCD, FINI_ELCD): New.
(vt_expand_loc): Use the new macros above. Drop ignore_cur_loc
parameter, adjust all callers.
(vt_expand_loc_dummy): Drop.
(vt_expand_1pvar): New.
(emit_note_insn_var_location): Operate on non-debug decls only.
Revamp multi-part cur_loc recomputation and one-part expansion.
Drop cur_loc_changed. Adjust access to offset.
(VEC (variable, heap)): Drop.
(changed_variables_stack, changed_values_stack): Drop.
(check_changed_vars_0, check_changed_vars_1): Remove.
(check_changed_vars_2, check_changed_vars_3): Remove.
(values_to_stack, remove_value_from_changed_variables): New.
(notify_dependents_of_changed_value, process_changed_values): New.
(emit_notes_for_changes): Revamp onepart updates.
(emit_notes_for_differences_1): Use onepart. Drop cur_loc_changed
and value chains. Propagate onepaux. Recover empty_var and onepaux
from dropped_values.
(emit_notes_for_differences_2): Drop value chains.
(emit_notes_in_bb): Adjust.
(vt_emit_notes): Drop value chains, changed_variables_stack.
Initialize and release dropped_values.
(create_entry_value): Revamp.
(vt_add_function_parameter): Use new interface.
(note_register_arguments): Remove.
(vt_initialize): Drop value chains and register arguments.
(vt_finalize): Drop value chains. Release windowed_parm_regs only
if HAVE_window_save.
* rtl.h: Document various pass-local uses of RTL flags.
* tree.h (DECL_RTL_KNOWN_SET): New.
* doc/invoke.texi (param max-vartrack-expr-depth): Update
description and default.
2011-10-19 Georg-Johann Lay <avr@gjlay.de> 2011-10-19 Georg-Johann Lay <avr@gjlay.de>
PR target/50447 PR target/50447
...@@ -9081,8 +9081,7 @@ compile time for more complete debug information. If this is set too ...@@ -9081,8 +9081,7 @@ compile time for more complete debug information. If this is set too
low, value expressions that are available and could be represented in low, value expressions that are available and could be represented in
debug information may end up not being used; setting this higher may debug information may end up not being used; setting this higher may
enable the compiler to find more complex debug expressions, but compile enable the compiler to find more complex debug expressions, but compile
time may grow exponentially, and even then, it may fail to find more time and memory use may grow. The default is 12.
usable expressions. The default is 10.
@item min-nondebug-insn-uid @item min-nondebug-insn-uid
Use uids starting at this parameter for nondebug insns. The range below Use uids starting at this parameter for nondebug insns. The range below
......
...@@ -265,7 +265,8 @@ struct GTY((chain_next ("RTX_NEXT (&%h)"), ...@@ -265,7 +265,8 @@ struct GTY((chain_next ("RTX_NEXT (&%h)"),
when we access a component. when we access a component.
1 in a CALL_INSN if it is a sibling call. 1 in a CALL_INSN if it is a sibling call.
1 in a SET that is for a return. 1 in a SET that is for a return.
In a CODE_LABEL, part of the two-bit alternate entry field. */ In a CODE_LABEL, part of the two-bit alternate entry field.
1 in a CONCAT is VAL_EXPR_IS_COPIED in var-tracking.c. */
unsigned int jump : 1; unsigned int jump : 1;
/* In a CODE_LABEL, part of the two-bit alternate entry field. /* In a CODE_LABEL, part of the two-bit alternate entry field.
1 in a MEM if it cannot trap. 1 in a MEM if it cannot trap.
...@@ -278,7 +279,9 @@ struct GTY((chain_next ("RTX_NEXT (&%h)"), ...@@ -278,7 +279,9 @@ struct GTY((chain_next ("RTX_NEXT (&%h)"),
constants pool. constants pool.
1 in a CALL_INSN logically equivalent to ECF_CONST and TREE_READONLY. 1 in a CALL_INSN logically equivalent to ECF_CONST and TREE_READONLY.
1 in a NOTE, or EXPR_LIST for a const call. 1 in a NOTE, or EXPR_LIST for a const call.
1 in a JUMP_INSN of an annulling branch. */ 1 in a JUMP_INSN of an annulling branch.
1 in a CONCAT is VAL_EXPR_IS_CLOBBERED in var-tracking.c.
1 in a preserved VALUE is PRESERVED_VALUE_P in cselib.c. */
unsigned int unchanging : 1; unsigned int unchanging : 1;
/* 1 in a MEM or ASM_OPERANDS expression if the memory reference is volatile. /* 1 in a MEM or ASM_OPERANDS expression if the memory reference is volatile.
1 in an INSN, CALL_INSN, JUMP_INSN, CODE_LABEL, BARRIER, or NOTE 1 in an INSN, CALL_INSN, JUMP_INSN, CODE_LABEL, BARRIER, or NOTE
...@@ -290,7 +293,8 @@ struct GTY((chain_next ("RTX_NEXT (&%h)"), ...@@ -290,7 +293,8 @@ struct GTY((chain_next ("RTX_NEXT (&%h)"),
non-local label. non-local label.
In a SYMBOL_REF, this flag is used for machine-specific purposes. In a SYMBOL_REF, this flag is used for machine-specific purposes.
In a PREFETCH, this flag indicates that it should be considered a scheduling In a PREFETCH, this flag indicates that it should be considered a scheduling
barrier. */ barrier.
1 in a CONCAT is VAL_NEEDS_RESOLUTION in var-tracking.c. */
unsigned int volatil : 1; unsigned int volatil : 1;
/* 1 in a MEM referring to a field of an aggregate. /* 1 in a MEM referring to a field of an aggregate.
0 if the MEM was a variable or the result of a * operator in C; 0 if the MEM was a variable or the result of a * operator in C;
...@@ -311,19 +315,24 @@ struct GTY((chain_next ("RTX_NEXT (&%h)"), ...@@ -311,19 +315,24 @@ struct GTY((chain_next ("RTX_NEXT (&%h)"),
In a REG, this is not needed for that purpose, and used instead In a REG, this is not needed for that purpose, and used instead
in `leaf_renumber_regs_insn'. in `leaf_renumber_regs_insn'.
1 in a SYMBOL_REF, means that emit_library_call 1 in a SYMBOL_REF, means that emit_library_call
has used it as the function. */ has used it as the function.
1 in a CONCAT is VAL_HOLDS_TRACK_EXPR in var-tracking.c.
1 in a VALUE or DEBUG_EXPR is VALUE_RECURSED_INTO in var-tracking.c. */
unsigned int used : 1; unsigned int used : 1;
/* 1 in an INSN or a SET if this rtx is related to the call frame, /* 1 in an INSN or a SET if this rtx is related to the call frame,
either changing how we compute the frame address or saving and either changing how we compute the frame address or saving and
restoring registers in the prologue and epilogue. restoring registers in the prologue and epilogue.
1 in a REG or MEM if it is a pointer. 1 in a REG or MEM if it is a pointer.
1 in a SYMBOL_REF if it addresses something in the per-function 1 in a SYMBOL_REF if it addresses something in the per-function
constant string pool. */ constant string pool.
1 in a VALUE is VALUE_CHANGED in var-tracking.c. */
unsigned frame_related : 1; unsigned frame_related : 1;
/* 1 in a REG or PARALLEL that is the current function's return value. /* 1 in a REG or PARALLEL that is the current function's return value.
1 in a MEM if it refers to a scalar. 1 in a MEM if it refers to a scalar.
1 in a SYMBOL_REF for a weak symbol. 1 in a SYMBOL_REF for a weak symbol.
1 in a CALL_INSN logically equivalent to ECF_PURE and DECL_PURE_P. */ 1 in a CALL_INSN logically equivalent to ECF_PURE and DECL_PURE_P.
1 in a CONCAT is VAL_EXPR_HAS_REVERSE in var-tracking.c.
1 in a VALUE or DEBUG_EXPR is NO_LOC_P in var-tracking.c. */
unsigned return_val : 1; unsigned return_val : 1;
/* The first element of the operands of this rtx. /* The first element of the operands of this rtx.
......
...@@ -2967,6 +2967,17 @@ extern void decl_value_expr_insert (tree, tree); ...@@ -2967,6 +2967,17 @@ extern void decl_value_expr_insert (tree, tree);
/* The DECL_RTL for NODE, if it is set, or NULL, if it is not set. */ /* The DECL_RTL for NODE, if it is set, or NULL, if it is not set. */
#define DECL_RTL_IF_SET(NODE) (DECL_RTL_SET_P (NODE) ? DECL_RTL (NODE) : NULL) #define DECL_RTL_IF_SET(NODE) (DECL_RTL_SET_P (NODE) ? DECL_RTL (NODE) : NULL)
#if (GCC_VERSION >= 2007)
#define DECL_RTL_KNOWN_SET(decl) __extension__ \
({ tree const __d = (decl); \
gcc_checking_assert (DECL_RTL_SET_P (__d)); \
/* Dereference it so the compiler knows it can't be NULL even \
without assertion checking. */ \
&*DECL_RTL_IF_SET (__d); })
#else
#define DECL_RTL_KNOWN_SET(decl) (&*DECL_RTL_IF_SET (decl))
#endif
/* In VAR_DECL and PARM_DECL nodes, nonzero means declared `register'. */ /* In VAR_DECL and PARM_DECL nodes, nonzero means declared `register'. */
#define DECL_REGISTER(NODE) (DECL_WRTL_CHECK (NODE)->decl_common.decl_flag_0) #define DECL_REGISTER(NODE) (DECL_WRTL_CHECK (NODE)->decl_common.decl_flag_0)
......
...@@ -301,6 +301,48 @@ typedef struct location_chain_def ...@@ -301,6 +301,48 @@ typedef struct location_chain_def
enum var_init_status init; enum var_init_status init;
} *location_chain; } *location_chain;
/* A vector of loc_exp_dep holds the active dependencies of a one-part
DV on VALUEs, i.e., the VALUEs expanded so as to form the current
location of DV. Each entry is also part of VALUE' s linked-list of
backlinks back to DV. */
typedef struct loc_exp_dep_s
{
/* The dependent DV. */
decl_or_value dv;
/* The dependency VALUE or DECL_DEBUG. */
rtx value;
/* The next entry in VALUE's backlinks list. */
struct loc_exp_dep_s *next;
/* A pointer to the pointer to this entry (head or prev's next) in
the doubly-linked list. */
struct loc_exp_dep_s **pprev;
} loc_exp_dep;
DEF_VEC_O (loc_exp_dep);
/* This data structure is allocated for one-part variables at the time
of emitting notes. */
struct onepart_aux
{
/* Doubly-linked list of dependent DVs. These are DVs whose cur_loc
computation used the expansion of this variable, and that ought
to be notified should this variable change. If the DV's cur_loc
expanded to NULL, all components of the loc list are regarded as
active, so that any changes in them give us a chance to get a
location. Otherwise, only components of the loc that expanded to
non-NULL are regarded as active dependencies. */
loc_exp_dep *backlinks;
/* This holds the LOC that was expanded into cur_loc. We need only
mark a one-part variable as changed if the FROM loc is removed,
or if it has no known location and a loc is added, or if it gets
a change notification from any of its active dependencies. */
rtx from;
/* The depth of the cur_loc expression. */
int depth;
/* Dependencies actively used when expand FROM into cur_loc. */
VEC (loc_exp_dep, none) deps;
};
/* Structure describing one part of variable. */ /* Structure describing one part of variable. */
typedef struct variable_part_def typedef struct variable_part_def
{ {
...@@ -310,13 +352,33 @@ typedef struct variable_part_def ...@@ -310,13 +352,33 @@ typedef struct variable_part_def
/* Location which was last emitted to location list. */ /* Location which was last emitted to location list. */
rtx cur_loc; rtx cur_loc;
/* The offset in the variable. */ union variable_aux
{
/* The offset in the variable, if !var->onepart. */
HOST_WIDE_INT offset; HOST_WIDE_INT offset;
/* Pointer to auxiliary data, if var->onepart and emit_notes. */
struct onepart_aux *onepaux;
} aux;
} variable_part; } variable_part;
/* Maximum number of location parts. */ /* Maximum number of location parts. */
#define MAX_VAR_PARTS 16 #define MAX_VAR_PARTS 16
/* Enumeration type used to discriminate various types of one-part
variables. */
typedef enum onepart_enum
{
/* Not a one-part variable. */
NOT_ONEPART = 0,
/* A one-part DECL that is not a DEBUG_EXPR_DECL. */
ONEPART_VDECL = 1,
/* A DEBUG_EXPR_DECL. */
ONEPART_DEXPR = 2,
/* A VALUE. */
ONEPART_VALUE = 3
} onepart_enum_t;
/* Structure describing where the variable is located. */ /* Structure describing where the variable is located. */
typedef struct variable_def typedef struct variable_def
{ {
...@@ -330,10 +392,8 @@ typedef struct variable_def ...@@ -330,10 +392,8 @@ typedef struct variable_def
/* Number of variable parts. */ /* Number of variable parts. */
char n_var_parts; char n_var_parts;
/* True if this variable changed (any of its) cur_loc fields /* What type of DV this is, according to enum onepart_enum. */
during the current emit_notes_for_changes resp. ENUM_BITFIELD (onepart_enum) onepart : CHAR_BIT;
emit_notes_for_differences call. */
bool cur_loc_changed;
/* True if this variable_def struct is currently in the /* True if this variable_def struct is currently in the
changed_variables hash table. */ changed_variables hash table. */
...@@ -344,29 +404,48 @@ typedef struct variable_def ...@@ -344,29 +404,48 @@ typedef struct variable_def
} *variable; } *variable;
typedef const struct variable_def *const_variable; typedef const struct variable_def *const_variable;
/* Structure for chaining backlinks from referenced VALUEs to
DVs that are referencing them. */
typedef struct value_chain_def
{
/* Next value_chain entry. */
struct value_chain_def *next;
/* The declaration of the variable, or an RTL value
being handled like a declaration, whose var_parts[0].loc_chain
references the VALUE owning this value_chain. */
decl_or_value dv;
/* Reference count. */
int refcount;
} *value_chain;
typedef const struct value_chain_def *const_value_chain;
/* Pointer to the BB's information specific to variable tracking pass. */ /* Pointer to the BB's information specific to variable tracking pass. */
#define VTI(BB) ((variable_tracking_info) (BB)->aux) #define VTI(BB) ((variable_tracking_info) (BB)->aux)
/* Macro to access MEM_OFFSET as an HOST_WIDE_INT. Evaluates MEM twice. */ /* Macro to access MEM_OFFSET as an HOST_WIDE_INT. Evaluates MEM twice. */
#define INT_MEM_OFFSET(mem) (MEM_OFFSET_KNOWN_P (mem) ? MEM_OFFSET (mem) : 0) #define INT_MEM_OFFSET(mem) (MEM_OFFSET_KNOWN_P (mem) ? MEM_OFFSET (mem) : 0)
#if ENABLE_CHECKING && (GCC_VERSION >= 2007)
/* Access VAR's Ith part's offset, checking that it's not a one-part
variable. */
#define VAR_PART_OFFSET(var, i) __extension__ \
(*({ variable const __v = (var); \
gcc_checking_assert (!__v->onepart); \
&__v->var_part[(i)].aux.offset; }))
/* Access VAR's one-part auxiliary data, checking that it is a
one-part variable. */
#define VAR_LOC_1PAUX(var) __extension__ \
(*({ variable const __v = (var); \
gcc_checking_assert (__v->onepart); \
&__v->var_part[0].aux.onepaux; }))
#else
#define VAR_PART_OFFSET(var, i) ((var)->var_part[(i)].aux.offset)
#define VAR_LOC_1PAUX(var) ((var)->var_part[0].aux.onepaux)
#endif
/* These are accessor macros for the one-part auxiliary data. When
convenient for users, they're guarded by tests that the data was
allocated. */
#define VAR_LOC_DEP_LST(var) (VAR_LOC_1PAUX (var) \
? VAR_LOC_1PAUX (var)->backlinks \
: NULL)
#define VAR_LOC_DEP_LSTP(var) (VAR_LOC_1PAUX (var) \
? &VAR_LOC_1PAUX (var)->backlinks \
: 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)
/* Alloc pool for struct attrs_def. */ /* Alloc pool for struct attrs_def. */
static alloc_pool attrs_pool; static alloc_pool attrs_pool;
...@@ -382,24 +461,24 @@ static alloc_pool loc_chain_pool; ...@@ -382,24 +461,24 @@ static alloc_pool loc_chain_pool;
/* Alloc pool for struct shared_hash_def. */ /* Alloc pool for struct shared_hash_def. */
static alloc_pool shared_hash_pool; static alloc_pool shared_hash_pool;
/* Alloc pool for struct value_chain_def. */
static alloc_pool value_chain_pool;
/* Changed variables, notes will be emitted for them. */ /* Changed variables, notes will be emitted for them. */
static htab_t changed_variables; static htab_t changed_variables;
/* Links from VALUEs to DVs referencing them in their current loc_chains. */
static htab_t value_chains;
/* Shall notes be emitted? */ /* Shall notes be emitted? */
static bool emit_notes; static bool emit_notes;
/* Values whose dynamic location lists have gone empty, but whose
cselib location lists are still usable. Use this to hold the
current location, the backlinks, etc, during emit_notes. */
static htab_t dropped_values;
/* Empty shared hashtable. */ /* Empty shared hashtable. */
static shared_hash empty_shared_hash; static shared_hash empty_shared_hash;
/* Scratch register bitmap used by cselib_expand_value_rtx. */ /* Scratch register bitmap used by cselib_expand_value_rtx. */
static bitmap scratch_regs = NULL; static bitmap scratch_regs = NULL;
#ifdef HAVE_window_save
typedef struct GTY(()) parm_reg { typedef struct GTY(()) parm_reg {
rtx outgoing; rtx outgoing;
rtx incoming; rtx incoming;
...@@ -410,6 +489,7 @@ DEF_VEC_ALLOC_O(parm_reg_t, gc); ...@@ -410,6 +489,7 @@ DEF_VEC_ALLOC_O(parm_reg_t, gc);
/* Vector of windowed parameter registers, if any. */ /* Vector of windowed parameter registers, if any. */
static VEC(parm_reg_t, gc) *windowed_parm_regs = NULL; static VEC(parm_reg_t, gc) *windowed_parm_regs = NULL;
#endif
/* Variable used to tell whether cselib_process_insn called our hook. */ /* Variable used to tell whether cselib_process_insn called our hook. */
static bool cselib_hook_called; static bool cselib_hook_called;
...@@ -420,7 +500,6 @@ static void stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *, ...@@ -420,7 +500,6 @@ static void stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *,
static void insn_stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *, static void insn_stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *,
HOST_WIDE_INT *); HOST_WIDE_INT *);
static bool vt_stack_adjustments (void); static bool vt_stack_adjustments (void);
static void note_register_arguments (rtx);
static hashval_t variable_htab_hash (const void *); static hashval_t variable_htab_hash (const void *);
static int variable_htab_eq (const void *, const void *); static int variable_htab_eq (const void *, const void *);
static void variable_htab_free (void *); static void variable_htab_free (void *);
...@@ -476,6 +555,7 @@ static void dump_vars (htab_t); ...@@ -476,6 +555,7 @@ static void dump_vars (htab_t);
static void dump_dataflow_set (dataflow_set *); static void dump_dataflow_set (dataflow_set *);
static void dump_dataflow_sets (void); static void dump_dataflow_sets (void);
static void set_dv_changed (decl_or_value, bool);
static void variable_was_changed (variable, dataflow_set *); static void variable_was_changed (variable, dataflow_set *);
static void **set_slot_part (dataflow_set *, rtx, void **, static void **set_slot_part (dataflow_set *, rtx, void **,
decl_or_value, HOST_WIDE_INT, decl_or_value, HOST_WIDE_INT,
...@@ -672,15 +752,11 @@ vt_stack_adjustments (void) ...@@ -672,15 +752,11 @@ vt_stack_adjustments (void)
for (insn = BB_HEAD (dest); for (insn = BB_HEAD (dest);
insn != NEXT_INSN (BB_END (dest)); insn != NEXT_INSN (BB_END (dest));
insn = NEXT_INSN (insn)) insn = NEXT_INSN (insn))
{
if (INSN_P (insn)) if (INSN_P (insn))
{ {
insn_stack_adjust_offset_pre_post (insn, &pre, &post); insn_stack_adjust_offset_pre_post (insn, &pre, &post);
offset += pre + post; offset += pre + post;
} }
if (CALL_P (insn))
note_register_arguments (insn);
}
VTI (dest)->out.stack_adjust = offset; VTI (dest)->out.stack_adjust = offset;
...@@ -1133,6 +1209,21 @@ dv_as_value (decl_or_value dv) ...@@ -1133,6 +1209,21 @@ dv_as_value (decl_or_value dv)
return (rtx)dv; return (rtx)dv;
} }
/* Return the DEBUG_EXPR of a DEBUG_EXPR_DECL or the VALUE in DV. */
static inline rtx
dv_as_rtx (decl_or_value dv)
{
tree decl;
if (dv_is_value_p (dv))
return dv_as_value (dv);
decl = dv_as_decl (dv);
gcc_checking_assert (TREE_CODE (decl) == DEBUG_EXPR_DECL);
return DECL_RTL_KNOWN_SET (decl);
}
/* Return the opaque pointer in the decl_or_value. */ /* Return the opaque pointer in the decl_or_value. */
static inline void * static inline void *
dv_as_opaque (decl_or_value dv) dv_as_opaque (decl_or_value dv)
...@@ -1140,36 +1231,36 @@ dv_as_opaque (decl_or_value dv) ...@@ -1140,36 +1231,36 @@ dv_as_opaque (decl_or_value dv)
return dv; return dv;
} }
/* Return true if a decl_or_value must not have more than one variable /* Return nonzero if a decl_or_value must not have more than one
part. */ variable part. The returned value discriminates among various
static inline bool kinds of one-part DVs ccording to enum onepart_enum. */
static inline onepart_enum_t
dv_onepart_p (decl_or_value dv) dv_onepart_p (decl_or_value dv)
{ {
tree decl; tree decl;
if (!MAY_HAVE_DEBUG_INSNS) if (!MAY_HAVE_DEBUG_INSNS)
return false; return NOT_ONEPART;
if (dv_is_value_p (dv)) if (dv_is_value_p (dv))
return true; return ONEPART_VALUE;
decl = dv_as_decl (dv); decl = dv_as_decl (dv);
if (!decl)
return true;
if (TREE_CODE (decl) == DEBUG_EXPR_DECL) if (TREE_CODE (decl) == DEBUG_EXPR_DECL)
return true; return ONEPART_DEXPR;
return (target_for_debug_bind (decl) != NULL_TREE); if (target_for_debug_bind (decl) != NULL_TREE)
return ONEPART_VDECL;
return NOT_ONEPART;
} }
/* Return the variable pool to be used for dv, depending on whether it /* Return the variable pool to be used for a dv of type ONEPART. */
can have multiple parts or not. */
static inline alloc_pool static inline alloc_pool
dv_pool (decl_or_value dv) onepart_pool (onepart_enum_t onepart)
{ {
return dv_onepart_p (dv) ? valvar_pool : var_pool; return onepart ? valvar_pool : var_pool;
} }
/* Build a decl_or_value out of a decl. */ /* Build a decl_or_value out of a decl. */
...@@ -1192,6 +1283,30 @@ dv_from_value (rtx value) ...@@ -1192,6 +1283,30 @@ dv_from_value (rtx value)
return dv; return dv;
} }
/* Return a value or the decl of a debug_expr as a decl_or_value. */
static inline decl_or_value
dv_from_rtx (rtx x)
{
decl_or_value dv;
switch (GET_CODE (x))
{
case DEBUG_EXPR:
dv = dv_from_decl (DEBUG_EXPR_TREE_DECL (x));
gcc_checking_assert (DECL_RTL_KNOWN_SET (DEBUG_EXPR_TREE_DECL (x)) == x);
break;
case VALUE:
dv = dv_from_value (x);
break;
default:
gcc_unreachable ();
}
return dv;
}
extern void debug_dv (decl_or_value dv); extern void debug_dv (decl_or_value dv);
DEBUG_FUNCTION void DEBUG_FUNCTION void
...@@ -1254,6 +1369,8 @@ variable_htab_eq (const void *x, const void *y) ...@@ -1254,6 +1369,8 @@ variable_htab_eq (const void *x, const void *y)
return (dv_as_opaque (v->dv) == dv_as_opaque (dv)); return (dv_as_opaque (v->dv) == dv_as_opaque (dv));
} }
static void loc_exp_dep_clear (variable var);
/* Free the element of VARIABLE_HTAB (its type is struct variable_def). */ /* Free the element of VARIABLE_HTAB (its type is struct variable_def). */
static void static void
...@@ -1278,29 +1395,18 @@ variable_htab_free (void *elem) ...@@ -1278,29 +1395,18 @@ variable_htab_free (void *elem)
} }
var->var_part[i].loc_chain = NULL; var->var_part[i].loc_chain = NULL;
} }
pool_free (dv_pool (var->dv), var); if (var->onepart && VAR_LOC_1PAUX (var))
} {
loc_exp_dep_clear (var);
/* The hash function for value_chains htab, computes the hash value if (VAR_LOC_DEP_LST (var))
from the VALUE. */ VAR_LOC_DEP_LST (var)->pprev = NULL;
XDELETE (VAR_LOC_1PAUX (var));
static hashval_t /* These may be reused across functions, so reset
value_chain_htab_hash (const void *x) e.g. NO_LOC_P. */
{ if (var->onepart == ONEPART_DEXPR)
const_value_chain const v = (const_value_chain) x; set_dv_changed (var->dv, true);
}
return dv_htab_hash (v->dv); pool_free (onepart_pool (var->onepart), var);
}
/* Compare the VALUE X with VALUE Y. */
static int
value_chain_htab_eq (const void *x, const void *y)
{
const_value_chain const v = (const_value_chain) x;
decl_or_value dv = CONST_CAST2 (decl_or_value, const void *, y);
return dv_as_opaque (v->dv) == dv_as_opaque (dv);
} }
/* Initialize the set (array) SET of attrs to empty lists. */ /* Initialize the set (array) SET of attrs to empty lists. */
...@@ -1569,13 +1675,12 @@ unshare_variable (dataflow_set *set, void **slot, variable var, ...@@ -1569,13 +1675,12 @@ unshare_variable (dataflow_set *set, void **slot, variable var,
variable new_var; variable new_var;
int i; int i;
new_var = (variable) pool_alloc (dv_pool (var->dv)); new_var = (variable) pool_alloc (onepart_pool (var->onepart));
new_var->dv = var->dv; new_var->dv = var->dv;
new_var->refcount = 1; new_var->refcount = 1;
var->refcount--; var->refcount--;
new_var->n_var_parts = var->n_var_parts; new_var->n_var_parts = var->n_var_parts;
new_var->cur_loc_changed = var->cur_loc_changed; new_var->onepart = var->onepart;
var->cur_loc_changed = false;
new_var->in_changed_variables = false; new_var->in_changed_variables = false;
if (! flag_var_tracking_uninit) if (! flag_var_tracking_uninit)
...@@ -1586,7 +1691,18 @@ unshare_variable (dataflow_set *set, void **slot, variable var, ...@@ -1586,7 +1691,18 @@ unshare_variable (dataflow_set *set, void **slot, variable var,
location_chain node; location_chain node;
location_chain *nextp; location_chain *nextp;
new_var->var_part[i].offset = var->var_part[i].offset; if (i == 0 && var->onepart)
{
/* One-part auxiliary data is only used while emitting
notes, so propagate it to the new variable in the active
dataflow set. If we're not emitting notes, this will be
a no-op. */
gcc_checking_assert (!VAR_LOC_1PAUX (var) || emit_notes);
VAR_LOC_1PAUX (new_var) = VAR_LOC_1PAUX (var);
VAR_LOC_1PAUX (var) = NULL;
}
else
VAR_PART_OFFSET (new_var, i) = VAR_PART_OFFSET (var, i);
nextp = &new_var->var_part[i].loc_chain; nextp = &new_var->var_part[i].loc_chain;
for (node = var->var_part[i].loc_chain; node; node = node->next) for (node = var->var_part[i].loc_chain; node; node = node->next)
{ {
...@@ -1891,6 +2007,26 @@ var_mem_delete (dataflow_set *set, rtx loc, bool clobber) ...@@ -1891,6 +2007,26 @@ var_mem_delete (dataflow_set *set, rtx loc, bool clobber)
delete_variable_part (set, loc, dv_from_decl (decl), offset); delete_variable_part (set, loc, dv_from_decl (decl), offset);
} }
/* Return true if LOC should not be expanded for location expressions,
or used in them. */
static inline bool
unsuitable_loc (rtx loc)
{
switch (GET_CODE (loc))
{
case PC:
case SCRATCH:
case CC0:
case ASM_INPUT:
case ASM_OPERANDS:
return true;
default:
return false;
}
}
/* Bind a value to a location it was just stored in. If MODIFIED /* Bind a value to a location it was just stored in. If MODIFIED
holds, assume the location was modified, detaching it from any holds, assume the location was modified, detaching it from any
values bound to it. */ values bound to it. */
...@@ -1904,10 +2040,10 @@ val_store (dataflow_set *set, rtx val, rtx loc, rtx insn, bool modified) ...@@ -1904,10 +2040,10 @@ val_store (dataflow_set *set, rtx val, rtx loc, rtx insn, bool modified)
if (dump_file) if (dump_file)
{ {
fprintf (dump_file, "%i: ", INSN_UID (insn)); fprintf (dump_file, "%i: ", insn ? INSN_UID (insn) : 0);
print_inline_rtx (dump_file, val, 0);
fprintf (dump_file, " stored in ");
print_inline_rtx (dump_file, loc, 0); print_inline_rtx (dump_file, loc, 0);
fprintf (dump_file, " evaluates to ");
print_inline_rtx (dump_file, val, 0);
if (v->locs) if (v->locs)
{ {
struct elt_loc_list *l; struct elt_loc_list *l;
...@@ -1920,6 +2056,8 @@ val_store (dataflow_set *set, rtx val, rtx loc, rtx insn, bool modified) ...@@ -1920,6 +2056,8 @@ val_store (dataflow_set *set, rtx val, rtx loc, rtx insn, bool modified)
fprintf (dump_file, "\n"); fprintf (dump_file, "\n");
} }
gcc_checking_assert (!unsuitable_loc (loc));
if (REG_P (loc)) if (REG_P (loc))
{ {
if (modified) if (modified)
...@@ -1931,6 +2069,8 @@ val_store (dataflow_set *set, rtx val, rtx loc, rtx insn, bool modified) ...@@ -1931,6 +2069,8 @@ val_store (dataflow_set *set, rtx val, rtx loc, rtx insn, bool modified)
var_mem_decl_set (set, loc, VAR_INIT_STATUS_INITIALIZED, var_mem_decl_set (set, loc, VAR_INIT_STATUS_INITIALIZED,
dv_from_value (val), 0, NULL_RTX, INSERT); dv_from_value (val), 0, NULL_RTX, INSERT);
else else
/* ??? Ideally we wouldn't get these, and use them from the static
cselib loc list. */
set_variable_part (set, loc, dv_from_value (val), 0, set_variable_part (set, loc, dv_from_value (val), 0,
VAR_INIT_STATUS_INITIALIZED, NULL_RTX, INSERT); VAR_INIT_STATUS_INITIALIZED, NULL_RTX, INSERT);
} }
...@@ -1997,13 +2137,6 @@ val_reset (dataflow_set *set, decl_or_value dv) ...@@ -1997,13 +2137,6 @@ val_reset (dataflow_set *set, decl_or_value dv)
delete_variable_part (set, dv_as_value (dv), dv_from_value (cval), 0); delete_variable_part (set, dv_as_value (dv), dv_from_value (cval), 0);
clobber_variable_part (set, NULL, dv, 0, NULL); clobber_variable_part (set, NULL, dv, 0, NULL);
/* ??? Should we make sure there aren't other available values or
variables whose values involve this one other than by
equivalence? E.g., at the very least we should reset MEMs, those
shouldn't be too hard to find cselib-looking up the value as an
address, then locating the resulting value in our own hash
table. */
} }
/* Find the values in a given location and map the val to another /* Find the values in a given location and map the val to another
...@@ -2029,6 +2162,8 @@ val_resolve (dataflow_set *set, rtx val, rtx loc, rtx insn) ...@@ -2029,6 +2162,8 @@ val_resolve (dataflow_set *set, rtx val, rtx loc, rtx insn)
val_reset (set, dv); val_reset (set, dv);
gcc_checking_assert (!unsuitable_loc (loc));
if (REG_P (loc)) if (REG_P (loc))
{ {
attrs node, found = NULL; attrs node, found = NULL;
...@@ -2061,6 +2196,8 @@ val_resolve (dataflow_set *set, rtx val, rtx loc, rtx insn) ...@@ -2061,6 +2196,8 @@ val_resolve (dataflow_set *set, rtx val, rtx loc, rtx insn)
var_mem_decl_set (set, loc, VAR_INIT_STATUS_INITIALIZED, var_mem_decl_set (set, loc, VAR_INIT_STATUS_INITIALIZED,
dv_from_value (val), 0, NULL_RTX, INSERT); dv_from_value (val), 0, NULL_RTX, INSERT);
else else
/* ??? Ideally we wouldn't get these, and use them from the static
cselib loc list. */
/* ??? Merge equivalent expressions. */ /* ??? Merge equivalent expressions. */
set_variable_part (set, loc, dv_from_value (val), 0, set_variable_part (set, loc, dv_from_value (val), 0,
VAR_INIT_STATUS_INITIALIZED, NULL_RTX, INSERT); VAR_INIT_STATUS_INITIALIZED, NULL_RTX, INSERT);
...@@ -2176,10 +2313,11 @@ variable_union (variable src, dataflow_set *set) ...@@ -2176,10 +2313,11 @@ variable_union (variable src, dataflow_set *set)
dst = (variable) *dstp; dst = (variable) *dstp;
gcc_assert (src->n_var_parts); gcc_assert (src->n_var_parts);
gcc_checking_assert (src->onepart == dst->onepart);
/* We can combine one-part variables very efficiently, because their /* We can combine one-part variables very efficiently, because their
entries are in canonical order. */ entries are in canonical order. */
if (dv_onepart_p (src->dv)) if (src->onepart)
{ {
location_chain *nodep, dnode, snode; location_chain *nodep, dnode, snode;
...@@ -2233,16 +2371,18 @@ variable_union (variable src, dataflow_set *set) ...@@ -2233,16 +2371,18 @@ variable_union (variable src, dataflow_set *set)
return 1; return 1;
} }
gcc_checking_assert (!src->onepart);
/* Count the number of location parts, result is K. */ /* Count the number of location parts, result is K. */
for (i = 0, j = 0, k = 0; for (i = 0, j = 0, k = 0;
i < src->n_var_parts && j < dst->n_var_parts; k++) i < src->n_var_parts && j < dst->n_var_parts; k++)
{ {
if (src->var_part[i].offset == dst->var_part[j].offset) if (VAR_PART_OFFSET (src, i) == VAR_PART_OFFSET (dst, j))
{ {
i++; i++;
j++; j++;
} }
else if (src->var_part[i].offset < dst->var_part[j].offset) else if (VAR_PART_OFFSET (src, i) < VAR_PART_OFFSET (dst, j))
i++; i++;
else else
j++; j++;
...@@ -2252,7 +2392,7 @@ variable_union (variable src, dataflow_set *set) ...@@ -2252,7 +2392,7 @@ variable_union (variable src, dataflow_set *set)
/* We track only variables whose size is <= MAX_VAR_PARTS bytes /* We track only variables whose size is <= MAX_VAR_PARTS bytes
thus there are at most MAX_VAR_PARTS different offsets. */ thus there are at most MAX_VAR_PARTS different offsets. */
gcc_assert (dv_onepart_p (dst->dv) ? k == 1 : k <= MAX_VAR_PARTS); gcc_checking_assert (dst->onepart ? k == 1 : k <= MAX_VAR_PARTS);
if (dst->n_var_parts != k && shared_var_p (dst, set->vars)) if (dst->n_var_parts != k && shared_var_p (dst, set->vars))
{ {
...@@ -2269,7 +2409,7 @@ variable_union (variable src, dataflow_set *set) ...@@ -2269,7 +2409,7 @@ variable_union (variable src, dataflow_set *set)
location_chain node, node2; location_chain node, node2;
if (i >= 0 && j >= 0 if (i >= 0 && j >= 0
&& src->var_part[i].offset == dst->var_part[j].offset) && VAR_PART_OFFSET (src, i) == VAR_PART_OFFSET (dst, j))
{ {
/* Compute the "sorted" union of the chains, i.e. the locations which /* Compute the "sorted" union of the chains, i.e. the locations which
are in both chains go first, they are sorted by the sum of are in both chains go first, they are sorted by the sum of
...@@ -2317,7 +2457,7 @@ variable_union (variable src, dataflow_set *set) ...@@ -2317,7 +2457,7 @@ variable_union (variable src, dataflow_set *set)
/* The most common case, much simpler, no qsort is needed. */ /* The most common case, much simpler, no qsort is needed. */
location_chain dstnode = dst->var_part[j].loc_chain; location_chain dstnode = dst->var_part[j].loc_chain;
dst->var_part[k].loc_chain = dstnode; dst->var_part[k].loc_chain = dstnode;
dst->var_part[k].offset = dst->var_part[j].offset; VAR_PART_OFFSET (dst, k) = VAR_PART_OFFSET(dst, j);
node2 = dstnode; node2 = dstnode;
for (node = src->var_part[i].loc_chain; node; node = node->next) for (node = src->var_part[i].loc_chain; node; node = node->next)
if (!((REG_P (dstnode->loc) if (!((REG_P (dstnode->loc)
...@@ -2455,20 +2595,20 @@ variable_union (variable src, dataflow_set *set) ...@@ -2455,20 +2595,20 @@ variable_union (variable src, dataflow_set *set)
dst->var_part[k].loc_chain = vui[0].lc; dst->var_part[k].loc_chain = vui[0].lc;
} }
dst->var_part[k].offset = dst->var_part[j].offset; VAR_PART_OFFSET (dst, k) = VAR_PART_OFFSET (dst, j);
} }
i--; i--;
j--; j--;
} }
else if ((i >= 0 && j >= 0 else if ((i >= 0 && j >= 0
&& src->var_part[i].offset < dst->var_part[j].offset) && VAR_PART_OFFSET (src, i) < VAR_PART_OFFSET (dst, j))
|| i < 0) || i < 0)
{ {
dst->var_part[k] = dst->var_part[j]; dst->var_part[k] = dst->var_part[j];
j--; j--;
} }
else if ((i >= 0 && j >= 0 else if ((i >= 0 && j >= 0
&& src->var_part[i].offset > dst->var_part[j].offset) && VAR_PART_OFFSET (src, i) > VAR_PART_OFFSET (dst, j))
|| j < 0) || j < 0)
{ {
location_chain *nextp; location_chain *nextp;
...@@ -2492,7 +2632,7 @@ variable_union (variable src, dataflow_set *set) ...@@ -2492,7 +2632,7 @@ variable_union (variable src, dataflow_set *set)
nextp = &new_lc->next; nextp = &new_lc->next;
} }
dst->var_part[k].offset = src->var_part[i].offset; VAR_PART_OFFSET (dst, k) = VAR_PART_OFFSET (src, i);
i--; i--;
} }
dst->var_part[k].cur_loc = NULL; dst->var_part[k].cur_loc = NULL;
...@@ -2543,25 +2683,46 @@ dataflow_set_union (dataflow_set *dst, dataflow_set *src) ...@@ -2543,25 +2683,46 @@ dataflow_set_union (dataflow_set *dst, dataflow_set *src)
/* Whether the value is currently being expanded. */ /* Whether the value is currently being expanded. */
#define VALUE_RECURSED_INTO(x) \ #define VALUE_RECURSED_INTO(x) \
(RTL_FLAG_CHECK2 ("VALUE_RECURSED_INTO", (x), VALUE, DEBUG_EXPR)->used) (RTL_FLAG_CHECK2 ("VALUE_RECURSED_INTO", (x), VALUE, DEBUG_EXPR)->used)
/* Whether the value is in changed_variables hash table. */
/* Whether no expansion was found, saving useless lookups.
It must only be set when VALUE_CHANGED is clear. */
#define NO_LOC_P(x) \
(RTL_FLAG_CHECK2 ("NO_LOC_P", (x), VALUE, DEBUG_EXPR)->return_val)
/* Whether cur_loc in the value needs to be (re)computed. */
#define VALUE_CHANGED(x) \ #define VALUE_CHANGED(x) \
(RTL_FLAG_CHECK1 ("VALUE_CHANGED", (x), VALUE)->frame_related) (RTL_FLAG_CHECK1 ("VALUE_CHANGED", (x), VALUE)->frame_related)
/* Whether the decl is in changed_variables hash table. */ /* Whether cur_loc in the decl needs to be (re)computed. */
#define DECL_CHANGED(x) TREE_VISITED (x) #define DECL_CHANGED(x) TREE_VISITED (x)
/* Record that DV has been added into resp. removed from changed_variables /* Record (if NEWV) that DV needs to have its cur_loc recomputed. For
hashtable. */ user DECLs, this means they're in changed_variables. Values and
debug exprs may be left with this flag set if no user variable
requires them to be evaluated. */
static inline void static inline void
set_dv_changed (decl_or_value dv, bool newv) set_dv_changed (decl_or_value dv, bool newv)
{ {
if (dv_is_value_p (dv)) switch (dv_onepart_p (dv))
{
case ONEPART_VALUE:
if (newv)
NO_LOC_P (dv_as_value (dv)) = false;
VALUE_CHANGED (dv_as_value (dv)) = newv; VALUE_CHANGED (dv_as_value (dv)) = newv;
else break;
case ONEPART_DEXPR:
if (newv)
NO_LOC_P (DECL_RTL_KNOWN_SET (dv_as_decl (dv))) = false;
/* Fall through... */
default:
DECL_CHANGED (dv_as_decl (dv)) = newv; DECL_CHANGED (dv_as_decl (dv)) = newv;
break;
}
} }
/* Return true if DV is present in changed_variables hash table. */ /* Return true if DV needs to have its cur_loc recomputed. */
static inline bool static inline bool
dv_changed_p (decl_or_value dv) dv_changed_p (decl_or_value dv)
...@@ -2585,12 +2746,11 @@ find_loc_in_1pdv (rtx loc, variable var, htab_t vars) ...@@ -2585,12 +2746,11 @@ find_loc_in_1pdv (rtx loc, variable var, htab_t vars)
if (!var) if (!var)
return NULL; return NULL;
gcc_checking_assert (dv_onepart_p (var->dv)); gcc_checking_assert (var->onepart);
if (!var->n_var_parts) if (!var->n_var_parts)
return NULL; return NULL;
gcc_checking_assert (var->var_part[0].offset == 0);
gcc_checking_assert (loc != dv_as_opaque (var->dv)); gcc_checking_assert (loc != dv_as_opaque (var->dv));
loc_code = GET_CODE (loc); loc_code = GET_CODE (loc);
...@@ -2639,6 +2799,8 @@ find_loc_in_1pdv (rtx loc, variable var, htab_t vars) ...@@ -2639,6 +2799,8 @@ find_loc_in_1pdv (rtx loc, variable var, htab_t vars)
return find_loc_in_1pdv (loc, rvar, vars); return find_loc_in_1pdv (loc, rvar, vars);
} }
/* ??? Gotta look in cselib_val locations too. */
return NULL; return NULL;
} }
...@@ -2683,7 +2845,7 @@ insert_into_intersection (location_chain *nodep, rtx loc, ...@@ -2683,7 +2845,7 @@ insert_into_intersection (location_chain *nodep, rtx loc,
*nodep = node; *nodep = node;
} }
/* Insert in DEST the intersection the locations present in both /* Insert in DEST the intersection of the locations present in both
S1NODE and S2VAR, directly or indirectly. S1NODE is from a S1NODE and S2VAR, directly or indirectly. S1NODE is from a
variable in DSM->cur, whereas S2VAR is from DSM->src. dvar is in variable in DSM->cur, whereas S2VAR is from DSM->src. dvar is in
DSM->dst. */ DSM->dst. */
...@@ -2700,11 +2862,10 @@ intersect_loc_chains (rtx val, location_chain *dest, struct dfset_merge *dsm, ...@@ -2700,11 +2862,10 @@ intersect_loc_chains (rtx val, location_chain *dest, struct dfset_merge *dsm,
{ {
location_chain s2node; location_chain s2node;
gcc_checking_assert (dv_onepart_p (s2var->dv)); gcc_checking_assert (s2var->onepart);
if (s2var->n_var_parts) if (s2var->n_var_parts)
{ {
gcc_checking_assert (s2var->var_part[0].offset == 0);
s2node = s2var->var_part[0].loc_chain; s2node = s2var->var_part[0].loc_chain;
for (; s1node && s2node; for (; s1node && s2node;
...@@ -2750,6 +2911,8 @@ intersect_loc_chains (rtx val, location_chain *dest, struct dfset_merge *dsm, ...@@ -2750,6 +2911,8 @@ intersect_loc_chains (rtx val, location_chain *dest, struct dfset_merge *dsm,
} }
} }
/* ??? gotta look in cselib_val locations too. */
/* ??? if the location is equivalent to any location in src, /* ??? if the location is equivalent to any location in src,
searched recursively searched recursively
...@@ -2839,6 +3002,18 @@ loc_cmp (rtx x, rtx y) ...@@ -2839,6 +3002,18 @@ loc_cmp (rtx x, rtx y)
if (GET_CODE (y) == VALUE) if (GET_CODE (y) == VALUE)
return 1; return 1;
/* Entry value is the least preferable kind of expression. */
if (GET_CODE (x) == ENTRY_VALUE)
{
if (GET_CODE (y) != ENTRY_VALUE)
return 1;
gcc_assert (GET_MODE (x) == GET_MODE (y));
return loc_cmp (XEXP (x, 0), XEXP (y, 0));
}
if (GET_CODE (y) == ENTRY_VALUE)
return -1;
if (GET_CODE (x) == GET_CODE (y)) if (GET_CODE (x) == GET_CODE (y))
/* Compare operands below. */; /* Compare operands below. */;
else if (GET_CODE (x) < GET_CODE (y)) else if (GET_CODE (x) < GET_CODE (y))
...@@ -2933,186 +3108,23 @@ loc_cmp (rtx x, rtx y) ...@@ -2933,186 +3108,23 @@ loc_cmp (rtx x, rtx y)
return 0; return 0;
} }
/* If decl or value DVP refers to VALUE from *LOC, add backlinks
from VALUE to DVP. */
static int
add_value_chain (rtx *loc, void *dvp)
{
decl_or_value dv, ldv;
value_chain vc, nvc;
void **slot;
if (GET_CODE (*loc) == VALUE)
ldv = dv_from_value (*loc);
else if (GET_CODE (*loc) == DEBUG_EXPR)
ldv = dv_from_decl (DEBUG_EXPR_TREE_DECL (*loc));
else
return 0;
if (dv_as_opaque (ldv) == dvp)
return 0;
dv = (decl_or_value) dvp;
slot = htab_find_slot_with_hash (value_chains, ldv, dv_htab_hash (ldv),
INSERT);
if (!*slot)
{
vc = (value_chain) pool_alloc (value_chain_pool);
vc->dv = ldv;
vc->next = NULL;
vc->refcount = 0;
*slot = (void *) vc;
}
else
{
for (vc = ((value_chain) *slot)->next; vc; vc = vc->next)
if (dv_as_opaque (vc->dv) == dv_as_opaque (dv))
break;
if (vc)
{
vc->refcount++;
return 0;
}
}
vc = (value_chain) *slot;
nvc = (value_chain) pool_alloc (value_chain_pool);
nvc->dv = dv;
nvc->next = vc->next;
nvc->refcount = 1;
vc->next = nvc;
return 0;
}
/* If decl or value DVP refers to VALUEs from within LOC, add backlinks
from those VALUEs to DVP. */
static void
add_value_chains (decl_or_value dv, rtx loc)
{
if (GET_CODE (loc) == VALUE || GET_CODE (loc) == DEBUG_EXPR)
{
add_value_chain (&loc, dv_as_opaque (dv));
return;
}
if (REG_P (loc))
return;
if (MEM_P (loc))
loc = XEXP (loc, 0);
for_each_rtx (&loc, add_value_chain, dv_as_opaque (dv));
}
/* If CSELIB_VAL_PTR of value DV refer to VALUEs, add backlinks from those
VALUEs to DV. Add the same time get rid of ASM_OPERANDS from locs list,
that is something we never can express in .debug_info and can prevent
reverse ops from being used. */
static void
add_cselib_value_chains (decl_or_value dv)
{
struct elt_loc_list **l;
for (l = &CSELIB_VAL_PTR (dv_as_value (dv))->locs; *l;)
if (GET_CODE ((*l)->loc) == ASM_OPERANDS)
*l = (*l)->next;
else
{
for_each_rtx (&(*l)->loc, add_value_chain, dv_as_opaque (dv));
l = &(*l)->next;
}
}
/* If decl or value DVP refers to VALUE from *LOC, remove backlinks
from VALUE to DVP. */
static int
remove_value_chain (rtx *loc, void *dvp)
{
decl_or_value dv, ldv;
value_chain vc;
void **slot;
if (GET_CODE (*loc) == VALUE)
ldv = dv_from_value (*loc);
else if (GET_CODE (*loc) == DEBUG_EXPR)
ldv = dv_from_decl (DEBUG_EXPR_TREE_DECL (*loc));
else
return 0;
if (dv_as_opaque (ldv) == dvp)
return 0;
dv = (decl_or_value) dvp;
slot = htab_find_slot_with_hash (value_chains, ldv, dv_htab_hash (ldv),
NO_INSERT);
for (vc = (value_chain) *slot; vc->next; vc = vc->next)
if (dv_as_opaque (vc->next->dv) == dv_as_opaque (dv))
{
value_chain dvc = vc->next;
gcc_assert (dvc->refcount > 0);
if (--dvc->refcount == 0)
{
vc->next = dvc->next;
pool_free (value_chain_pool, dvc);
if (vc->next == NULL && vc == (value_chain) *slot)
{
pool_free (value_chain_pool, vc);
htab_clear_slot (value_chains, slot);
}
}
return 0;
}
gcc_unreachable ();
}
/* If decl or value DVP refers to VALUEs from within LOC, remove backlinks
from those VALUEs to DVP. */
static void
remove_value_chains (decl_or_value dv, rtx loc)
{
if (GET_CODE (loc) == VALUE || GET_CODE (loc) == DEBUG_EXPR)
{
remove_value_chain (&loc, dv_as_opaque (dv));
return;
}
if (REG_P (loc))
return;
if (MEM_P (loc))
loc = XEXP (loc, 0);
for_each_rtx (&loc, remove_value_chain, dv_as_opaque (dv));
}
#if ENABLE_CHECKING #if ENABLE_CHECKING
/* If CSELIB_VAL_PTR of value DV refer to VALUEs, remove backlinks from those
VALUEs to DV. */
static void
remove_cselib_value_chains (decl_or_value dv)
{
struct elt_loc_list *l;
for (l = CSELIB_VAL_PTR (dv_as_value (dv))->locs; l; l = l->next)
for_each_rtx (&l->loc, remove_value_chain, dv_as_opaque (dv));
}
/* Check the order of entries in one-part variables. */ /* Check the order of entries in one-part variables. */
static int static int
canonicalize_loc_order_check (void **slot, void *data ATTRIBUTE_UNUSED) canonicalize_loc_order_check (void **slot, void *data ATTRIBUTE_UNUSED)
{ {
variable var = (variable) *slot; variable var = (variable) *slot;
decl_or_value dv = var->dv;
location_chain node, next; location_chain node, next;
#ifdef ENABLE_RTL_CHECKING #ifdef ENABLE_RTL_CHECKING
int i; int i;
for (i = 0; i < var->n_var_parts; i++) for (i = 0; i < var->n_var_parts; i++)
gcc_assert (var->var_part[0].cur_loc == NULL); gcc_assert (var->var_part[0].cur_loc == NULL);
gcc_assert (!var->cur_loc_changed && !var->in_changed_variables); gcc_assert (!var->in_changed_variables);
#endif #endif
if (!dv_onepart_p (dv)) if (!var->onepart)
return 1; return 1;
gcc_assert (var->n_var_parts == 1); gcc_assert (var->n_var_parts == 1);
...@@ -3186,7 +3198,7 @@ canonicalize_values_star (void **slot, void *data) ...@@ -3186,7 +3198,7 @@ canonicalize_values_star (void **slot, void *data)
bool has_value; bool has_value;
bool has_marks; bool has_marks;
if (!dv_onepart_p (dv)) if (!var->onepart)
return 1; return 1;
gcc_checking_assert (var->n_var_parts == 1); gcc_checking_assert (var->n_var_parts == 1);
...@@ -3408,7 +3420,7 @@ canonicalize_vars_star (void **slot, void *data) ...@@ -3408,7 +3420,7 @@ canonicalize_vars_star (void **slot, void *data)
variable cvar; variable cvar;
location_chain cnode; location_chain cnode;
if (!dv_onepart_p (dv) || dv_is_value_p (dv)) if (!var->onepart || var->onepart == ONEPART_VALUE)
return 1; return 1;
gcc_assert (var->n_var_parts == 1); gcc_assert (var->n_var_parts == 1);
...@@ -3461,7 +3473,7 @@ variable_merge_over_cur (variable s1var, struct dfset_merge *dsm) ...@@ -3461,7 +3473,7 @@ variable_merge_over_cur (variable s1var, struct dfset_merge *dsm)
void **dstslot; void **dstslot;
variable s2var, dvar = NULL; variable s2var, dvar = NULL;
decl_or_value dv = s1var->dv; decl_or_value dv = s1var->dv;
bool onepart = dv_onepart_p (dv); onepart_enum_t onepart = s1var->onepart;
rtx val; rtx val;
hashval_t dvhash; hashval_t dvhash;
location_chain node, *nodep; location_chain node, *nodep;
...@@ -3475,8 +3487,7 @@ variable_merge_over_cur (variable s1var, struct dfset_merge *dsm) ...@@ -3475,8 +3487,7 @@ variable_merge_over_cur (variable s1var, struct dfset_merge *dsm)
if (!onepart) if (!onepart)
return variable_union (s1var, dst); return variable_union (s1var, dst);
gcc_checking_assert (s1var->n_var_parts == 1 gcc_checking_assert (s1var->n_var_parts == 1);
&& s1var->var_part[0].offset == 0);
dvhash = dv_htab_hash (dv); dvhash = dv_htab_hash (dv);
if (dv_is_value_p (dv)) if (dv_is_value_p (dv))
...@@ -3493,16 +3504,16 @@ variable_merge_over_cur (variable s1var, struct dfset_merge *dsm) ...@@ -3493,16 +3504,16 @@ variable_merge_over_cur (variable s1var, struct dfset_merge *dsm)
dsm->src_onepart_cnt--; dsm->src_onepart_cnt--;
gcc_assert (s2var->var_part[0].loc_chain gcc_assert (s2var->var_part[0].loc_chain
&& s2var->n_var_parts == 1 && s2var->onepart == onepart
&& s2var->var_part[0].offset == 0); && s2var->n_var_parts == 1);
dstslot = shared_hash_find_slot_noinsert_1 (dst->vars, dv, dvhash); dstslot = shared_hash_find_slot_noinsert_1 (dst->vars, dv, dvhash);
if (dstslot) if (dstslot)
{ {
dvar = (variable)*dstslot; dvar = (variable)*dstslot;
gcc_assert (dvar->refcount == 1 gcc_assert (dvar->refcount == 1
&& dvar->n_var_parts == 1 && dvar->onepart == onepart
&& dvar->var_part[0].offset == 0); && dvar->n_var_parts == 1);
nodep = &dvar->var_part[0].loc_chain; nodep = &dvar->var_part[0].loc_chain;
} }
else else
...@@ -3529,15 +3540,18 @@ variable_merge_over_cur (variable s1var, struct dfset_merge *dsm) ...@@ -3529,15 +3540,18 @@ variable_merge_over_cur (variable s1var, struct dfset_merge *dsm)
{ {
if (node) if (node)
{ {
dvar = (variable) pool_alloc (dv_pool (dv)); dvar = (variable) pool_alloc (onepart_pool (onepart));
dvar->dv = dv; dvar->dv = dv;
dvar->refcount = 1; dvar->refcount = 1;
dvar->n_var_parts = 1; dvar->n_var_parts = 1;
dvar->cur_loc_changed = false; dvar->onepart = onepart;
dvar->in_changed_variables = false; dvar->in_changed_variables = false;
dvar->var_part[0].offset = 0;
dvar->var_part[0].loc_chain = node; dvar->var_part[0].loc_chain = node;
dvar->var_part[0].cur_loc = NULL; dvar->var_part[0].cur_loc = NULL;
if (onepart)
VAR_LOC_1PAUX (dvar) = NULL;
else
VAR_PART_OFFSET (dvar, 0) = 0;
dstslot dstslot
= shared_hash_find_slot_unshare_1 (&dst->vars, dv, dvhash, = shared_hash_find_slot_unshare_1 (&dst->vars, dv, dvhash,
...@@ -3662,15 +3676,16 @@ variable_merge_over_cur (variable s1var, struct dfset_merge *dsm) ...@@ -3662,15 +3676,16 @@ variable_merge_over_cur (variable s1var, struct dfset_merge *dsm)
INSERT); INSERT);
if (!*slot) if (!*slot)
{ {
variable var = (variable) pool_alloc (dv_pool (dv)); variable var = (variable) pool_alloc (onepart_pool
(ONEPART_VALUE));
var->dv = dv; var->dv = dv;
var->refcount = 1; var->refcount = 1;
var->n_var_parts = 1; var->n_var_parts = 1;
var->cur_loc_changed = false; var->onepart = ONEPART_VALUE;
var->in_changed_variables = false; var->in_changed_variables = false;
var->var_part[0].offset = 0;
var->var_part[0].loc_chain = NULL; var->var_part[0].loc_chain = NULL;
var->var_part[0].cur_loc = NULL; var->var_part[0].cur_loc = NULL;
VAR_LOC_1PAUX (var) = NULL;
*slot = var; *slot = var;
} }
...@@ -3717,9 +3732,8 @@ variable_merge_over_src (variable s2var, struct dfset_merge *dsm) ...@@ -3717,9 +3732,8 @@ variable_merge_over_src (variable s2var, struct dfset_merge *dsm)
{ {
dataflow_set *dst = dsm->dst; dataflow_set *dst = dsm->dst;
decl_or_value dv = s2var->dv; decl_or_value dv = s2var->dv;
bool onepart = dv_onepart_p (dv);
if (!onepart) if (!s2var->onepart)
{ {
void **dstp = shared_hash_find_slot (dst->vars, dv); void **dstp = shared_hash_find_slot (dst->vars, dv);
*dstp = s2var; *dstp = s2var;
...@@ -3864,7 +3878,7 @@ remove_duplicate_values (variable var) ...@@ -3864,7 +3878,7 @@ remove_duplicate_values (variable var)
{ {
location_chain node, *nodep; location_chain node, *nodep;
gcc_assert (dv_onepart_p (var->dv)); gcc_assert (var->onepart);
gcc_assert (var->n_var_parts == 1); gcc_assert (var->n_var_parts == 1);
gcc_assert (var->refcount == 1); gcc_assert (var->refcount == 1);
...@@ -3915,7 +3929,7 @@ variable_post_merge_new_vals (void **slot, void *info) ...@@ -3915,7 +3929,7 @@ variable_post_merge_new_vals (void **slot, void *info)
variable var = (variable)*slot; variable var = (variable)*slot;
location_chain node; location_chain node;
if (!dv_onepart_p (var->dv) || !var->n_var_parts) if (!var->onepart || !var->n_var_parts)
return 1; return 1;
gcc_assert (var->n_var_parts == 1); gcc_assert (var->n_var_parts == 1);
...@@ -4146,13 +4160,11 @@ find_mem_expr_in_1pdv (tree expr, rtx val, htab_t vars) ...@@ -4146,13 +4160,11 @@ find_mem_expr_in_1pdv (tree expr, rtx val, htab_t vars)
if (!var) if (!var)
return NULL; return NULL;
gcc_assert (dv_onepart_p (var->dv)); gcc_assert (var->onepart);
if (!var->n_var_parts) if (!var->n_var_parts)
return NULL; return NULL;
gcc_assert (var->var_part[0].offset == 0);
VALUE_RECURSED_INTO (val) = true; VALUE_RECURSED_INTO (val) = true;
for (node = var->var_part[0].loc_chain; node; node = node->next) for (node = var->var_part[0].loc_chain; node; node = node->next)
...@@ -4206,7 +4218,7 @@ dataflow_set_preserve_mem_locs (void **slot, void *data) ...@@ -4206,7 +4218,7 @@ dataflow_set_preserve_mem_locs (void **slot, void *data)
dataflow_set *set = (dataflow_set *) data; dataflow_set *set = (dataflow_set *) data;
variable var = (variable) *slot; variable var = (variable) *slot;
if (dv_is_decl_p (var->dv) && dv_onepart_p (var->dv)) if (var->onepart == ONEPART_VDECL || var->onepart == ONEPART_DEXPR)
{ {
tree decl = dv_as_decl (var->dv); tree decl = dv_as_decl (var->dv);
location_chain loc, *locp; location_chain loc, *locp;
...@@ -4277,10 +4289,7 @@ dataflow_set_preserve_mem_locs (void **slot, void *data) ...@@ -4277,10 +4289,7 @@ dataflow_set_preserve_mem_locs (void **slot, void *data)
{ {
changed = true; changed = true;
var->var_part[0].cur_loc = NULL; var->var_part[0].cur_loc = NULL;
var->cur_loc_changed = true;
} }
add_value_chains (var->dv, loc->loc);
remove_value_chains (var->dv, old_loc);
} }
locp = &loc->next; locp = &loc->next;
continue; continue;
...@@ -4288,12 +4297,10 @@ dataflow_set_preserve_mem_locs (void **slot, void *data) ...@@ -4288,12 +4297,10 @@ dataflow_set_preserve_mem_locs (void **slot, void *data)
if (emit_notes) if (emit_notes)
{ {
remove_value_chains (var->dv, old_loc);
if (old_loc == var->var_part[0].cur_loc) if (old_loc == var->var_part[0].cur_loc)
{ {
changed = true; changed = true;
var->var_part[0].cur_loc = NULL; var->var_part[0].cur_loc = NULL;
var->cur_loc_changed = true;
} }
} }
*locp = loc->next; *locp = loc->next;
...@@ -4321,10 +4328,11 @@ dataflow_set_remove_mem_locs (void **slot, void *data) ...@@ -4321,10 +4328,11 @@ dataflow_set_remove_mem_locs (void **slot, void *data)
dataflow_set *set = (dataflow_set *) data; dataflow_set *set = (dataflow_set *) data;
variable var = (variable) *slot; variable var = (variable) *slot;
if (dv_is_value_p (var->dv)) if (var->onepart == ONEPART_VALUE)
{ {
location_chain loc, *locp; location_chain loc, *locp;
bool changed = false; bool changed = false;
rtx cur_loc;
gcc_assert (var->n_var_parts == 1); gcc_assert (var->n_var_parts == 1);
...@@ -4343,6 +4351,11 @@ dataflow_set_remove_mem_locs (void **slot, void *data) ...@@ -4343,6 +4351,11 @@ dataflow_set_remove_mem_locs (void **slot, void *data)
gcc_assert (var->n_var_parts == 1); gcc_assert (var->n_var_parts == 1);
} }
if (VAR_LOC_1PAUX (var))
cur_loc = VAR_LOC_FROM (var);
else
cur_loc = var->var_part[0].cur_loc;
for (locp = &var->var_part[0].loc_chain, loc = *locp; for (locp = &var->var_part[0].loc_chain, loc = *locp;
loc; loc = *locp) loc; loc = *locp)
{ {
...@@ -4353,17 +4366,16 @@ dataflow_set_remove_mem_locs (void **slot, void *data) ...@@ -4353,17 +4366,16 @@ dataflow_set_remove_mem_locs (void **slot, void *data)
continue; continue;
} }
if (emit_notes)
remove_value_chains (var->dv, loc->loc);
*locp = loc->next; *locp = loc->next;
/* If we have deleted the location which was last emitted /* If we have deleted the location which was last emitted
we have to emit new location so add the variable to set we have to emit new location so add the variable to set
of changed variables. */ of changed variables. */
if (var->var_part[0].cur_loc == loc->loc) if (cur_loc == loc->loc)
{ {
changed = true; changed = true;
var->var_part[0].cur_loc = NULL; var->var_part[0].cur_loc = NULL;
var->cur_loc_changed = true; if (VAR_LOC_1PAUX (var))
VAR_LOC_FROM (var) = NULL;
} }
pool_free (loc_chain_pool, loc); pool_free (loc_chain_pool, loc);
} }
...@@ -4467,20 +4479,24 @@ variable_different_p (variable var1, variable var2) ...@@ -4467,20 +4479,24 @@ variable_different_p (variable var1, variable var2)
if (var1 == var2) if (var1 == var2)
return false; return false;
if (var1->onepart != var2->onepart)
return true;
if (var1->n_var_parts != var2->n_var_parts) if (var1->n_var_parts != var2->n_var_parts)
return true; return true;
for (i = 0; i < var1->n_var_parts; i++) if (var1->onepart && var1->n_var_parts)
{ {
if (var1->var_part[i].offset != var2->var_part[i].offset) gcc_checking_assert (dv_as_opaque (var1->dv) == dv_as_opaque (var2->dv)
return true; && var1->n_var_parts == 1);
/* One-part values have locations in a canonical order. */ /* One-part values have locations in a canonical order. */
if (i == 0 && var1->var_part[i].offset == 0 && dv_onepart_p (var1->dv))
{
gcc_assert (var1->n_var_parts == 1
&& dv_as_opaque (var1->dv) == dv_as_opaque (var2->dv));
return onepart_variable_different_p (var1, var2); return onepart_variable_different_p (var1, var2);
} }
for (i = 0; i < var1->n_var_parts; i++)
{
if (VAR_PART_OFFSET (var1, i) != VAR_PART_OFFSET (var2, i))
return true;
if (variable_part_different_p (&var1->var_part[i], &var2->var_part[i])) if (variable_part_different_p (&var1->var_part[i], &var2->var_part[i]))
return true; return true;
if (variable_part_different_p (&var2->var_part[i], &var1->var_part[i])) if (variable_part_different_p (&var2->var_part[i], &var1->var_part[i]))
...@@ -5020,9 +5036,6 @@ log_op_type (rtx x, basic_block bb, rtx insn, ...@@ -5020,9 +5036,6 @@ log_op_type (rtx x, basic_block bb, rtx insn,
/* All preserved VALUEs. */ /* All preserved VALUEs. */
static VEC (rtx, heap) *preserved_values; static VEC (rtx, heap) *preserved_values;
/* Registers used in the current function for passing parameters. */
static HARD_REG_SET argument_reg_set;
/* Ensure VAL is preserved and remember it in a vector for vt_emit_notes. */ /* Ensure VAL is preserved and remember it in a vector for vt_emit_notes. */
static void static void
...@@ -5089,11 +5102,7 @@ add_uses (rtx *ploc, void *data) ...@@ -5089,11 +5102,7 @@ add_uses (rtx *ploc, void *data)
if (MEM_P (vloc) if (MEM_P (vloc)
&& !REG_P (XEXP (vloc, 0)) && !REG_P (XEXP (vloc, 0))
&& !MEM_P (XEXP (vloc, 0)) && !MEM_P (XEXP (vloc, 0)))
&& GET_CODE (XEXP (vloc, 0)) != ENTRY_VALUE
&& (GET_CODE (XEXP (vloc, 0)) != PLUS
|| XEXP (XEXP (vloc, 0), 0) != cfa_base_rtx
|| !CONST_INT_P (XEXP (XEXP (vloc, 0), 1))))
{ {
rtx mloc = vloc; rtx mloc = vloc;
enum machine_mode address_mode = get_address_mode (mloc); enum machine_mode address_mode = get_address_mode (mloc);
...@@ -5105,6 +5114,12 @@ add_uses (rtx *ploc, void *data) ...@@ -5105,6 +5114,12 @@ add_uses (rtx *ploc, void *data)
{ {
micro_operation moa; micro_operation moa;
preserve_value (val); preserve_value (val);
if (GET_CODE (XEXP (mloc, 0)) != ENTRY_VALUE
&& (GET_CODE (XEXP (mloc, 0)) != PLUS
|| XEXP (XEXP (mloc, 0), 0) != cfa_base_rtx
|| !CONST_INT_P (XEXP (XEXP (mloc, 0), 1))))
{
mloc = cselib_subst_to_values (XEXP (mloc, 0), mloc = cselib_subst_to_values (XEXP (mloc, 0),
GET_MODE (mloc)); GET_MODE (mloc));
moa.type = MO_VAL_USE; moa.type = MO_VAL_USE;
...@@ -5114,7 +5129,9 @@ add_uses (rtx *ploc, void *data) ...@@ -5114,7 +5129,9 @@ add_uses (rtx *ploc, void *data)
if (dump_file && (dump_flags & TDF_DETAILS)) if (dump_file && (dump_flags & TDF_DETAILS))
log_op_type (moa.u.loc, cui->bb, cui->insn, log_op_type (moa.u.loc, cui->bb, cui->insn,
moa.type, dump_file); moa.type, dump_file);
VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &moa); VEC_safe_push (micro_operation, heap, VTI (bb)->mos,
&moa);
}
} }
} }
...@@ -5122,7 +5139,7 @@ add_uses (rtx *ploc, void *data) ...@@ -5122,7 +5139,7 @@ add_uses (rtx *ploc, void *data)
&& (GET_CODE (vloc) != CONST && (GET_CODE (vloc) != CONST
|| for_each_rtx (&vloc, non_suitable_const, NULL))) || for_each_rtx (&vloc, non_suitable_const, NULL)))
/* For constants don't look up any value. */; /* For constants don't look up any value. */;
else if (!VAR_LOC_UNKNOWN_P (vloc) else if (!VAR_LOC_UNKNOWN_P (vloc) && !unsuitable_loc (vloc)
&& (val = find_use_val (vloc, GET_MODE (oloc), cui))) && (val = find_use_val (vloc, GET_MODE (oloc), cui)))
{ {
enum machine_mode mode2; enum machine_mode mode2;
...@@ -5168,11 +5185,7 @@ add_uses (rtx *ploc, void *data) ...@@ -5168,11 +5185,7 @@ add_uses (rtx *ploc, void *data)
if (MEM_P (oloc) if (MEM_P (oloc)
&& !REG_P (XEXP (oloc, 0)) && !REG_P (XEXP (oloc, 0))
&& !MEM_P (XEXP (oloc, 0)) && !MEM_P (XEXP (oloc, 0)))
&& GET_CODE (XEXP (oloc, 0)) != ENTRY_VALUE
&& (GET_CODE (XEXP (oloc, 0)) != PLUS
|| XEXP (XEXP (oloc, 0), 0) != cfa_base_rtx
|| !CONST_INT_P (XEXP (XEXP (oloc, 0), 1))))
{ {
rtx mloc = oloc; rtx mloc = oloc;
enum machine_mode address_mode = get_address_mode (mloc); enum machine_mode address_mode = get_address_mode (mloc);
...@@ -5184,6 +5197,12 @@ add_uses (rtx *ploc, void *data) ...@@ -5184,6 +5197,12 @@ add_uses (rtx *ploc, void *data)
{ {
micro_operation moa; micro_operation moa;
preserve_value (val); preserve_value (val);
if (GET_CODE (XEXP (mloc, 0)) != ENTRY_VALUE
&& (GET_CODE (XEXP (mloc, 0)) != PLUS
|| XEXP (XEXP (mloc, 0), 0) != cfa_base_rtx
|| !CONST_INT_P (XEXP (XEXP (mloc, 0), 1))))
{
mloc = cselib_subst_to_values (XEXP (mloc, 0), mloc = cselib_subst_to_values (XEXP (mloc, 0),
GET_MODE (mloc)); GET_MODE (mloc));
moa.type = MO_VAL_USE; moa.type = MO_VAL_USE;
...@@ -5193,7 +5212,9 @@ add_uses (rtx *ploc, void *data) ...@@ -5193,7 +5212,9 @@ add_uses (rtx *ploc, void *data)
if (dump_file && (dump_flags & TDF_DETAILS)) if (dump_file && (dump_flags & TDF_DETAILS))
log_op_type (moa.u.loc, cui->bb, cui->insn, log_op_type (moa.u.loc, cui->bb, cui->insn,
moa.type, dump_file); moa.type, dump_file);
VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &moa); VEC_safe_push (micro_operation, heap, VTI (bb)->mos,
&moa);
}
} }
} }
...@@ -5255,7 +5276,18 @@ add_uses_1 (rtx *x, void *cui) ...@@ -5255,7 +5276,18 @@ add_uses_1 (rtx *x, void *cui)
for_each_rtx (x, add_uses, cui); for_each_rtx (x, add_uses, cui);
} }
#define EXPR_DEPTH (PARAM_VALUE (PARAM_MAX_VARTRACK_EXPR_DEPTH)) /* This is the value used during expansion of locations. We want it
to be unbounded, so that variables expanded deep in a recursion
nest are fully evaluated, so that their values are cached
correctly. We avoid recursion cycles through other means, and we
don't unshare RTL, so excess complexity is not a problem. */
#define EXPR_DEPTH (INT_MAX)
/* We use this to keep too-complex expressions from being emitted as
location notes, and then to debug information. Users can trade
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))
/* Attempt to reverse the EXPR operation in the debug info. Say for /* Attempt to reverse the EXPR operation in the debug info. Say for
reg1 = reg2 + 6 even when reg2 is no longer live we reg1 = reg2 + 6 even when reg2 is no longer live we
...@@ -5382,10 +5414,8 @@ add_stores (rtx loc, const_rtx expr, void *cuip) ...@@ -5382,10 +5414,8 @@ add_stores (rtx loc, const_rtx expr, void *cuip)
mo.u.loc = loc; mo.u.loc = loc;
if (GET_CODE (expr) == SET if (GET_CODE (expr) == SET
&& SET_DEST (expr) == loc && SET_DEST (expr) == loc
&& REGNO (loc) < FIRST_PSEUDO_REGISTER && !unsuitable_loc (SET_SRC (expr))
&& TEST_HARD_REG_BIT (argument_reg_set, REGNO (loc)) && find_use_val (loc, mode, cui))
&& find_use_val (loc, mode, cui)
&& GET_CODE (SET_SRC (expr)) != ASM_OPERANDS)
{ {
gcc_checking_assert (type == MO_VAL_SET); gcc_checking_assert (type == MO_VAL_SET);
mo.u.loc = gen_rtx_SET (VOIDmode, loc, SET_SRC (expr)); mo.u.loc = gen_rtx_SET (VOIDmode, loc, SET_SRC (expr));
...@@ -5422,11 +5452,7 @@ add_stores (rtx loc, const_rtx expr, void *cuip) ...@@ -5422,11 +5452,7 @@ add_stores (rtx loc, const_rtx expr, void *cuip)
{ {
if (MEM_P (loc) && type == MO_VAL_SET if (MEM_P (loc) && type == MO_VAL_SET
&& !REG_P (XEXP (loc, 0)) && !REG_P (XEXP (loc, 0))
&& !MEM_P (XEXP (loc, 0)) && !MEM_P (XEXP (loc, 0)))
&& GET_CODE (XEXP (loc, 0)) != ENTRY_VALUE
&& (GET_CODE (XEXP (loc, 0)) != PLUS
|| XEXP (XEXP (loc, 0), 0) != cfa_base_rtx
|| !CONST_INT_P (XEXP (XEXP (loc, 0), 1))))
{ {
rtx mloc = loc; rtx mloc = loc;
enum machine_mode address_mode = get_address_mode (mloc); enum machine_mode address_mode = get_address_mode (mloc);
...@@ -5437,17 +5463,25 @@ add_stores (rtx loc, const_rtx expr, void *cuip) ...@@ -5437,17 +5463,25 @@ add_stores (rtx loc, const_rtx expr, void *cuip)
if (val && !cselib_preserved_value_p (val)) if (val && !cselib_preserved_value_p (val))
{ {
preserve_value (val); preserve_value (val);
mo.type = MO_VAL_USE;
if (GET_CODE (XEXP (mloc, 0)) != ENTRY_VALUE
&& (GET_CODE (XEXP (mloc, 0)) != PLUS
|| XEXP (XEXP (mloc, 0), 0) != cfa_base_rtx
|| !CONST_INT_P (XEXP (XEXP (mloc, 0), 1))))
{
mloc = cselib_subst_to_values (XEXP (mloc, 0), mloc = cselib_subst_to_values (XEXP (mloc, 0),
GET_MODE (mloc)); GET_MODE (mloc));
mo.u.loc = gen_rtx_CONCAT (address_mode, val->val_rtx, mloc); mo.type = MO_VAL_USE;
mo.insn = cui->insn; mo.insn = cui->insn;
mo.u.loc = gen_rtx_CONCAT (address_mode,
val->val_rtx, mloc);
if (dump_file && (dump_flags & TDF_DETAILS)) if (dump_file && (dump_flags & TDF_DETAILS))
log_op_type (mo.u.loc, cui->bb, cui->insn, log_op_type (mo.u.loc, cui->bb, cui->insn,
mo.type, dump_file); mo.type, dump_file);
VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &mo); VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &mo);
} }
} }
}
if (GET_CODE (expr) == CLOBBER || !track_p) if (GET_CODE (expr) == CLOBBER || !track_p)
{ {
...@@ -5893,7 +5927,7 @@ prepare_call_arguments (basic_block bb, rtx insn) ...@@ -5893,7 +5927,7 @@ prepare_call_arguments (basic_block bb, rtx insn)
tree dtemp = VEC_index (tree, *debug_args, ix + 1); tree dtemp = VEC_index (tree, *debug_args, ix + 1);
enum machine_mode mode = DECL_MODE (dtemp); enum machine_mode mode = DECL_MODE (dtemp);
item = gen_rtx_DEBUG_PARAMETER_REF (mode, param); item = gen_rtx_DEBUG_PARAMETER_REF (mode, param);
item = gen_rtx_CONCAT (mode, item, DECL_RTL (dtemp)); item = gen_rtx_CONCAT (mode, item, DECL_RTL_KNOWN_SET (dtemp));
call_arguments = gen_rtx_EXPR_LIST (VOIDmode, item, call_arguments = gen_rtx_EXPR_LIST (VOIDmode, item,
call_arguments); call_arguments);
} }
...@@ -6705,7 +6739,7 @@ dump_var (variable var) ...@@ -6705,7 +6739,7 @@ dump_var (variable var)
for (i = 0; i < var->n_var_parts; i++) for (i = 0; i < var->n_var_parts; i++)
{ {
fprintf (dump_file, " offset %ld\n", fprintf (dump_file, " offset %ld\n",
(long) var->var_part[i].offset); (long)(var->onepart ? 0 : VAR_PART_OFFSET (var, i)));
for (node = var->var_part[i].loc_chain; node; node = node->next) for (node = var->var_part[i].loc_chain; node; node = node->next)
{ {
fprintf (dump_file, " "); fprintf (dump_file, " ");
...@@ -6766,6 +6800,73 @@ dump_dataflow_sets (void) ...@@ -6766,6 +6800,73 @@ dump_dataflow_sets (void)
} }
} }
/* Return the variable for DV in dropped_values, inserting one if
requested with INSERT. */
static inline variable
variable_from_dropped (decl_or_value dv, enum insert_option insert)
{
void **slot;
variable empty_var;
onepart_enum_t onepart;
slot = htab_find_slot_with_hash (dropped_values, dv, dv_htab_hash (dv),
insert);
if (!slot)
return NULL;
if (*slot)
return (variable) *slot;
gcc_checking_assert (insert == INSERT);
onepart = dv_onepart_p (dv);
gcc_checking_assert (onepart == ONEPART_VALUE || onepart == ONEPART_DEXPR);
empty_var = (variable) pool_alloc (onepart_pool (onepart));
empty_var->dv = dv;
empty_var->refcount = 1;
empty_var->n_var_parts = 0;
empty_var->onepart = onepart;
empty_var->in_changed_variables = false;
empty_var->var_part[0].loc_chain = NULL;
empty_var->var_part[0].cur_loc = NULL;
VAR_LOC_1PAUX (empty_var) = NULL;
set_dv_changed (dv, true);
*slot = empty_var;
return empty_var;
}
/* Recover the one-part aux from dropped_values. */
static struct onepart_aux *
recover_dropped_1paux (variable var)
{
variable dvar;
gcc_checking_assert (var->onepart);
if (VAR_LOC_1PAUX (var))
return VAR_LOC_1PAUX (var);
if (var->onepart == ONEPART_VDECL)
return NULL;
dvar = variable_from_dropped (var->dv, NO_INSERT);
if (!dvar)
return NULL;
VAR_LOC_1PAUX (var) = VAR_LOC_1PAUX (dvar);
VAR_LOC_1PAUX (dvar) = NULL;
return VAR_LOC_1PAUX (var);
}
/* Add variable VAR to the hash table of changed variables and /* Add variable VAR to the hash table of changed variables and
if it has no locations delete it from SET's hash table. */ if it has no locations delete it from SET's hash table. */
...@@ -6777,7 +6878,6 @@ variable_was_changed (variable var, dataflow_set *set) ...@@ -6777,7 +6878,6 @@ variable_was_changed (variable var, dataflow_set *set)
if (emit_notes) if (emit_notes)
{ {
void **slot; void **slot;
bool old_cur_loc_changed = false;
/* Remember this decl or VALUE has been added to changed_variables. */ /* Remember this decl or VALUE has been added to changed_variables. */
set_dv_changed (var->dv, true); set_dv_changed (var->dv, true);
...@@ -6791,30 +6891,76 @@ variable_was_changed (variable var, dataflow_set *set) ...@@ -6791,30 +6891,76 @@ variable_was_changed (variable var, dataflow_set *set)
variable old_var = (variable) *slot; variable old_var = (variable) *slot;
gcc_assert (old_var->in_changed_variables); gcc_assert (old_var->in_changed_variables);
old_var->in_changed_variables = false; old_var->in_changed_variables = false;
old_cur_loc_changed = old_var->cur_loc_changed; if (var != old_var && var->onepart)
{
/* Restore the auxiliary info from an empty variable
previously created for changed_variables, so it is
not lost. */
gcc_checking_assert (!VAR_LOC_1PAUX (var));
VAR_LOC_1PAUX (var) = VAR_LOC_1PAUX (old_var);
VAR_LOC_1PAUX (old_var) = NULL;
}
variable_htab_free (*slot); variable_htab_free (*slot);
} }
if (set && var->n_var_parts == 0) if (set && var->n_var_parts == 0)
{ {
variable empty_var; onepart_enum_t onepart = var->onepart;
variable empty_var = NULL;
void **dslot = NULL;
if (onepart == ONEPART_VALUE || onepart == ONEPART_DEXPR)
{
dslot = htab_find_slot_with_hash (dropped_values, var->dv,
dv_htab_hash (var->dv),
INSERT);
empty_var = (variable) *dslot;
empty_var = (variable) pool_alloc (dv_pool (var->dv)); if (empty_var)
{
gcc_checking_assert (!empty_var->in_changed_variables);
if (!VAR_LOC_1PAUX (var))
{
VAR_LOC_1PAUX (var) = VAR_LOC_1PAUX (empty_var);
VAR_LOC_1PAUX (empty_var) = NULL;
}
else
gcc_checking_assert (!VAR_LOC_1PAUX (empty_var));
}
}
if (!empty_var)
{
empty_var = (variable) pool_alloc (onepart_pool (onepart));
empty_var->dv = var->dv; empty_var->dv = var->dv;
empty_var->refcount = 1; empty_var->refcount = 1;
empty_var->n_var_parts = 0; empty_var->n_var_parts = 0;
empty_var->cur_loc_changed = true; empty_var->onepart = onepart;
if (dslot)
{
empty_var->refcount++;
*dslot = empty_var;
}
}
else
empty_var->refcount++;
empty_var->in_changed_variables = true; empty_var->in_changed_variables = true;
*slot = empty_var; *slot = empty_var;
if (onepart)
{
empty_var->var_part[0].loc_chain = NULL;
empty_var->var_part[0].cur_loc = NULL;
VAR_LOC_1PAUX (empty_var) = VAR_LOC_1PAUX (var);
VAR_LOC_1PAUX (var) = NULL;
}
goto drop_var; goto drop_var;
} }
else else
{ {
if (var->onepart && !VAR_LOC_1PAUX (var))
recover_dropped_1paux (var);
var->refcount++; var->refcount++;
var->in_changed_variables = true; var->in_changed_variables = true;
/* If within processing one uop a variable is deleted
and then readded, we need to assume it has changed. */
if (old_cur_loc_changed)
var->cur_loc_changed = true;
*slot = var; *slot = var;
} }
} }
...@@ -6849,13 +6995,24 @@ find_variable_location_part (variable var, HOST_WIDE_INT offset, ...@@ -6849,13 +6995,24 @@ find_variable_location_part (variable var, HOST_WIDE_INT offset,
{ {
int pos, low, high; int pos, low, high;
if (var->onepart)
{
if (offset != 0)
return -1;
if (insertion_point)
*insertion_point = 0;
return var->n_var_parts - 1;
}
/* Find the location part. */ /* Find the location part. */
low = 0; low = 0;
high = var->n_var_parts; high = var->n_var_parts;
while (low != high) while (low != high)
{ {
pos = (low + high) / 2; pos = (low + high) / 2;
if (var->var_part[pos].offset < offset) if (VAR_PART_OFFSET (var, pos) < offset)
low = pos + 1; low = pos + 1;
else else
high = pos; high = pos;
...@@ -6865,7 +7022,7 @@ find_variable_location_part (variable var, HOST_WIDE_INT offset, ...@@ -6865,7 +7022,7 @@ find_variable_location_part (variable var, HOST_WIDE_INT offset,
if (insertion_point) if (insertion_point)
*insertion_point = pos; *insertion_point = pos;
if (pos < var->n_var_parts && var->var_part[pos].offset == offset) if (pos < var->n_var_parts && VAR_PART_OFFSET (var, pos) == offset)
return pos; return pos;
return -1; return -1;
...@@ -6880,26 +7037,34 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot, ...@@ -6880,26 +7037,34 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot,
location_chain node, next; location_chain node, next;
location_chain *nextp; location_chain *nextp;
variable var; variable var;
bool onepart = dv_onepart_p (dv); onepart_enum_t onepart;
gcc_assert (offset == 0 || !onepart);
gcc_assert (loc != dv_as_opaque (dv));
var = (variable) *slot; var = (variable) *slot;
if (var)
onepart = var->onepart;
else
onepart = dv_onepart_p (dv);
gcc_checking_assert (offset == 0 || !onepart);
gcc_checking_assert (loc != dv_as_opaque (dv));
if (! flag_var_tracking_uninit) if (! flag_var_tracking_uninit)
initialized = VAR_INIT_STATUS_INITIALIZED; initialized = VAR_INIT_STATUS_INITIALIZED;
if (!var) if (!var)
{ {
/* Create new variable information. */ /* Create new variable information. */
var = (variable) pool_alloc (dv_pool (dv)); var = (variable) pool_alloc (onepart_pool (onepart));
var->dv = dv; var->dv = dv;
var->refcount = 1; var->refcount = 1;
var->n_var_parts = 1; var->n_var_parts = 1;
var->cur_loc_changed = false; var->onepart = onepart;
var->in_changed_variables = false; var->in_changed_variables = false;
var->var_part[0].offset = offset; if (var->onepart)
VAR_LOC_1PAUX (var) = NULL;
else
VAR_PART_OFFSET (var, 0) = offset;
var->var_part[0].loc_chain = NULL; var->var_part[0].loc_chain = NULL;
var->var_part[0].cur_loc = NULL; var->var_part[0].cur_loc = NULL;
*slot = var; *slot = var;
...@@ -7054,7 +7219,7 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot, ...@@ -7054,7 +7219,7 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot,
/* We track only variables whose size is <= MAX_VAR_PARTS bytes /* We track only variables whose size is <= MAX_VAR_PARTS bytes
thus there are at most MAX_VAR_PARTS different offsets. */ thus there are at most MAX_VAR_PARTS different offsets. */
gcc_assert (var->n_var_parts < MAX_VAR_PARTS gcc_assert (var->n_var_parts < MAX_VAR_PARTS
&& (!var->n_var_parts || !dv_onepart_p (var->dv))); && (!var->n_var_parts || !onepart));
/* We have to move the elements of array starting at index /* We have to move the elements of array starting at index
inspos to the next position. */ inspos to the next position. */
...@@ -7062,7 +7227,8 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot, ...@@ -7062,7 +7227,8 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot,
var->var_part[pos] = var->var_part[pos - 1]; var->var_part[pos] = var->var_part[pos - 1];
var->n_var_parts++; var->n_var_parts++;
var->var_part[pos].offset = offset; gcc_checking_assert (!onepart);
VAR_PART_OFFSET (var, pos) = offset;
var->var_part[pos].loc_chain = NULL; var->var_part[pos].loc_chain = NULL;
var->var_part[pos].cur_loc = NULL; var->var_part[pos].cur_loc = NULL;
} }
...@@ -7083,10 +7249,7 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot, ...@@ -7083,10 +7249,7 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot,
if (node->set_src != NULL && set_src == NULL) if (node->set_src != NULL && set_src == NULL)
set_src = node->set_src; set_src = node->set_src;
if (var->var_part[pos].cur_loc == node->loc) if (var->var_part[pos].cur_loc == node->loc)
{
var->var_part[pos].cur_loc = NULL; var->var_part[pos].cur_loc = NULL;
var->cur_loc_changed = true;
}
pool_free (loc_chain_pool, node); pool_free (loc_chain_pool, node);
*nextp = next; *nextp = next;
break; break;
...@@ -7106,9 +7269,6 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot, ...@@ -7106,9 +7269,6 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot,
node->next = *nextp; node->next = *nextp;
*nextp = node; *nextp = node;
if (onepart && emit_notes)
add_value_chains (var->dv, loc);
/* If no location was emitted do so. */ /* If no location was emitted do so. */
if (var->var_part[pos].cur_loc == NULL) if (var->var_part[pos].cur_loc == NULL)
variable_was_changed (var, set); variable_was_changed (var, set);
...@@ -7238,6 +7398,7 @@ delete_slot_part (dataflow_set *set, rtx loc, void **slot, ...@@ -7238,6 +7398,7 @@ delete_slot_part (dataflow_set *set, rtx loc, void **slot,
location_chain node, next; location_chain node, next;
location_chain *nextp; location_chain *nextp;
bool changed; bool changed;
rtx cur_loc;
if (shared_var_p (var, set->vars)) if (shared_var_p (var, set->vars))
{ {
...@@ -7258,6 +7419,11 @@ delete_slot_part (dataflow_set *set, rtx loc, void **slot, ...@@ -7258,6 +7419,11 @@ delete_slot_part (dataflow_set *set, rtx loc, void **slot,
} }
} }
if (pos == 0 && var->onepart && VAR_LOC_1PAUX (var))
cur_loc = VAR_LOC_FROM (var);
else
cur_loc = var->var_part[pos].cur_loc;
/* Delete the location part. */ /* Delete the location part. */
changed = false; changed = false;
nextp = &var->var_part[pos].loc_chain; nextp = &var->var_part[pos].loc_chain;
...@@ -7268,16 +7434,15 @@ delete_slot_part (dataflow_set *set, rtx loc, void **slot, ...@@ -7268,16 +7434,15 @@ delete_slot_part (dataflow_set *set, rtx loc, void **slot,
&& REGNO (node->loc) == REGNO (loc)) && REGNO (node->loc) == REGNO (loc))
|| rtx_equal_p (node->loc, loc)) || rtx_equal_p (node->loc, loc))
{ {
if (emit_notes && pos == 0 && dv_onepart_p (var->dv))
remove_value_chains (var->dv, node->loc);
/* If we have deleted the location which was last emitted /* If we have deleted the location which was last emitted
we have to emit new location so add the variable to set we have to emit new location so add the variable to set
of changed variables. */ of changed variables. */
if (var->var_part[pos].cur_loc == node->loc) if (cur_loc == node->loc)
{ {
changed = true; changed = true;
var->var_part[pos].cur_loc = NULL; var->var_part[pos].cur_loc = NULL;
var->cur_loc_changed = true; if (pos == 0 && var->onepart && VAR_LOC_1PAUX (var))
VAR_LOC_FROM (var) = NULL;
} }
pool_free (loc_chain_pool, node); pool_free (loc_chain_pool, node);
*nextp = next; *nextp = next;
...@@ -7291,8 +7456,6 @@ delete_slot_part (dataflow_set *set, rtx loc, void **slot, ...@@ -7291,8 +7456,6 @@ delete_slot_part (dataflow_set *set, rtx loc, void **slot,
{ {
changed = true; changed = true;
var->n_var_parts--; var->n_var_parts--;
if (emit_notes)
var->cur_loc_changed = true;
while (pos < var->n_var_parts) while (pos < var->n_var_parts)
{ {
var->var_part[pos] = var->var_part[pos + 1]; var->var_part[pos] = var->var_part[pos + 1];
...@@ -7321,6 +7484,12 @@ delete_variable_part (dataflow_set *set, rtx loc, decl_or_value dv, ...@@ -7321,6 +7484,12 @@ delete_variable_part (dataflow_set *set, rtx loc, decl_or_value dv,
delete_slot_part (set, loc, slot, offset); delete_slot_part (set, loc, slot, offset);
} }
DEF_VEC_P (variable);
DEF_VEC_ALLOC_P (variable, heap);
DEF_VEC_ALLOC_P_STACK (rtx);
#define VEC_rtx_stack_alloc(alloc) VEC_stack_alloc (rtx, alloc)
/* Structure for passing some other parameters to function /* Structure for passing some other parameters to function
vt_expand_loc_callback. */ vt_expand_loc_callback. */
struct expand_loc_callback_data struct expand_loc_callback_data
...@@ -7328,56 +7497,368 @@ struct expand_loc_callback_data ...@@ -7328,56 +7497,368 @@ struct expand_loc_callback_data
/* The variables and values active at this point. */ /* The variables and values active at this point. */
htab_t vars; htab_t vars;
/* True in vt_expand_loc_dummy calls, no rtl should be allocated. /* Stack of values and debug_exprs under expansion, and their
Non-NULL should be returned if vt_expand_loc would return children. */
non-NULL in that case, NULL otherwise. cur_loc_changed should be VEC (rtx, stack) *expanding;
computed and cur_loc recomputed when possible (but just once
per emit_notes_for_changes call). */ /* Stack of values and debug_exprs whose expansion hit recursion
bool dummy; cycles. They will have VALUE_RECURSED_INTO marked when added to
this list. This flag will be cleared if any of its dependencies
/* True if expansion of subexpressions had to recompute some resolves to a valid location. So, if the flag remains set at the
VALUE/DEBUG_EXPR_DECL's cur_loc or used a VALUE/DEBUG_EXPR_DECL end of the search, we know no valid location for this one can
whose cur_loc has been already recomputed during current possibly exist. */
emit_notes_for_changes call. */ VEC (rtx, stack) *pending;
bool cur_loc_changed;
/* The maximum depth among the sub-expressions under expansion.
/* True if cur_loc should be ignored and any possible location Zero indicates no expansion so far. */
returned. */ int depth;
bool ignore_cur_loc;
}; };
/* Allocate the one-part auxiliary data structure for VAR, with enough
room for COUNT dependencies. */
static void
loc_exp_dep_alloc (variable var, int count)
{
size_t allocsize;
gcc_checking_assert (var->onepart);
/* We can be called with COUNT == 0 to allocate the data structure
without any dependencies, e.g. for the backlinks only. However,
if we are specifying a COUNT, then the dependency list must have
been emptied before. It would be possible to adjust pointers or
force it empty here, but this is better done at an earlier point
in the algorithm, so we instead leave an assertion to catch
errors. */
gcc_checking_assert (!count
|| VEC_empty (loc_exp_dep, VAR_LOC_DEP_VEC (var)));
if (VAR_LOC_1PAUX (var)
&& VEC_space (loc_exp_dep, VAR_LOC_DEP_VEC (var), count))
return;
allocsize = offsetof (struct onepart_aux, deps)
+ VEC_embedded_size (loc_exp_dep, count);
if (VAR_LOC_1PAUX (var))
{
VAR_LOC_1PAUX (var) = XRESIZEVAR (struct onepart_aux,
VAR_LOC_1PAUX (var), allocsize);
/* If the reallocation moves the onepaux structure, the
back-pointer to BACKLINKS in the first list member will still
point to its old location. Adjust it. */
if (VAR_LOC_DEP_LST (var))
VAR_LOC_DEP_LST (var)->pprev = VAR_LOC_DEP_LSTP (var);
}
else
{
VAR_LOC_1PAUX (var) = XNEWVAR (struct onepart_aux, allocsize);
*VAR_LOC_DEP_LSTP (var) = NULL;
VAR_LOC_FROM (var) = NULL;
VAR_LOC_DEPTH (var) = 0;
}
VEC_embedded_init (loc_exp_dep, VAR_LOC_DEP_VEC (var), count);
}
/* Remove all entries from the vector of active dependencies of VAR,
removing them from the back-links lists too. */
static void
loc_exp_dep_clear (variable var)
{
while (!VEC_empty (loc_exp_dep, VAR_LOC_DEP_VEC (var)))
{
loc_exp_dep *led = VEC_last (loc_exp_dep, VAR_LOC_DEP_VEC (var));
if (led->next)
led->next->pprev = led->pprev;
if (led->pprev)
*led->pprev = led->next;
VEC_pop (loc_exp_dep, VAR_LOC_DEP_VEC (var));
}
}
/* Insert an active dependency from VAR on X to the vector of
dependencies, and add the corresponding back-link to X's list of
back-links in VARS. */
static void
loc_exp_insert_dep (variable var, rtx x, htab_t vars)
{
decl_or_value dv;
variable xvar;
loc_exp_dep *led;
dv = dv_from_rtx (x);
/* ??? Build a vector of variables parallel to EXPANDING, to avoid
an additional look up? */
xvar = (variable) htab_find_with_hash (vars, dv, dv_htab_hash (dv));
if (!xvar)
{
xvar = variable_from_dropped (dv, NO_INSERT);
gcc_checking_assert (xvar);
}
/* No point in adding the same backlink more than once. This may
arise if say the same value appears in two complex expressions in
the same loc_list, or even more than once in a single
expression. */
if (VAR_LOC_DEP_LST (xvar) && VAR_LOC_DEP_LST (xvar)->dv == var->dv)
return;
VEC_quick_push (loc_exp_dep, VAR_LOC_DEP_VEC (var), NULL);
led = VEC_last (loc_exp_dep, VAR_LOC_DEP_VEC (var));
led->dv = var->dv;
led->value = x;
loc_exp_dep_alloc (xvar, 0);
led->pprev = VAR_LOC_DEP_LSTP (xvar);
led->next = *led->pprev;
if (led->next)
led->next->pprev = &led->next;
*led->pprev = led;
}
/* Create active dependencies of VAR on COUNT values starting at
VALUE, and corresponding back-links to the entries in VARS. Return
true if we found any pending-recursion results. */
static bool
loc_exp_dep_set (variable var, rtx result, rtx *value, int count, htab_t vars)
{
bool pending_recursion = false;
gcc_checking_assert (VEC_empty (loc_exp_dep, VAR_LOC_DEP_VEC (var)));
/* Set up all dependencies from last_child (as set up at the end of
the loop above) to the end. */
loc_exp_dep_alloc (var, count);
while (count--)
{
rtx x = *value++;
if (!pending_recursion)
pending_recursion = !result && VALUE_RECURSED_INTO (x);
loc_exp_insert_dep (var, x, vars);
}
return pending_recursion;
}
/* Notify the back-links of IVAR that are pending recursion that we
have found a non-NIL value for it, so they are cleared for another
attempt to compute a current location. */
static void
notify_dependents_of_resolved_value (variable ivar, htab_t vars)
{
loc_exp_dep *led, *next;
for (led = VAR_LOC_DEP_LST (ivar); led; led = next)
{
decl_or_value dv = led->dv;
variable var;
next = led->next;
if (dv_is_value_p (dv))
{
rtx value = dv_as_value (dv);
/* If we have already resolved it, leave it alone. */
if (!VALUE_RECURSED_INTO (value))
continue;
/* Check that VALUE_RECURSED_INTO, true from the test above,
implies NO_LOC_P. */
gcc_checking_assert (NO_LOC_P (value));
/* We won't notify variables that are being expanded,
because their dependency list is cleared before
recursing. */
VALUE_RECURSED_INTO (value) = false;
gcc_checking_assert (dv_changed_p (dv));
}
else if (!dv_changed_p (dv))
continue;
var = (variable) htab_find_with_hash (vars, dv, dv_htab_hash (dv));
if (!var)
var = variable_from_dropped (dv, NO_INSERT);
if (var)
notify_dependents_of_resolved_value (var, vars);
if (next)
next->pprev = led->pprev;
if (led->pprev)
*led->pprev = next;
led->next = NULL;
led->pprev = NULL;
}
}
static rtx vt_expand_loc_callback (rtx x, bitmap regs,
int max_depth, void *data);
/* Return the combined depth, when one sub-expression evaluated to
BEST_DEPTH and the previous known depth was SAVED_DEPTH. */
static inline int
update_depth (int saved_depth, int best_depth)
{
/* If we didn't find anything, stick with what we had. */
if (!best_depth)
return saved_depth;
/* If we found hadn't found anything, use the depth of the current
expression. Do NOT add one extra level, we want to compute the
maximum depth among sub-expressions. We'll increment it later,
if appropriate. */
if (!saved_depth)
return best_depth;
if (saved_depth < best_depth)
return best_depth;
else
return saved_depth;
}
/* Expand VAR to a location RTX, updating its cur_loc. Use REGS and
DATA for cselib expand callback. If PENDRECP is given, indicate in
it whether any sub-expression couldn't be fully evaluated because
it is pending recursion resolution. */
static inline rtx
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;
location_chain loc, next;
rtx result = NULL;
int first_child, result_first_child, last_child;
bool pending_recursion;
rtx loc_from = NULL;
struct elt_loc_list *cloc = NULL;
int depth, saved_depth = elcd->depth;
/* Clear all backlinks pointing at this, so that we're not notified
while we're active. */
loc_exp_dep_clear (var);
if (var->onepart == ONEPART_VALUE)
{
cselib_val *val = CSELIB_VAL_PTR (dv_as_value (var->dv));
gcc_checking_assert (cselib_preserved_value_p (val));
cloc = val->locs;
}
first_child = result_first_child = last_child
= VEC_length (rtx, elcd->expanding);
/* Attempt to expand each available location in turn. */
for (next = loc = var->n_var_parts ? var->var_part[0].loc_chain : NULL;
loc || cloc; loc = next)
{
result_first_child = last_child;
if (!loc || (GET_CODE (loc->loc) == ENTRY_VALUE && cloc))
{
loc_from = cloc->loc;
next = loc;
cloc = cloc->next;
if (unsuitable_loc (loc_from))
continue;
}
else
{
loc_from = loc->loc;
next = loc->next;
}
gcc_checking_assert (!unsuitable_loc (loc_from));
elcd->depth = 0;
result = cselib_expand_value_rtx_cb (loc_from, regs, EXPR_DEPTH,
vt_expand_loc_callback, data);
last_child = VEC_length (rtx, elcd->expanding);
if (result)
{
depth = elcd->depth;
gcc_checking_assert (depth || result_first_child == last_child);
if (last_child - result_first_child != 1)
depth++;
if (depth <= EXPR_USE_DEPTH)
break;
result = NULL;
}
/* Set it up in case we leave the loop. */
depth = 0;
loc_from = NULL;
result_first_child = first_child;
}
/* Register all encountered dependencies as active. */
pending_recursion = loc_exp_dep_set
(var, result, VEC_address (rtx, elcd->expanding) + result_first_child,
last_child - result_first_child, elcd->vars);
VEC_truncate (rtx, elcd->expanding, first_child);
/* Record where the expansion came from. */
gcc_checking_assert (!result || !pending_recursion);
VAR_LOC_FROM (var) = loc_from;
VAR_LOC_DEPTH (var) = depth;
elcd->depth = update_depth (saved_depth, depth);
/* Indicate whether any of the dependencies are pending recursion
resolution. */
if (pendrecp)
*pendrecp = pending_recursion;
if (!pendrecp || !pending_recursion)
var->var_part[0].cur_loc = result;
return result;
}
/* Callback for cselib_expand_value, that looks for expressions /* Callback for cselib_expand_value, that looks for expressions
holding the value in the var-tracking hash tables. Return X for holding the value in the var-tracking hash tables. Return X for
standard processing, anything else is to be used as-is. */ standard processing, anything else is to be used as-is. */
static rtx static rtx
vt_expand_loc_callback (rtx x, bitmap regs, int max_depth, void *data) vt_expand_loc_callback (rtx x, bitmap regs,
int max_depth ATTRIBUTE_UNUSED,
void *data)
{ {
struct expand_loc_callback_data *elcd struct expand_loc_callback_data *elcd
= (struct expand_loc_callback_data *) data; = (struct expand_loc_callback_data *) data;
bool dummy = elcd->dummy;
bool cur_loc_changed = elcd->cur_loc_changed;
rtx cur_loc;
decl_or_value dv; decl_or_value dv;
variable var; variable var;
location_chain loc; rtx result, subreg;
rtx result, subreg, xret; bool pending_recursion = false;
bool from_empty = false;
switch (GET_CODE (x)) switch (GET_CODE (x))
{ {
case SUBREG: case SUBREG:
if (dummy)
{
if (cselib_dummy_expand_value_rtx_cb (SUBREG_REG (x), regs,
max_depth - 1,
vt_expand_loc_callback, data))
return pc_rtx;
else
return NULL;
}
subreg = cselib_expand_value_rtx_cb (SUBREG_REG (x), regs, subreg = cselib_expand_value_rtx_cb (SUBREG_REG (x), regs,
max_depth - 1, EXPR_DEPTH,
vt_expand_loc_callback, data); vt_expand_loc_callback, data);
if (!subreg) if (!subreg)
...@@ -7395,148 +7876,172 @@ vt_expand_loc_callback (rtx x, bitmap regs, int max_depth, void *data) ...@@ -7395,148 +7876,172 @@ vt_expand_loc_callback (rtx x, bitmap regs, int max_depth, void *data)
return result; return result;
case DEBUG_EXPR: case DEBUG_EXPR:
dv = dv_from_decl (DEBUG_EXPR_TREE_DECL (x));
xret = NULL;
break;
case VALUE: case VALUE:
dv = dv_from_value (x); dv = dv_from_rtx (x);
xret = x;
break; break;
default: default:
return x; return x;
} }
if (VALUE_RECURSED_INTO (x)) VEC_safe_push (rtx, stack, elcd->expanding, x);
/* Check that VALUE_RECURSED_INTO implies NO_LOC_P. */
gcc_checking_assert (!VALUE_RECURSED_INTO (x) || NO_LOC_P (x));
if (NO_LOC_P (x))
return NULL; return NULL;
var = (variable) htab_find_with_hash (elcd->vars, dv, dv_htab_hash (dv)); var = (variable) htab_find_with_hash (elcd->vars, dv, dv_htab_hash (dv));
if (!var) if (!var)
{ {
if (dummy && dv_changed_p (dv)) from_empty = true;
elcd->cur_loc_changed = true; var = variable_from_dropped (dv, INSERT);
return xret;
} }
if (var->n_var_parts == 0) gcc_checking_assert (var);
if (!dv_changed_p (dv))
{ {
if (dummy) gcc_checking_assert (!NO_LOC_P (x));
elcd->cur_loc_changed = true; gcc_checking_assert (var->var_part[0].cur_loc);
return xret; gcc_checking_assert (VAR_LOC_1PAUX (var));
} gcc_checking_assert (VAR_LOC_1PAUX (var)->depth);
gcc_assert (var->n_var_parts == 1); elcd->depth = update_depth (elcd->depth, VAR_LOC_1PAUX (var)->depth);
return var->var_part[0].cur_loc;
}
VALUE_RECURSED_INTO (x) = true; VALUE_RECURSED_INTO (x) = true;
result = NULL; /* This is tentative, but it makes some tests simpler. */
NO_LOC_P (x) = true;
if (var->var_part[0].cur_loc && !elcd->ignore_cur_loc) gcc_checking_assert (var->n_var_parts == 1 || from_empty);
{
if (dummy) result = vt_expand_var_loc_chain (var, regs, data, &pending_recursion);
if (pending_recursion)
{ {
if (cselib_dummy_expand_value_rtx_cb (var->var_part[0].cur_loc, regs, gcc_checking_assert (!result);
max_depth, VEC_safe_push (rtx, stack, elcd->pending, x);
vt_expand_loc_callback, data))
result = pc_rtx;
}
else
result = cselib_expand_value_rtx_cb (var->var_part[0].cur_loc, regs,
max_depth,
vt_expand_loc_callback, data);
if (result)
set_dv_changed (dv, false);
cur_loc = var->var_part[0].cur_loc;
} }
else else
cur_loc = NULL_RTX;
if (!result && (dv_changed_p (dv) || elcd->ignore_cur_loc))
{ {
if (!elcd->ignore_cur_loc) NO_LOC_P (x) = !result;
VALUE_RECURSED_INTO (x) = false;
set_dv_changed (dv, false); set_dv_changed (dv, false);
for (loc = var->var_part[0].loc_chain; loc; loc = loc->next)
if (loc->loc == cur_loc)
continue;
else if (dummy)
{
elcd->cur_loc_changed = cur_loc_changed;
if (cselib_dummy_expand_value_rtx_cb (loc->loc, regs, max_depth,
vt_expand_loc_callback,
data))
{
result = pc_rtx;
break;
}
}
else
{
result = cselib_expand_value_rtx_cb (loc->loc, regs, max_depth,
vt_expand_loc_callback, data);
if (result) if (result)
break; notify_dependents_of_resolved_value (var, elcd->vars);
}
if (dummy && (result || var->var_part[0].cur_loc))
var->cur_loc_changed = true;
if (!elcd->ignore_cur_loc)
var->var_part[0].cur_loc = loc ? loc->loc : NULL_RTX;
} }
if (dummy)
return result;
}
/* While expanding variables, we may encounter recursion cycles
because of mutual (possibly indirect) dependencies between two
particular variables (or values), say A and B. If we're trying to
expand A when we get to B, which in turn attempts to expand A, if
we can't find any other expansion for B, we'll add B to this
pending-recursion stack, and tentatively return NULL for its
location. This tentative value will be used for any other
occurrences of B, unless A gets some other location, in which case
it will notify B that it is worth another try at computing a
location for it, and it will use the location computed for A then.
At the end of the expansion, the tentative NULL locations become
final for all members of PENDING that didn't get a notification.
This function performs this finalization of NULL locations. */
static void
resolve_expansions_pending_recursion (VEC (rtx, stack) *pending)
{
while (!VEC_empty (rtx, pending))
{ {
if (var->cur_loc_changed) rtx x = VEC_pop (rtx, pending);
elcd->cur_loc_changed = true; decl_or_value dv;
else if (!result && var->var_part[0].cur_loc == NULL_RTX)
elcd->cur_loc_changed = cur_loc_changed;
}
if (!VALUE_RECURSED_INTO (x))
continue;
gcc_checking_assert (NO_LOC_P (x));
VALUE_RECURSED_INTO (x) = false; VALUE_RECURSED_INTO (x) = false;
if (result) dv = dv_from_rtx (x);
return result; gcc_checking_assert (dv_changed_p (dv));
else set_dv_changed (dv, false);
return xret; }
} }
/* Expand VALUEs in LOC, using VARS as well as cselib's equivalence /* Initialize expand_loc_callback_data D with variable hash table V.
tables. */ It must be a macro because of alloca (VEC stack). */
#define INIT_ELCD(d, v) \
do \
{ \
(d).vars = (v); \
(d).expanding = VEC_alloc (rtx, stack, 4); \
(d).pending = VEC_alloc (rtx, stack, 4); \
(d).depth = 0; \
} \
while (0)
/* Finalize expand_loc_callback_data D, resolved to location L. */
#define FINI_ELCD(d, l) \
do \
{ \
resolve_expansions_pending_recursion ((d).pending); \
VEC_free (rtx, stack, (d).pending); \
VEC_free (rtx, stack, (d).expanding); \
\
if ((l) && MEM_P (l)) \
(l) = targetm.delegitimize_address (l); \
} \
while (0)
/* Expand VALUEs and DEBUG_EXPRs in LOC to a location, using the
equivalences in VARS, updating their CUR_LOCs in the process. */
static rtx static rtx
vt_expand_loc (rtx loc, htab_t vars, bool ignore_cur_loc) vt_expand_loc (rtx loc, htab_t vars)
{ {
struct expand_loc_callback_data data; struct expand_loc_callback_data data;
rtx result;
if (!MAY_HAVE_DEBUG_INSNS) if (!MAY_HAVE_DEBUG_INSNS)
return loc; return loc;
data.vars = vars; INIT_ELCD (data, vars);
data.dummy = false;
data.cur_loc_changed = false; result = cselib_expand_value_rtx_cb (loc, scratch_regs, EXPR_DEPTH,
data.ignore_cur_loc = ignore_cur_loc;
loc = cselib_expand_value_rtx_cb (loc, scratch_regs, EXPR_DEPTH,
vt_expand_loc_callback, &data); vt_expand_loc_callback, &data);
if (loc && MEM_P (loc)) FINI_ELCD (data, result);
loc = targetm.delegitimize_address (loc);
return loc; return result;
} }
/* Like vt_expand_loc, but only return true/false (whether vt_expand_loc /* Expand the one-part VARiable to a location, using the equivalences
would succeed or not, without actually allocating new rtxes. */ in VARS, updating their CUR_LOCs in the process. */
static bool static rtx
vt_expand_loc_dummy (rtx loc, htab_t vars, bool *pcur_loc_changed) vt_expand_1pvar (variable var, htab_t vars)
{ {
struct expand_loc_callback_data data; struct expand_loc_callback_data data;
bool ret; rtx loc;
gcc_assert (MAY_HAVE_DEBUG_INSNS); gcc_checking_assert (var->onepart && var->n_var_parts == 1);
data.vars = vars;
data.dummy = true; if (!dv_changed_p (var->dv))
data.cur_loc_changed = false; return var->var_part[0].cur_loc;
data.ignore_cur_loc = false;
ret = cselib_dummy_expand_value_rtx_cb (loc, scratch_regs, EXPR_DEPTH, INIT_ELCD (data, vars);
vt_expand_loc_callback, &data);
*pcur_loc_changed = data.cur_loc_changed; loc = vt_expand_var_loc_chain (var, scratch_regs, &data, NULL);
return ret;
gcc_checking_assert (VEC_empty (rtx, data.expanding));
FINI_ELCD (data, loc);
return loc;
} }
/* Emit the NOTE_INSN_VAR_LOCATION for variable *VARP. DATA contains /* Emit the NOTE_INSN_VAR_LOCATION for variable *VARP. DATA contains
...@@ -7561,49 +8066,57 @@ emit_note_insn_var_location (void **varp, void *data) ...@@ -7561,49 +8066,57 @@ emit_note_insn_var_location (void **varp, void *data)
tree decl; tree decl;
location_chain lc; location_chain lc;
if (dv_is_value_p (var->dv)) gcc_checking_assert (var->onepart == NOT_ONEPART
goto value_or_debug_decl; || var->onepart == ONEPART_VDECL);
decl = dv_as_decl (var->dv); decl = dv_as_decl (var->dv);
if (TREE_CODE (decl) == DEBUG_EXPR_DECL)
goto value_or_debug_decl;
complete = true; complete = true;
last_limit = 0; last_limit = 0;
n_var_parts = 0; n_var_parts = 0;
if (!MAY_HAVE_DEBUG_INSNS) if (!var->onepart)
{
for (i = 0; i < var->n_var_parts; i++) for (i = 0; i < var->n_var_parts; i++)
if (var->var_part[i].cur_loc == NULL && var->var_part[i].loc_chain) if (var->var_part[i].cur_loc == NULL && var->var_part[i].loc_chain)
{
var->var_part[i].cur_loc = var->var_part[i].loc_chain->loc; var->var_part[i].cur_loc = var->var_part[i].loc_chain->loc;
var->cur_loc_changed = true;
}
if (var->n_var_parts == 0)
var->cur_loc_changed = true;
}
if (!var->cur_loc_changed)
goto clear;
for (i = 0; i < var->n_var_parts; i++) for (i = 0; i < var->n_var_parts; i++)
{ {
enum machine_mode mode, wider_mode; enum machine_mode mode, wider_mode;
rtx loc2; rtx loc2;
HOST_WIDE_INT offset;
if (last_limit < var->var_part[i].offset) if (i == 0 && var->onepart)
{
gcc_checking_assert (var->n_var_parts == 1);
offset = 0;
initialized = VAR_INIT_STATUS_INITIALIZED;
loc2 = vt_expand_1pvar (var, vars);
}
else
{
if (last_limit < VAR_PART_OFFSET (var, i))
{ {
complete = false; complete = false;
break; break;
} }
else if (last_limit > var->var_part[i].offset) else if (last_limit > VAR_PART_OFFSET (var, i))
continue; continue;
offsets[n_var_parts] = var->var_part[i].offset; offset = VAR_PART_OFFSET (var, i);
if (!var->var_part[i].cur_loc) if (!var->var_part[i].cur_loc)
{ {
complete = false; complete = false;
continue; continue;
} }
loc2 = vt_expand_loc (var->var_part[i].cur_loc, vars, false); for (lc = var->var_part[i].loc_chain; lc; lc = lc->next)
if (var->var_part[i].cur_loc == lc->loc)
{
initialized = lc->init;
break;
}
gcc_assert (lc);
loc2 = var->var_part[i].cur_loc;
}
offsets[n_var_parts] = offset;
if (!loc2) if (!loc2)
{ {
complete = false; complete = false;
...@@ -7611,29 +8124,22 @@ emit_note_insn_var_location (void **varp, void *data) ...@@ -7611,29 +8124,22 @@ emit_note_insn_var_location (void **varp, void *data)
} }
loc[n_var_parts] = loc2; loc[n_var_parts] = loc2;
mode = GET_MODE (var->var_part[i].cur_loc); mode = GET_MODE (var->var_part[i].cur_loc);
if (mode == VOIDmode && dv_onepart_p (var->dv)) if (mode == VOIDmode && var->onepart)
mode = DECL_MODE (decl); mode = DECL_MODE (decl);
for (lc = var->var_part[i].loc_chain; lc; lc = lc->next)
if (var->var_part[i].cur_loc == lc->loc)
{
initialized = lc->init;
break;
}
gcc_assert (lc);
last_limit = offsets[n_var_parts] + GET_MODE_SIZE (mode); last_limit = offsets[n_var_parts] + GET_MODE_SIZE (mode);
/* Attempt to merge adjacent registers or memory. */ /* Attempt to merge adjacent registers or memory. */
wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode = GET_MODE_WIDER_MODE (mode);
for (j = i + 1; j < var->n_var_parts; j++) for (j = i + 1; j < var->n_var_parts; j++)
if (last_limit <= var->var_part[j].offset) if (last_limit <= VAR_PART_OFFSET (var, j))
break; break;
if (j < var->n_var_parts if (j < var->n_var_parts
&& wider_mode != VOIDmode && wider_mode != VOIDmode
&& var->var_part[j].cur_loc && var->var_part[j].cur_loc
&& mode == GET_MODE (var->var_part[j].cur_loc) && mode == GET_MODE (var->var_part[j].cur_loc)
&& (REG_P (loc[n_var_parts]) || MEM_P (loc[n_var_parts])) && (REG_P (loc[n_var_parts]) || MEM_P (loc[n_var_parts]))
&& last_limit == var->var_part[j].offset && last_limit == (var->onepart ? 0 : VAR_PART_OFFSET (var, j))
&& (loc2 = vt_expand_loc (var->var_part[j].cur_loc, vars, false)) && (loc2 = vt_expand_loc (var->var_part[j].cur_loc, vars))
&& GET_CODE (loc[n_var_parts]) == GET_CODE (loc2)) && GET_CODE (loc[n_var_parts]) == GET_CODE (loc2))
{ {
rtx new_loc = NULL; rtx new_loc = NULL;
...@@ -7746,152 +8252,152 @@ emit_note_insn_var_location (void **varp, void *data) ...@@ -7746,152 +8252,152 @@ emit_note_insn_var_location (void **varp, void *data)
} }
NOTE_VAR_LOCATION (note) = note_vl; NOTE_VAR_LOCATION (note) = note_vl;
clear:
set_dv_changed (var->dv, false); set_dv_changed (var->dv, false);
var->cur_loc_changed = false;
gcc_assert (var->in_changed_variables); gcc_assert (var->in_changed_variables);
var->in_changed_variables = false; var->in_changed_variables = false;
htab_clear_slot (changed_variables, varp); htab_clear_slot (changed_variables, varp);
/* Continue traversing the hash table. */ /* Continue traversing the hash table. */
return 1; return 1;
value_or_debug_decl:
if (dv_changed_p (var->dv) && var->n_var_parts)
{
location_chain lc;
bool cur_loc_changed;
if (var->var_part[0].cur_loc
&& vt_expand_loc_dummy (var->var_part[0].cur_loc, vars,
&cur_loc_changed))
goto clear;
for (lc = var->var_part[0].loc_chain; lc; lc = lc->next)
if (lc->loc != var->var_part[0].cur_loc
&& vt_expand_loc_dummy (lc->loc, vars, &cur_loc_changed))
break;
var->var_part[0].cur_loc = lc ? lc->loc : NULL_RTX;
}
goto clear;
} }
DEF_VEC_P (variable); /* While traversing changed_variables, push onto DATA (a stack of RTX
DEF_VEC_ALLOC_P (variable, heap); values) entries that aren't user variables. */
/* Stack of variable_def pointers that need processing with
check_changed_vars_2. */
static VEC (variable, heap) *changed_variables_stack; static int
values_to_stack (void **slot, void *data)
/* VALUEs with no variables that need set_dv_changed (val, false) {
called before check_changed_vars_3. */ VEC (rtx, stack) **changed_values_stack = (VEC (rtx, stack) **)data;
variable var = (variable) *slot;
static VEC (rtx, heap) *changed_values_stack; if (var->onepart == ONEPART_VALUE)
VEC_safe_push (rtx, stack, *changed_values_stack, dv_as_value (var->dv));
else if (var->onepart == ONEPART_DEXPR)
VEC_safe_push (rtx, stack, *changed_values_stack,
DECL_RTL_KNOWN_SET (dv_as_decl (var->dv)));
/* Helper function for check_changed_vars_1 and check_changed_vars_2. */ return 1;
}
/* Remove from changed_variables the entry whose DV corresponds to
value or debug_expr VAL. */
static void static void
check_changed_vars_0 (decl_or_value dv, htab_t htab) remove_value_from_changed_variables (rtx val)
{ {
value_chain vc decl_or_value dv = dv_from_rtx (val);
= (value_chain) htab_find_with_hash (value_chains, dv, dv_htab_hash (dv)); void **slot;
variable var;
if (vc == NULL) slot = htab_find_slot_with_hash (changed_variables,
return; dv, dv_htab_hash (dv), NO_INSERT);
for (vc = vc->next; vc; vc = vc->next) var = (variable) *slot;
if (!dv_changed_p (vc->dv)) var->in_changed_variables = false;
{ htab_clear_slot (changed_variables, slot);
variable vcvar
= (variable) htab_find_with_hash (htab, vc->dv,
dv_htab_hash (vc->dv));
if (vcvar)
{
set_dv_changed (vc->dv, true);
VEC_safe_push (variable, heap, changed_variables_stack, vcvar);
}
else if (dv_is_value_p (vc->dv))
{
set_dv_changed (vc->dv, true);
VEC_safe_push (rtx, heap, changed_values_stack,
dv_as_value (vc->dv));
check_changed_vars_0 (vc->dv, htab);
}
}
} }
/* Populate changed_variables_stack with variable_def pointers /* If VAL (a value or debug_expr) has backlinks to variables actively
that need variable_was_changed called on them. */ dependent on it in HTAB or in CHANGED_VARIABLES, mark them as
changed, adding to CHANGED_VALUES_STACK any dependencies that may
have dependencies of their own to notify. */
static int static void
check_changed_vars_1 (void **slot, void *data) notify_dependents_of_changed_value (rtx val, htab_t htab,
VEC (rtx, stack) **changed_values_stack)
{ {
variable var = (variable) *slot; void **slot;
htab_t htab = (htab_t) data; variable var;
loc_exp_dep *led;
decl_or_value dv = dv_from_rtx (val);
if (dv_is_value_p (var->dv) slot = htab_find_slot_with_hash (changed_variables,
|| TREE_CODE (dv_as_decl (var->dv)) == DEBUG_EXPR_DECL) dv, dv_htab_hash (dv), NO_INSERT);
check_changed_vars_0 (var->dv, htab); if (!slot)
return 1; slot = htab_find_slot_with_hash (htab,
} dv, dv_htab_hash (dv), NO_INSERT);
if (!slot)
slot = htab_find_slot_with_hash (dropped_values,
dv, dv_htab_hash (dv), NO_INSERT);
var = (variable) *slot;
/* Add VAR to changed_variables and also for VALUEs add recursively while ((led = VAR_LOC_DEP_LST (var)))
all DVs that aren't in changed_variables yet but reference the {
VALUE from its loc_chain. */ decl_or_value ldv = led->dv;
void **islot;
variable ivar;
static void /* Deactivate and remove the backlink, as it was “used up”. It
check_changed_vars_2 (variable var, htab_t htab) makes no sense to attempt to notify the same entity again:
{ either it will be recomputed and re-register an active
variable_was_changed (var, NULL); dependency, or it will still have the changed mark. */
if (dv_is_value_p (var->dv) if (led->next)
|| TREE_CODE (dv_as_decl (var->dv)) == DEBUG_EXPR_DECL) led->next->pprev = led->pprev;
check_changed_vars_0 (var->dv, htab); if (led->pprev)
*led->pprev = led->next;
led->next = NULL;
led->pprev = NULL;
if (dv_changed_p (ldv))
continue;
switch (dv_onepart_p (ldv))
{
case ONEPART_VALUE:
case ONEPART_DEXPR:
set_dv_changed (ldv, true);
VEC_safe_push (rtx, stack, *changed_values_stack, dv_as_rtx (ldv));
break;
default:
islot = htab_find_slot_with_hash (htab, ldv, dv_htab_hash (ldv),
NO_INSERT);
ivar = (variable) *islot;
gcc_checking_assert (!VAR_LOC_DEP_LST (ivar));
variable_was_changed (ivar, NULL);
break;
}
}
} }
/* For each changed decl (except DEBUG_EXPR_DECLs) recompute /* Take out of changed_variables any entries that don't refer to use
cur_loc if needed (and cur_loc of all VALUEs and DEBUG_EXPR_DECLs variables. Back-propagate change notifications from values and
it needs and are also in changed variables) and track whether debug_exprs to their active dependencies in HTAB or in
cur_loc (or anything it uses to compute location) had to change CHANGED_VARIABLES. */
during the current emit_notes_for_changes call. */
static int static void
check_changed_vars_3 (void **slot, void *data) process_changed_values (htab_t htab)
{ {
variable var = (variable) *slot; int i, n;
htab_t vars = (htab_t) data; rtx val;
int i; VEC (rtx, stack) *changed_values_stack = VEC_alloc (rtx, stack, 20);
location_chain lc;
bool cur_loc_changed;
if (dv_is_value_p (var->dv) /* Move values from changed_variables to changed_values_stack. */
|| TREE_CODE (dv_as_decl (var->dv)) == DEBUG_EXPR_DECL) htab_traverse (changed_variables, values_to_stack, &changed_values_stack);
return 1;
for (i = 0; i < var->n_var_parts; i++) /* Back-propagate change notifications in values while popping
them from the stack. */
for (n = i = VEC_length (rtx, changed_values_stack);
i > 0; i = VEC_length (rtx, changed_values_stack))
{ {
if (var->var_part[i].cur_loc val = VEC_pop (rtx, changed_values_stack);
&& vt_expand_loc_dummy (var->var_part[i].cur_loc, vars, notify_dependents_of_changed_value (val, htab, &changed_values_stack);
&cur_loc_changed))
/* This condition will hold when visiting each of the entries
originally in changed_variables. We can't remove them
earlier because this could drop the backlinks before we got a
chance to use them. */
if (i == n)
{ {
if (cur_loc_changed) remove_value_from_changed_variables (val);
var->cur_loc_changed = true; n--;
continue;
} }
for (lc = var->var_part[i].loc_chain; lc; lc = lc->next)
if (lc->loc != var->var_part[i].cur_loc
&& vt_expand_loc_dummy (lc->loc, vars, &cur_loc_changed))
break;
if (lc || var->var_part[i].cur_loc)
var->cur_loc_changed = true;
var->var_part[i].cur_loc = lc ? lc->loc : NULL_RTX;
} }
if (var->n_var_parts == 0)
var->cur_loc_changed = true; VEC_free (rtx, stack, changed_values_stack);
return 1;
} }
/* Emit NOTE_INSN_VAR_LOCATION note for each variable from a chain /* Emit NOTE_INSN_VAR_LOCATION note for each variable from a chain
CHANGED_VARIABLES and delete this chain. WHERE specifies whether the notes CHANGED_VARIABLES and delete this chain. WHERE specifies whether
shall be emitted before of after instruction INSN. */ the notes shall be emitted before of after instruction INSN. */
static void static void
emit_notes_for_changes (rtx insn, enum emit_note_where where, emit_notes_for_changes (rtx insn, enum emit_note_where where,
...@@ -7904,19 +8410,7 @@ emit_notes_for_changes (rtx insn, enum emit_note_where where, ...@@ -7904,19 +8410,7 @@ emit_notes_for_changes (rtx insn, enum emit_note_where where,
return; return;
if (MAY_HAVE_DEBUG_INSNS) if (MAY_HAVE_DEBUG_INSNS)
{ process_changed_values (htab);
/* Unfortunately this has to be done in two steps, because
we can't traverse a hashtab into which we are inserting
through variable_was_changed. */
htab_traverse (changed_variables, check_changed_vars_1, htab);
while (VEC_length (variable, changed_variables_stack) > 0)
check_changed_vars_2 (VEC_pop (variable, changed_variables_stack),
htab);
while (VEC_length (rtx, changed_values_stack) > 0)
set_dv_changed (dv_from_value (VEC_pop (rtx, changed_values_stack)),
false);
htab_traverse (changed_variables, check_changed_vars_3, htab);
}
data.insn = insn; data.insn = insn;
data.where = where; data.where = where;
...@@ -7941,78 +8435,59 @@ emit_notes_for_differences_1 (void **slot, void *data) ...@@ -7941,78 +8435,59 @@ emit_notes_for_differences_1 (void **slot, void *data)
if (!new_var) if (!new_var)
{ {
/* Variable has disappeared. */ /* Variable has disappeared. */
variable empty_var; variable empty_var = NULL;
empty_var = (variable) pool_alloc (dv_pool (old_var->dv)); if (old_var->onepart == ONEPART_VALUE
|| old_var->onepart == ONEPART_DEXPR)
{
empty_var = variable_from_dropped (old_var->dv, NO_INSERT);
if (empty_var)
{
gcc_checking_assert (!empty_var->in_changed_variables);
if (!VAR_LOC_1PAUX (old_var))
{
VAR_LOC_1PAUX (old_var) = VAR_LOC_1PAUX (empty_var);
VAR_LOC_1PAUX (empty_var) = NULL;
}
else
gcc_checking_assert (!VAR_LOC_1PAUX (empty_var));
}
}
if (!empty_var)
{
empty_var = (variable) pool_alloc (onepart_pool (old_var->onepart));
empty_var->dv = old_var->dv; empty_var->dv = old_var->dv;
empty_var->refcount = 0; empty_var->refcount = 0;
empty_var->n_var_parts = 0; empty_var->n_var_parts = 0;
empty_var->cur_loc_changed = false; empty_var->onepart = old_var->onepart;
empty_var->in_changed_variables = false; empty_var->in_changed_variables = false;
if (dv_onepart_p (old_var->dv)) }
{
location_chain lc;
gcc_assert (old_var->n_var_parts == 1); if (empty_var->onepart)
for (lc = old_var->var_part[0].loc_chain; lc; lc = lc->next) {
remove_value_chains (old_var->dv, lc->loc); /* Propagate the auxiliary data to (ultimately)
changed_variables. */
empty_var->var_part[0].loc_chain = NULL;
empty_var->var_part[0].cur_loc = NULL;
VAR_LOC_1PAUX (empty_var) = VAR_LOC_1PAUX (old_var);
VAR_LOC_1PAUX (old_var) = NULL;
} }
variable_was_changed (empty_var, NULL); variable_was_changed (empty_var, NULL);
/* Continue traversing the hash table. */ /* Continue traversing the hash table. */
return 1; return 1;
} }
if (variable_different_p (old_var, new_var)) /* Update cur_loc and one-part auxiliary data, before new_var goes
{ through variable_was_changed. */
if (dv_onepart_p (old_var->dv)) if (old_var != new_var && new_var->onepart)
{
location_chain lc1, lc2;
gcc_assert (old_var->n_var_parts == 1
&& new_var->n_var_parts == 1);
lc1 = old_var->var_part[0].loc_chain;
lc2 = new_var->var_part[0].loc_chain;
while (lc1
&& lc2
&& ((REG_P (lc1->loc) && REG_P (lc2->loc))
|| rtx_equal_p (lc1->loc, lc2->loc)))
{ {
lc1 = lc1->next; gcc_checking_assert (VAR_LOC_1PAUX (new_var) == NULL);
lc2 = lc2->next; VAR_LOC_1PAUX (new_var) = VAR_LOC_1PAUX (old_var);
} VAR_LOC_1PAUX (old_var) = NULL;
for (; lc2; lc2 = lc2->next) new_var->var_part[0].cur_loc = old_var->var_part[0].cur_loc;
add_value_chains (old_var->dv, lc2->loc);
for (; lc1; lc1 = lc1->next)
remove_value_chains (old_var->dv, lc1->loc);
} }
if (variable_different_p (old_var, new_var))
variable_was_changed (new_var, NULL); variable_was_changed (new_var, NULL);
}
/* Update cur_loc. */
if (old_var != new_var)
{
int i;
for (i = 0; i < new_var->n_var_parts; i++)
{
new_var->var_part[i].cur_loc = NULL;
if (old_var->n_var_parts != new_var->n_var_parts
|| old_var->var_part[i].offset != new_var->var_part[i].offset)
new_var->cur_loc_changed = true;
else if (old_var->var_part[i].cur_loc != NULL)
{
location_chain lc;
rtx cur_loc = old_var->var_part[i].cur_loc;
for (lc = new_var->var_part[i].loc_chain; lc; lc = lc->next)
if (lc->loc == cur_loc
|| rtx_equal_p (cur_loc, lc->loc))
{
new_var->var_part[i].cur_loc = lc->loc;
break;
}
if (lc == NULL)
new_var->cur_loc_changed = true;
}
}
}
/* Continue traversing the hash table. */ /* Continue traversing the hash table. */
return 1; return 1;
...@@ -8033,15 +8508,6 @@ emit_notes_for_differences_2 (void **slot, void *data) ...@@ -8033,15 +8508,6 @@ emit_notes_for_differences_2 (void **slot, void *data)
if (!old_var) if (!old_var)
{ {
int i; int i;
/* Variable has appeared. */
if (dv_onepart_p (new_var->dv))
{
location_chain lc;
gcc_assert (new_var->n_var_parts == 1);
for (lc = new_var->var_part[0].loc_chain; lc; lc = lc->next)
add_value_chains (new_var->dv, lc->loc);
}
for (i = 0; i < new_var->n_var_parts; i++) for (i = 0; i < new_var->n_var_parts; i++)
new_var->var_part[i].cur_loc = NULL; new_var->var_part[i].cur_loc = NULL;
variable_was_changed (new_var, NULL); variable_was_changed (new_var, NULL);
...@@ -8111,7 +8577,7 @@ emit_notes_in_bb (basic_block bb, dataflow_set *set) ...@@ -8111,7 +8577,7 @@ emit_notes_in_bb (basic_block bb, dataflow_set *set)
{ {
XEXP (XEXP (*p, 0), 1) XEXP (XEXP (*p, 0), 1)
= vt_expand_loc (XEXP (XEXP (*p, 0), 1), = vt_expand_loc (XEXP (XEXP (*p, 0), 1),
shared_hash_htab (set->vars), true); shared_hash_htab (set->vars));
/* If expansion is successful, keep it in the list. */ /* If expansion is successful, keep it in the list. */
if (XEXP (XEXP (*p, 0), 1)) if (XEXP (XEXP (*p, 0), 1))
p = &XEXP (*p, 1); p = &XEXP (*p, 1);
...@@ -8412,15 +8878,9 @@ vt_emit_notes (void) ...@@ -8412,15 +8878,9 @@ vt_emit_notes (void)
emit_notes = true; emit_notes = true;
if (MAY_HAVE_DEBUG_INSNS) if (MAY_HAVE_DEBUG_INSNS)
{ dropped_values = htab_create (cselib_get_next_uid () * 2,
unsigned int i; variable_htab_hash, variable_htab_eq,
rtx val; variable_htab_free);
FOR_EACH_VEC_ELT (rtx, preserved_values, i, val)
add_cselib_value_chains (dv_from_value (val));
changed_variables_stack = VEC_alloc (variable, heap, 40);
changed_values_stack = VEC_alloc (rtx, heap, 40);
}
dataflow_set_init (&cur); dataflow_set_init (&cur);
...@@ -8441,23 +8901,11 @@ vt_emit_notes (void) ...@@ -8441,23 +8901,11 @@ vt_emit_notes (void)
htab_traverse (shared_hash_htab (cur.vars), htab_traverse (shared_hash_htab (cur.vars),
emit_notes_for_differences_1, emit_notes_for_differences_1,
shared_hash_htab (empty_shared_hash)); shared_hash_htab (empty_shared_hash));
if (MAY_HAVE_DEBUG_INSNS)
{
unsigned int i;
rtx val;
FOR_EACH_VEC_ELT (rtx, preserved_values, i, val)
remove_cselib_value_chains (dv_from_value (val));
gcc_assert (htab_elements (value_chains) == 0);
}
#endif #endif
dataflow_set_destroy (&cur); dataflow_set_destroy (&cur);
if (MAY_HAVE_DEBUG_INSNS) if (MAY_HAVE_DEBUG_INSNS)
{ htab_delete (dropped_values);
VEC_free (variable, heap, changed_variables_stack);
VEC_free (rtx, heap, changed_values_stack);
}
emit_notes = false; emit_notes = false;
} }
...@@ -8489,36 +8937,27 @@ vt_get_decl_and_offset (rtx rtl, tree *declp, HOST_WIDE_INT *offsetp) ...@@ -8489,36 +8937,27 @@ vt_get_decl_and_offset (rtx rtl, tree *declp, HOST_WIDE_INT *offsetp)
return false; return false;
} }
/* Helper function for vt_add_function_parameter. RTL is /* Mark the value for the ENTRY_VALUE of RTL as equivalent to EQVAL in
the expression and VAL corresponding cselib_val pointer OUT. */
for which ENTRY_VALUE should be created. */
static void static void
create_entry_value (rtx rtl, cselib_val *val) create_entry_value (dataflow_set *out, rtx eqval, rtx rtl)
{ {
cselib_val *val2; rtx ev = gen_rtx_ENTRY_VALUE (GET_MODE (rtl));
struct elt_loc_list *el; cselib_val *val;
el = (struct elt_loc_list *) ggc_alloc_cleared_atomic (sizeof (*el));
el->loc = gen_rtx_ENTRY_VALUE (GET_MODE (rtl)); ENTRY_VALUE_EXP (ev) = rtl;
ENTRY_VALUE_EXP (el->loc) = rtl;
val2 = cselib_lookup_from_insn (el->loc, GET_MODE (rtl), true, val = cselib_lookup_from_insn (ev, GET_MODE (ev), true,
VOIDmode, get_insns ()); VOIDmode, get_insns ());
el->next = val->locs;
el->setting_insn = get_insns ();
val->locs = el;
if (val2
&& val2 != val
&& val2->locs
&& rtx_equal_p (val2->locs->loc, el->loc))
{
struct elt_loc_list *el2;
preserve_value (val2); if (val->val_rtx != eqval)
el2 = (struct elt_loc_list *) ggc_alloc_cleared_atomic (sizeof (*el2)); {
el2->next = val2->locs; preserve_value (val);
el2->loc = val->val_rtx; set_variable_part (out, val->val_rtx, dv_from_value (eqval), 0,
el2->setting_insn = get_insns (); VAR_INIT_STATUS_INITIALIZED, NULL_RTX, INSERT);
val2->locs = el2; set_variable_part (out, eqval, dv_from_value (val->val_rtx), 0,
VAR_INIT_STATUS_INITIALIZED, NULL_RTX, INSERT);
} }
} }
...@@ -8678,20 +9117,22 @@ vt_add_function_parameter (tree parm) ...@@ -8678,20 +9117,22 @@ vt_add_function_parameter (tree parm)
VAR_INIT_STATUS_INITIALIZED, NULL, INSERT); VAR_INIT_STATUS_INITIALIZED, NULL, INSERT);
if (dv_is_value_p (dv)) if (dv_is_value_p (dv))
{ {
cselib_val *val = CSELIB_VAL_PTR (dv_as_value (dv)); create_entry_value (out, dv_as_value (dv), incoming);
create_entry_value (incoming, val);
if (TREE_CODE (TREE_TYPE (parm)) == REFERENCE_TYPE if (TREE_CODE (TREE_TYPE (parm)) == REFERENCE_TYPE
&& INTEGRAL_TYPE_P (TREE_TYPE (TREE_TYPE (parm)))) && INTEGRAL_TYPE_P (TREE_TYPE (TREE_TYPE (parm))))
{ {
enum machine_mode indmode enum machine_mode indmode
= TYPE_MODE (TREE_TYPE (TREE_TYPE (parm))); = TYPE_MODE (TREE_TYPE (TREE_TYPE (parm)));
rtx mem = gen_rtx_MEM (indmode, incoming); rtx mem = gen_rtx_MEM (indmode, incoming);
val = cselib_lookup_from_insn (mem, indmode, true, cselib_val *val = cselib_lookup_from_insn (mem, indmode, true,
VOIDmode, get_insns ()); VOIDmode,
get_insns ());
if (val) if (val)
{ {
preserve_value (val); preserve_value (val);
create_entry_value (mem, val); set_variable_part (out, mem, dv_from_value (val->val_rtx), 0,
VAR_INIT_STATUS_INITIALIZED, NULL, INSERT);
create_entry_value (out, val->val_rtx, mem);
} }
} }
} }
...@@ -8755,23 +9196,6 @@ fp_setter (rtx insn) ...@@ -8755,23 +9196,6 @@ fp_setter (rtx insn)
return false; return false;
} }
/* Gather all registers used for passing arguments to other functions
called from the current routine. */
static void
note_register_arguments (rtx insn)
{
rtx link, x;
for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1))
if (GET_CODE (XEXP (link, 0)) == USE)
{
x = XEXP (XEXP (link, 0), 0);
if (REG_P (x) && REGNO (x) < FIRST_PSEUDO_REGISTER)
SET_HARD_REG_BIT (argument_reg_set, REGNO (x));
}
}
/* Initialize cfa_base_rtx, create a preserved VALUE for it and /* Initialize cfa_base_rtx, create a preserved VALUE for it and
ensure it isn't flushed during cselib_reset_table. ensure it isn't flushed during cselib_reset_table.
Can be called only if frame_pointer_rtx resp. arg_pointer_rtx Can be called only if frame_pointer_rtx resp. arg_pointer_rtx
...@@ -8843,14 +9267,6 @@ vt_initialize (void) ...@@ -8843,14 +9267,6 @@ vt_initialize (void)
variable_htab_free); variable_htab_free);
changed_variables = htab_create (10, variable_htab_hash, variable_htab_eq, changed_variables = htab_create (10, variable_htab_hash, variable_htab_eq,
variable_htab_free); variable_htab_free);
if (MAY_HAVE_DEBUG_INSNS)
{
value_chain_pool = create_alloc_pool ("value_chain_def pool",
sizeof (struct value_chain_def),
1024);
value_chains = htab_create (32, value_chain_htab_hash,
value_chain_htab_eq, NULL);
}
/* Init the IN and OUT sets. */ /* Init the IN and OUT sets. */
FOR_ALL_BB (bb) FOR_ALL_BB (bb)
...@@ -8876,8 +9292,6 @@ vt_initialize (void) ...@@ -8876,8 +9292,6 @@ vt_initialize (void)
valvar_pool = NULL; valvar_pool = NULL;
} }
CLEAR_HARD_REG_SET (argument_reg_set);
/* In order to factor out the adjustments made to the stack pointer or to /* In order to factor out the adjustments made to the stack pointer or to
the hard frame pointer and thus be able to use DW_OP_fbreg operations the hard frame pointer and thus be able to use DW_OP_fbreg operations
instead of individual location lists, we're going to rewrite MEMs based instead of individual location lists, we're going to rewrite MEMs based
...@@ -8962,14 +9376,6 @@ vt_initialize (void) ...@@ -8962,14 +9376,6 @@ vt_initialize (void)
} }
} }
if (frame_pointer_needed)
{
rtx insn;
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
if (CALL_P (insn))
note_register_arguments (insn);
}
hard_frame_pointer_adjustment = -1; hard_frame_pointer_adjustment = -1;
vt_add_function_parameters (); vt_add_function_parameters ();
...@@ -9155,8 +9561,6 @@ vt_finalize (void) ...@@ -9155,8 +9561,6 @@ vt_finalize (void)
if (MAY_HAVE_DEBUG_INSNS) if (MAY_HAVE_DEBUG_INSNS)
{ {
htab_delete (value_chains);
free_alloc_pool (value_chain_pool);
free_alloc_pool (valvar_pool); free_alloc_pool (valvar_pool);
VEC_free (rtx, heap, preserved_values); VEC_free (rtx, heap, preserved_values);
cselib_finish (); cselib_finish ();
...@@ -9164,7 +9568,9 @@ vt_finalize (void) ...@@ -9164,7 +9568,9 @@ vt_finalize (void)
scratch_regs = NULL; scratch_regs = NULL;
} }
#ifdef HAVE_window_save
VEC_free (parm_reg_t, gc, windowed_parm_regs); VEC_free (parm_reg_t, gc, windowed_parm_regs);
#endif
if (vui_vec) if (vui_vec)
XDELETEVEC (vui_vec); XDELETEVEC (vui_vec);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment