2006-12-08 Jakub Jelinek * g++.dg/debug/vartrack1.C: New test. 2006-09-11 Alexandre Oliva PR target/28672 * var-tracking.c (dump_dataflow_set): Start dumping at register zero. (clobber_variable_part): Kill only the variable part in registers holding it, leaving other variables alone. 2006-08-09 Alexandre Oliva * var-tracking.c (enum micro_operation_type): Add MO_COPY. (var_debug_decl): New function. (var_reg_set): Follow debug decl link. Add location even if reg is already known to hold some other variable. (var_mem_set): Follow debug decl link. (var_reg_delete_and_set, var_mem_delete_and_set): Follow debug decl link. Delete other known locations of the variable part if requested. (var_reg_delete, var_mem_delete): Delete other known locations of the variable part if requested. (same_variable_part_p): New function. (add_stores): Select MO_COPY when appropriate. (vt_initialize): Handle it. (compute_bb_dataflow, emit_notes_in_bb): Likewise. Delete known locations for MO_SET and MO_CLOBBER. (find_variable_location_part): New function. (set_variable_part, delete_variable_part): Use it. (clobber_variable_part): New function. --- gcc/var-tracking.c.orig 2006-12-08 02:14:36.000000000 -0200 +++ gcc/var-tracking.c 2006-12-08 02:14:51.000000000 -0200 @@ -112,6 +112,8 @@ enum micro_operation_type MO_USE_NO_VAR,/* Use location which is not associated with a variable or the variable is not trackable. */ MO_SET, /* Set location. */ + MO_COPY, /* Copy the same portion of a variable from one + loation to another. */ MO_CLOBBER, /* Clobber location. */ MO_CALL, /* Call insn. */ MO_ADJUST /* Adjust stack pointer. */ @@ -293,13 +295,14 @@ static void vars_clear (htab_t); static variable unshare_variable (dataflow_set *set, variable var); static int vars_copy_1 (void **, void *); static void vars_copy (htab_t, htab_t); +static tree var_debug_decl (tree); static void var_reg_set (dataflow_set *, rtx); -static void var_reg_delete_and_set (dataflow_set *, rtx); -static void var_reg_delete (dataflow_set *, rtx); +static void var_reg_delete_and_set (dataflow_set *, rtx, bool); +static void var_reg_delete (dataflow_set *, rtx, bool); static void var_regno_delete (dataflow_set *, int); static void var_mem_set (dataflow_set *, rtx); -static void var_mem_delete_and_set (dataflow_set *, rtx); -static void var_mem_delete (dataflow_set *, rtx); +static void var_mem_delete_and_set (dataflow_set *, rtx, bool); +static void var_mem_delete (dataflow_set *, rtx, bool); static void dataflow_set_init (dataflow_set *, int); static void dataflow_set_clear (dataflow_set *); @@ -316,6 +319,7 @@ static void dataflow_set_destroy (datafl static bool contains_symbol_ref (rtx); static bool track_expr_p (tree); +static bool same_variable_part_p (rtx, tree, HOST_WIDE_INT); static int count_uses (rtx *, void *); static void count_uses_1 (rtx *, void *); static void count_stores (rtx, rtx, void *); @@ -333,6 +337,7 @@ static void dump_dataflow_sets (void); static void variable_was_changed (variable, htab_t); static void set_variable_part (dataflow_set *, rtx, tree, HOST_WIDE_INT); +static void clobber_variable_part (dataflow_set *, rtx, tree, HOST_WIDE_INT); static void delete_variable_part (dataflow_set *, rtx, tree, HOST_WIDE_INT); static int emit_note_insn_var_location (void **, void *); static void emit_notes_for_changes (rtx, enum emit_note_where); @@ -794,6 +799,14 @@ vars_copy (htab_t dst, htab_t src) htab_traverse (src, vars_copy_1, dst); } +/* Map a decl to its main debug decl. */ + +static inline tree +var_debug_decl (tree decl) +{ + return decl; +} + /* Set the register to contain REG_EXPR (LOC), REG_OFFSET (LOC). */ static void @@ -801,23 +814,35 @@ var_reg_set (dataflow_set *set, rtx loc) { tree decl = REG_EXPR (loc); HOST_WIDE_INT offset = REG_OFFSET (loc); + attrs node; + + decl = var_debug_decl (decl); - if (set->regs[REGNO (loc)] == NULL) + for (node = set->regs[REGNO (loc)]; node; node = node->next) + if (node->decl == decl && node->offset == offset) + break; + if (!node) attrs_list_insert (&set->regs[REGNO (loc)], decl, offset, loc); set_variable_part (set, loc, decl, offset); } -/* Delete current content of register LOC in dataflow set SET - and set the register to contain REG_EXPR (LOC), REG_OFFSET (LOC). */ +/* Delete current content of register LOC in dataflow set SET and set + the register to contain REG_EXPR (LOC), REG_OFFSET (LOC). If + MODIFY is true, any other live copies of the same variable part are + also deleted from the dataflow set, otherwise the variable part is + assumed to be copied from another location holding the same + part. */ static void -var_reg_delete_and_set (dataflow_set *set, rtx loc) +var_reg_delete_and_set (dataflow_set *set, rtx loc, bool modify) { tree decl = REG_EXPR (loc); HOST_WIDE_INT offset = REG_OFFSET (loc); attrs node, next; attrs *nextp; + decl = var_debug_decl (decl); + nextp = &set->regs[REGNO (loc)]; for (node = *nextp; node; node = next) { @@ -834,17 +859,31 @@ var_reg_delete_and_set (dataflow_set *se nextp = &node->next; } } + if (modify) + clobber_variable_part (set, loc, decl, offset); var_reg_set (set, loc); } -/* Delete current content of register LOC in dataflow set SET. */ +/* Delete current content of register LOC in dataflow set SET. If + CLOBBER is true, also delete any other live copies of the same + variable part. */ static void -var_reg_delete (dataflow_set *set, rtx loc) +var_reg_delete (dataflow_set *set, rtx loc, bool clobber) { attrs *reg = &set->regs[REGNO (loc)]; attrs node, next; + if (clobber) + { + tree decl = REG_EXPR (loc); + HOST_WIDE_INT offset = REG_OFFSET (loc); + + decl = var_debug_decl (decl); + + clobber_variable_part (set, NULL, decl, offset); + } + for (node = *reg; node; node = next) { next = node->next; @@ -881,28 +920,44 @@ var_mem_set (dataflow_set *set, rtx loc) tree decl = MEM_EXPR (loc); HOST_WIDE_INT offset = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0; + decl = var_debug_decl (decl); + set_variable_part (set, loc, decl, offset); } -/* Delete and set the location part of variable MEM_EXPR (LOC) - in dataflow set SET to LOC. +/* Delete and set the location part of variable MEM_EXPR (LOC) in + dataflow set SET to LOC. If MODIFY is true, any other live copies + of the same variable part are also deleted from the dataflow set, + otherwise the variable part is assumed to be copied from another + location holding the same part. Adjust the address first if it is stack pointer based. */ static void -var_mem_delete_and_set (dataflow_set *set, rtx loc) +var_mem_delete_and_set (dataflow_set *set, rtx loc, bool modify) { + tree decl = MEM_EXPR (loc); + HOST_WIDE_INT offset = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0; + + decl = var_debug_decl (decl); + + if (modify) + clobber_variable_part (set, NULL, decl, offset); var_mem_set (set, loc); } -/* Delete the location part LOC from dataflow set SET. +/* Delete the location part LOC from dataflow set SET. If CLOBBER is + true, also delete any other live copies of the same variable part. Adjust the address first if it is stack pointer based. */ static void -var_mem_delete (dataflow_set *set, rtx loc) +var_mem_delete (dataflow_set *set, rtx loc, bool clobber) { tree decl = MEM_EXPR (loc); HOST_WIDE_INT offset = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0; + decl = var_debug_decl (decl); + if (clobber) + clobber_variable_part (set, NULL, decl, offset); delete_variable_part (set, loc, decl, offset); } @@ -1476,6 +1531,41 @@ track_expr_p (tree expr) return 1; } +/* Determine whether a given LOC refers to the same variable part as + EXPR+OFFSET. */ + +static bool +same_variable_part_p (rtx loc, tree expr, HOST_WIDE_INT offset) +{ + tree expr2; + HOST_WIDE_INT offset2; + + if (! DECL_P (expr)) + return false; + + if (GET_CODE (loc) == REG) + { + expr2 = REG_EXPR (loc); + offset2 = REG_OFFSET (loc); + } + else if (GET_CODE (loc) == MEM) + { + expr2 = MEM_EXPR (loc); + offset2 = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0; + } + else + return false; + + if (! expr2 || ! DECL_P (expr2)) + return false; + + expr = var_debug_decl (expr); + expr2 = var_debug_decl (expr2); + + return (expr == expr2 && offset == offset2); +} + + /* Count uses (register and memory references) LOC which will be tracked. INSN is instruction which the LOC is part of. */ @@ -1570,9 +1660,18 @@ add_stores (rtx loc, rtx expr, void *ins basic_block bb = BLOCK_FOR_INSN ((rtx) insn); micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++; - mo->type = ((GET_CODE (expr) != CLOBBER && REG_EXPR (loc) - && track_expr_p (REG_EXPR (loc))) - ? MO_SET : MO_CLOBBER); + if (GET_CODE (expr) == CLOBBER + || ! REG_EXPR (loc) + || ! track_expr_p (REG_EXPR (loc))) + mo->type = MO_CLOBBER; + else if (GET_CODE (expr) == SET + && SET_DEST (expr) == loc + && same_variable_part_p (SET_SRC (expr), + REG_EXPR (loc), + REG_OFFSET (loc))) + mo->type = MO_COPY; + else + mo->type = MO_SET; mo->u.loc = loc; mo->insn = NEXT_INSN ((rtx) insn); } @@ -1583,7 +1682,17 @@ add_stores (rtx loc, rtx expr, void *ins basic_block bb = BLOCK_FOR_INSN ((rtx) insn); micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++; - mo->type = GET_CODE (expr) == CLOBBER ? MO_CLOBBER : MO_SET; + if (GET_CODE (expr) == CLOBBER) + mo->type = MO_CLOBBER; + else if (GET_CODE (expr) == SET + && SET_DEST (expr) == loc + && same_variable_part_p (SET_SRC (expr), + MEM_EXPR (loc), + MEM_OFFSET (loc) + ? INTVAL (MEM_OFFSET (loc)) : 0)) + mo->type = MO_COPY; + else + mo->type = MO_SET; mo->u.loc = loc; mo->insn = NEXT_INSN ((rtx) insn); } @@ -1631,21 +1740,42 @@ compute_bb_dataflow (basic_block bb) rtx loc = VTI (bb)->mos[i].u.loc; if (GET_CODE (loc) == REG) - var_reg_delete_and_set (out, loc); + var_reg_delete_and_set (out, loc, true); else if (GET_CODE (loc) == MEM) - var_mem_delete_and_set (out, loc); + var_mem_delete_and_set (out, loc, true); + } + break; + + case MO_COPY: + { + rtx loc = VTI (bb)->mos[i].u.loc; + + if (GET_CODE (loc) == REG) + var_reg_delete_and_set (out, loc, false); + else if (GET_CODE (loc) == MEM) + var_mem_delete_and_set (out, loc, false); } break; case MO_USE_NO_VAR: + { + rtx loc = VTI (bb)->mos[i].u.loc; + + if (GET_CODE (loc) == REG) + var_reg_delete (out, loc, false); + else if (GET_CODE (loc) == MEM) + var_mem_delete (out, loc, false); + } + break; + case MO_CLOBBER: { rtx loc = VTI (bb)->mos[i].u.loc; if (GET_CODE (loc) == REG) - var_reg_delete (out, loc); + var_reg_delete (out, loc, true); else if (GET_CODE (loc) == MEM) - var_mem_delete (out, loc); + var_mem_delete (out, loc, true); } break; @@ -1824,7 +1954,7 @@ dump_dataflow_set (dataflow_set *set) fprintf (rtl_dump_file, "Stack adjustment: " HOST_WIDE_INT_PRINT_DEC "\n", set->stack_adjust); - for (i = 1; i < FIRST_PSEUDO_REGISTER; i++) + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) { if (set->regs[i]) { @@ -1905,6 +2035,39 @@ variable_was_changed (variable var, htab } } +/* Look for the index in VAR->var_part corresponding to OFFSET. + Return -1 if not found. If INSERTION_POINT is non-NULL, the + referenced int will be set to the index that the part has or should + have, if it should be inserted. */ + +static inline int +find_variable_location_part (variable var, HOST_WIDE_INT offset, + int *insertion_point) +{ + int pos, low, high; + + /* Find the location part. */ + low = 0; + high = var->n_var_parts; + while (low != high) + { + pos = (low + high) / 2; + if (var->var_part[pos].offset < offset) + low = pos + 1; + else + high = pos; + } + pos = low; + + if (insertion_point) + *insertion_point = pos; + + if (pos < var->n_var_parts && var->var_part[pos].offset == offset) + return pos; + + return -1; +} + /* Set the part of variable's location in the dataflow set SET. The variable part is specified by variable's declaration DECL and offset OFFSET and the part's location by LOC. */ @@ -1912,7 +2075,7 @@ variable_was_changed (variable var, htab static void set_variable_part (dataflow_set *set, rtx loc, tree decl, HOST_WIDE_INT offset) { - int pos, low, high; + int pos; location_chain node, next; location_chain *nextp; variable var; @@ -1935,22 +2098,13 @@ set_variable_part (dataflow_set *set, rt } else { + int inspos = 0; + var = (variable) *slot; - /* Find the location part. */ - low = 0; - high = var->n_var_parts; - while (low != high) - { - pos = (low + high) / 2; - if (var->var_part[pos].offset < offset) - low = pos + 1; - else - high = pos; - } - pos = low; + pos = find_variable_location_part (var, offset, &inspos); - if (pos < var->n_var_parts && var->var_part[pos].offset == offset) + if (pos >= 0) { node = var->var_part[pos].loc_chain; @@ -1985,10 +2139,10 @@ set_variable_part (dataflow_set *set, rt abort (); #endif - /* We have to move the elements of array starting at index low to the - next position. */ - for (high = var->n_var_parts; high > low; high--) - var->var_part[high] = var->var_part[high - 1]; + /* We have to move the elements of array starting at index + inspos to the next position. */ + for (pos = var->n_var_parts; pos > inspos; pos--) + var->var_part[pos] = var->var_part[pos - 1]; var->n_var_parts++; var->var_part[pos].offset = offset; @@ -2028,6 +2182,67 @@ set_variable_part (dataflow_set *set, rt } } +/* Remove all recorded register locations for the given variable part + from dataflow set SET, except for those that are identical to loc. + The variable part is specified by variable's declaration DECL and + offset OFFSET. */ + +static void +clobber_variable_part (dataflow_set *set, rtx loc, tree decl, + HOST_WIDE_INT offset) +{ + void **slot; + + if (! decl || ! DECL_P (decl)) + return; + + slot = htab_find_slot_with_hash (set->vars, decl, VARIABLE_HASH_VAL (decl), + NO_INSERT); + if (slot) + { + variable var = (variable) *slot; + int pos = find_variable_location_part (var, offset, NULL); + + if (pos >= 0) + { + location_chain node, next; + + /* Remove the register locations from the dataflow set. */ + next = var->var_part[pos].loc_chain; + for (node = next; node; node = next) + { + next = node->next; + if (node->loc != loc) + { + if (REG_P (node->loc)) + { + attrs anode, anext; + attrs *anextp; + + /* Remove the variable part from the register's + list, but preserve any other variable parts + that might be regarded as live in that same + register. */ + anextp = &set->regs[REGNO (node->loc)]; + for (anode = *anextp; anode; anode = anext) + { + anext = anode->next; + if (anode->decl == decl + && anode->offset == offset) + { + pool_free (attrs_pool, anode); + *anextp = anext; + } + } + } + + delete_variable_part (set, node->loc, decl, offset); + } + } + } + } +} + /* Delete the part of variable's location from dataflow set SET. The variable part is specified by variable's declaration DECL and offset OFFSET and the part's location by LOC. */ @@ -2036,7 +2251,6 @@ static void delete_variable_part (dataflow_set *set, rtx loc, tree decl, HOST_WIDE_INT offset) { - int pos, low, high; void **slot; slot = htab_find_slot_with_hash (set->vars, decl, VARIABLE_HASH_VAL (decl), @@ -2044,21 +2258,9 @@ delete_variable_part (dataflow_set *set, if (slot) { variable var = (variable) *slot; + int pos = find_variable_location_part (var, offset, NULL); - /* Find the location part. */ - low = 0; - high = var->n_var_parts; - while (low != high) - { - pos = (low + high) / 2; - if (var->var_part[pos].offset < offset) - low = pos + 1; - else - high = pos; - } - pos = low; - - if (pos < var->n_var_parts && var->var_part[pos].offset == offset) + if (pos >= 0) { location_chain node, next; location_chain *nextp; @@ -2124,7 +2326,7 @@ delete_variable_part (dataflow_set *set, } } if (changed) - variable_was_changed (var, set->vars); + variable_was_changed (var, set->vars); } } } @@ -2418,28 +2620,50 @@ emit_notes_in_bb (basic_block bb) rtx loc = VTI (bb)->mos[i].u.loc; if (GET_CODE (loc) == REG) - var_reg_delete_and_set (&set, loc); + var_reg_delete_and_set (&set, loc, true); + else + var_mem_delete_and_set (&set, loc, true); + + emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN); + } + break; + + case MO_COPY: + { + rtx loc = VTI (bb)->mos[i].u.loc; + + if (GET_CODE (loc) == REG) + var_reg_delete_and_set (&set, loc, false); else - var_mem_delete_and_set (&set, loc); + var_mem_delete_and_set (&set, loc, false); emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN); } break; case MO_USE_NO_VAR: - case MO_CLOBBER: { rtx loc = VTI (bb)->mos[i].u.loc; if (GET_CODE (loc) == REG) - var_reg_delete (&set, loc); + var_reg_delete (&set, loc, false); else - var_mem_delete (&set, loc); + var_mem_delete (&set, loc, false); + + emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN); + } + break; + + case MO_CLOBBER: + { + rtx loc = VTI (bb)->mos[i].u.loc; - if (VTI (bb)->mos[i].type == MO_USE_NO_VAR) - emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN); + if (GET_CODE (loc) == REG) + var_reg_delete (&set, loc, true); else - emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN); + var_mem_delete (&set, loc, true); + + emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN); } break; @@ -2673,7 +2897,8 @@ vt_initialize (void) { while (n1 < n2 && VTI (bb)->mos[n1].type == MO_CLOBBER) n1++; - while (n1 < n2 && VTI (bb)->mos[n2].type == MO_SET) + while (n1 < n2 && (VTI (bb)->mos[n2].type == MO_SET + || VTI (bb)->mos[n2].type == MO_COPY)) n2--; if (n1 < n2) { --- gcc/testsuite/g++.dg/debug/vartrack1.C 2004-06-24 14:04:38.000000000 -0400 +++ gcc/testsuite/g++.dg/debug/vartrack1.C 2006-12-08 05:29:41.000000000 -0500 @@ -0,0 +1,99 @@ +// This testcase used to hang the compiler in vt_find_locations. +// { dg-do compile } +// { dg-options "-O2 -g" } + +struct S +{ + int a; + S *b, *c, *d; +}; + +struct T +{ + void f1 (S *x); + void f2 (S *x); + void f3 (S *x, S *y); + S *e; +}; + +void +T::f3 (S *x, S *y) +{ + while (x != this->e && (!x || x->a == 1)) + { + if (x == y->c) + { + S *w = y->d; + if (w && w->a == 0) + { + w->a = 1; + y->a = 0; + f2 (y); + w = y->d; + } + if (w && (!w->c || w->c->a == 1) && (!w->d || w->d->a == 1)) + { + w->a = 0; + x = y; + y = x->b; + } + else + { + if (w && (!w->d || w->d->a == 1)) + { + if (w->c) + w->c->a = 1; + w->a = 0; + f1 (w); + w = y->d; + } + if (w) + { + w->a = y->a; + if (w->d) + w->d->a = 1; + } + y->a = 1; + f2 (y); + x = e; + } + } + else + { + S *w = y->c; + if (w && w->a == 0) + { + w->a = 1; + y->a = 0; + f1 (y); + w = y->c; + } + if (w && (!w->c || w->c->a == 1) && (!w->d || w->d->a == 1)) + { + w->a = 0; + x = y; + y = x->b; + } + else + { + if (w && (!w->c || w->c->a == 1)) + { + w->a = 0; + if (w->d) + w->d->a = 1; + f2 (w); + w = y->c; + } + if (w) + { + w->a = y->a; + if (w->c) + w->c->a = 1; + } + y->a = 1; + f1 (y); + x = e; + } + } + } +}