The final part, an updated version of the old 004-dw2cfg patch. This does much better placement of remember/restore; in almost all cases the code is identical to what we currently generate, modulo minor differences around the PROLOGUE_END label. I've made it emit queued register saves before PROLOGUE_END so that we can use the state there for forced labels.
Bernd
Index: gcc/dwarf2out.c =================================================================== --- gcc.orig/dwarf2out.c +++ gcc/dwarf2out.c @@ -465,12 +465,11 @@ static void initial_return_save (rtx); static HOST_WIDE_INT stack_adjust_offset (const_rtx, HOST_WIDE_INT, HOST_WIDE_INT); static void output_cfi (dw_cfi_ref, dw_fde_ref, int); -static void output_cfi_directive (dw_cfi_ref); +static void output_cfi_directive (FILE *, dw_cfi_ref); static void output_call_frame_info (int); static void dwarf2out_note_section_used (void); static bool clobbers_queued_reg_save (const_rtx); static void dwarf2out_frame_debug_expr (rtx); -static void dwarf2out_cfi_begin_epilogue (rtx); static void dwarf2out_frame_debug_restore_state (void); /* Support for complex CFA locations. */ @@ -823,9 +822,6 @@ new_cfi (void) /* The insn after which a new CFI note should be emitted. */ static rtx cfi_insn; -/* True if remember_state should be emitted before following CFI directive. */ -static bool emit_cfa_remember; - /* True if any CFI directives were emitted at the current insn. */ static bool any_cfis_emitted; @@ -868,28 +864,34 @@ dwarf2out_maybe_emit_cfi_label (void) } } +static void +add_cfa_remember (void) +{ + dw_cfi_ref cfi_remember; + + /* Emit the state save. */ + cfi_remember = new_cfi (); + cfi_remember->dw_cfi_opc = DW_CFA_remember_state; + add_fde_cfi (cfi_remember); +} + +/* Nonnull if add_fde_cfi should not just emit a NOTE_INSN_CFI, but + also add the CFI to this vector. */ +static cfi_vec *cfi_insn_vec; + /* Add CFI to the current fde at the PC value indicated by LABEL if specified, or to the CIE if LABEL is NULL. */ static void add_fde_cfi (dw_cfi_ref cfi) { - if (emit_cfa_remember) - { - dw_cfi_ref cfi_remember; - - /* Emit the state save. */ - emit_cfa_remember = false; - cfi_remember = new_cfi (); - cfi_remember->dw_cfi_opc = DW_CFA_remember_state; - add_fde_cfi (cfi_remember); - } - any_cfis_emitted = true; if (cfi_insn != NULL) { cfi_insn = emit_note_after (NOTE_INSN_CFI, cfi_insn); NOTE_CFI (cfi_insn) = cfi; + if (cfi_insn_vec != NULL) + VEC_safe_push (dw_cfi_ref, gc, *cfi_insn_vec, cfi); } else { @@ -980,12 +982,6 @@ static dw_cfa_location old_cfa; from the CFA. */ static dw_cfa_location cfa_store; -/* The current save location around an epilogue. */ -static dw_cfa_location cfa_remember; - -/* Like cfa_remember, but a copy of old_cfa. */ -static dw_cfa_location old_cfa_remember; - /* The running total of the size of arguments pushed onto the stack. */ static HOST_WIDE_INT args_size; @@ -1339,179 +1335,6 @@ stack_adjust_offset (const_rtx pattern, return offset; } -/* Precomputed args_size for CODE_LABELs and BARRIERs preceeding them, - indexed by INSN_UID. */ - -static HOST_WIDE_INT *barrier_args_size; - -/* Helper function for compute_barrier_args_size. Handle one insn. */ - -static HOST_WIDE_INT -compute_barrier_args_size_1 (rtx insn, HOST_WIDE_INT cur_args_size, - VEC (rtx, heap) **next) -{ - HOST_WIDE_INT offset = 0; - int i; - - if (! RTX_FRAME_RELATED_P (insn)) - { - if (prologue_epilogue_contains (insn)) - /* Nothing */; - else if (GET_CODE (PATTERN (insn)) == SET) - offset = stack_adjust_offset (PATTERN (insn), cur_args_size, 0); - else if (GET_CODE (PATTERN (insn)) == PARALLEL - || GET_CODE (PATTERN (insn)) == SEQUENCE) - { - /* There may be stack adjustments inside compound insns. Search - for them. */ - for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--) - if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET) - offset += stack_adjust_offset (XVECEXP (PATTERN (insn), 0, i), - cur_args_size, offset); - } - } - else - { - rtx expr = find_reg_note (insn, REG_FRAME_RELATED_EXPR, NULL_RTX); - - if (expr) - { - expr = XEXP (expr, 0); - if (GET_CODE (expr) == PARALLEL - || GET_CODE (expr) == SEQUENCE) - for (i = 1; i < XVECLEN (expr, 0); i++) - { - rtx elem = XVECEXP (expr, 0, i); - - if (GET_CODE (elem) == SET && !RTX_FRAME_RELATED_P (elem)) - offset += stack_adjust_offset (elem, cur_args_size, offset); - } - } - } - -#ifndef STACK_GROWS_DOWNWARD - offset = -offset; -#endif - - cur_args_size += offset; - if (cur_args_size < 0) - cur_args_size = 0; - - if (JUMP_P (insn)) - { - rtx dest = JUMP_LABEL (insn); - - if (dest) - { - if (barrier_args_size [INSN_UID (dest)] < 0) - { - barrier_args_size [INSN_UID (dest)] = cur_args_size; - VEC_safe_push (rtx, heap, *next, dest); - } - } - } - - return cur_args_size; -} - -/* Walk the whole function and compute args_size on BARRIERs. */ - -static void -compute_barrier_args_size (void) -{ - int max_uid = get_max_uid (), i; - rtx insn; - VEC (rtx, heap) *worklist, *next, *tmp; - - barrier_args_size = XNEWVEC (HOST_WIDE_INT, max_uid); - for (i = 0; i < max_uid; i++) - barrier_args_size[i] = -1; - - worklist = VEC_alloc (rtx, heap, 20); - next = VEC_alloc (rtx, heap, 20); - insn = get_insns (); - barrier_args_size[INSN_UID (insn)] = 0; - VEC_quick_push (rtx, worklist, insn); - for (;;) - { - while (!VEC_empty (rtx, worklist)) - { - rtx prev, body, first_insn; - HOST_WIDE_INT cur_args_size; - - first_insn = insn = VEC_pop (rtx, worklist); - cur_args_size = barrier_args_size[INSN_UID (insn)]; - prev = prev_nonnote_insn (insn); - if (prev && BARRIER_P (prev)) - barrier_args_size[INSN_UID (prev)] = cur_args_size; - - for (; insn; insn = NEXT_INSN (insn)) - { - if (INSN_DELETED_P (insn) || NOTE_P (insn)) - continue; - if (BARRIER_P (insn)) - break; - - if (LABEL_P (insn)) - { - if (insn == first_insn) - continue; - else if (barrier_args_size[INSN_UID (insn)] < 0) - { - barrier_args_size[INSN_UID (insn)] = cur_args_size; - continue; - } - else - { - /* The insns starting with this label have been - already scanned or are in the worklist. */ - break; - } - } - - body = PATTERN (insn); - if (GET_CODE (body) == SEQUENCE) - { - HOST_WIDE_INT dest_args_size = cur_args_size; - for (i = 1; i < XVECLEN (body, 0); i++) - if (INSN_ANNULLED_BRANCH_P (XVECEXP (body, 0, 0)) - && INSN_FROM_TARGET_P (XVECEXP (body, 0, i))) - dest_args_size - = compute_barrier_args_size_1 (XVECEXP (body, 0, i), - dest_args_size, &next); - else - cur_args_size - = compute_barrier_args_size_1 (XVECEXP (body, 0, i), - cur_args_size, &next); - - if (INSN_ANNULLED_BRANCH_P (XVECEXP (body, 0, 0))) - compute_barrier_args_size_1 (XVECEXP (body, 0, 0), - dest_args_size, &next); - else - cur_args_size - = compute_barrier_args_size_1 (XVECEXP (body, 0, 0), - cur_args_size, &next); - } - else - cur_args_size - = compute_barrier_args_size_1 (insn, cur_args_size, &next); - } - } - - if (VEC_empty (rtx, next)) - break; - - /* Swap WORKLIST with NEXT and truncate NEXT for next iteration. */ - tmp = next; - next = worklist; - worklist = tmp; - VEC_truncate (rtx, next, 0); - } - - VEC_free (rtx, heap, worklist); - VEC_free (rtx, heap, next); -} - /* Add a CFI to update the running total of the size of arguments pushed onto the stack. */ @@ -1608,25 +1431,7 @@ dwarf2out_notice_stack_adjust (rtx insn, return; } else if (BARRIER_P (insn)) - { - /* Don't call compute_barrier_args_size () if the only - BARRIER is at the end of function. */ - if (barrier_args_size == NULL && next_nonnote_insn (insn)) - compute_barrier_args_size (); - if (barrier_args_size == NULL) - offset = 0; - else - { - offset = barrier_args_size[INSN_UID (insn)]; - if (offset < 0) - offset = 0; - } - - offset -= args_size; -#ifndef STACK_GROWS_DOWNWARD - offset = -offset; -#endif - } + return; else if (GET_CODE (PATTERN (insn)) == SET) offset = stack_adjust_offset (PATTERN (insn), args_size, 0); else if (GET_CODE (PATTERN (insn)) == PARALLEL @@ -2054,9 +1859,12 @@ add_cfis_to_fde (void) next = NEXT_INSN (insn); if (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_SWITCH_TEXT_SECTIONS) - /* Don't attempt to advance_loc4 between labels in different - sections. */ - first = true; + { + fde->dw_fde_switch_cfi_index = VEC_length (dw_cfi_ref, fde->dw_fde_cfi); + /* Don't attempt to advance_loc4 between labels in different + sections. */ + first = true; + } if (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_CFI) { @@ -2097,6 +1905,17 @@ add_cfis_to_fde (void) } } +/* A subroutine of dwarf2out_frame_debug_init, emit a CFA_restore_state. */ + +void +dwarf2out_frame_debug_restore_state (void) +{ + dw_cfi_ref cfi = new_cfi (); + + cfi->dw_cfi_opc = DW_CFA_restore_state; + add_fde_cfi (cfi); +} + /* Record call frame debugging information for an expression EXPR, which either sets SP or FP (adjusting how we calculate the frame address) or saves a register to the stack or another register. @@ -2797,9 +2616,6 @@ dwarf2out_frame_debug (rtx insn, bool af else cfi_insn = PREV_INSN (insn); - if (!NONJUMP_INSN_P (insn) || clobbers_queued_reg_save (insn)) - dwarf2out_flush_queued_reg_saves (); - if (!RTX_FRAME_RELATED_P (insn)) { /* ??? This should be done unconditionally since stack adjustments @@ -2945,56 +2761,224 @@ dwarf2out_frame_debug_init (void) num_regs_saved_in_regs = 0; } -/* After the (optional) text prologue has been written, emit CFI insns - and update the FDE for frame-related instructions. */ +/* Copy a CFI vector, except for args_size opcodes. */ +static cfi_vec +copy_cfi_vec_parts (cfi_vec in_vec) +{ + int length = VEC_length (dw_cfi_ref, in_vec); + /* Ensure we always have a pointer to a vector, not just NULL. */ + cfi_vec new_vec = VEC_alloc (dw_cfi_ref, gc, length > 0 ? length : 1); + int i; + for (i = 0; i < length; i++) + { + dw_cfi_ref elt = VEC_index (dw_cfi_ref, in_vec, i); + if (elt->dw_cfi_opc == DW_CFA_GNU_args_size) + continue; -void -dwarf2out_frame_debug_after_prologue (void) + VEC_quick_push (dw_cfi_ref, new_vec, elt); + } + return new_vec; +} + +/* Record the state of the CFI program at a point in the program. */ +typedef struct { - rtx insn; - if (barrier_args_size) + /* The CFI instructions up to this point. */ + cfi_vec cfis; + /* Copies of the global variables with the same name. */ + dw_cfa_location cfa, cfa_store, old_cfa; + /* True if we have seen this point during a scan in scan_until_barrier. */ + bool visited; + /* True if this point was used as a starting point for such a scan. */ + bool used_as_start; + /* Other than CFI instructions and CFA state, the only thing necessary to + be tracked is the argument size. */ + int args_size; + /* Nonzero for states that must be remembered and restored. If higher + than one, the first restores will be immediately followed by another + remember. */ + int n_restores; +} jump_target_info; + +/* Return true if we'll want to save or restore CFI state at INSN. This is + true for labels and barriers, and certain notes. */ +static bool +save_point_p (rtx insn) +{ + return (BARRIER_P (insn) || LABEL_P (insn) + || (NOTE_P (insn) + && (NOTE_KIND (insn) == NOTE_INSN_FUNCTION_BEG + || NOTE_KIND (insn) == NOTE_INSN_PROLOGUE_END + || NOTE_KIND (insn) == NOTE_INSN_SWITCH_TEXT_SECTIONS))); +} + +/* Save the current state in INFO. */ + +static void +record_current_state (jump_target_info *info) +{ + info->cfis = copy_cfi_vec_parts (*cfi_insn_vec); + info->args_size = args_size; + info->cfa = cfa; + info->old_cfa = old_cfa; + info->cfa_store = cfa_store; +} + +/* LABEL is the target of a jump we encountered while scanning the + function. Record it in START_POINTS as a potential new starting point + for the scan, unless we've visited it before. UID_LUID gives a + mapping for uids used to index INFO, which holds the CFI + information for labels and barriers. */ +static void +maybe_record_jump_target (rtx label, VEC (rtx, heap) **start_points, + int *uid_luid, jump_target_info *info) +{ + int uid; + + if (GET_CODE (label) == LABEL_REF) + label = XEXP (label, 0); + gcc_assert (LABEL_P (label)); + uid = INSN_UID (label); + info += uid_luid[uid]; + if (info->visited || info->cfis) + return; + + if (dump_file) + fprintf (dump_file, "recording label %d as possible jump target\n", uid); + + VEC_safe_push (rtx, heap, *start_points, label); + record_current_state (info); +} + +/* Return true if VEC1 and VEC2 are identical up to the length of VEC1. */ +static bool +vec_is_prefix_of (cfi_vec vec1, cfi_vec vec2) +{ + int i; + int len1 = VEC_length (dw_cfi_ref, vec1); + int len2 = VEC_length (dw_cfi_ref, vec2); + if (len1 > len2) + return false; + for (i = 0; i < len1; i++) + if (VEC_index (dw_cfi_ref, vec1, i) != VEC_index (dw_cfi_ref, vec1, i)) + return false; + return true; +} + +/* Append entries to FDE's cfi vector. PREFIX and FULL are two + existing vectors, where PREFIX is contained in FULL as a prefix. */ + +static void +append_extra_cfis (cfi_vec prefix, cfi_vec full) +{ + int i; + int len = VEC_length (dw_cfi_ref, full); + int prefix_len = VEC_length (dw_cfi_ref, prefix); + + gcc_assert (prefix_len <= len); + for (i = 0; i < prefix_len; i++) { - XDELETEVEC (barrier_args_size); - barrier_args_size = NULL; + dw_cfi_ref elt, elt2; + + elt = VEC_index (dw_cfi_ref, full, i); + elt2 = VEC_index (dw_cfi_ref, prefix, i); + gcc_assert (elt == elt2); } - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + for (; i < len; i++) { - rtx pat; - if (BARRIER_P (insn)) - { - dwarf2out_frame_debug (insn, false); - continue; - } - else if (NOTE_P (insn)) + dw_cfi_ref elt = VEC_index (dw_cfi_ref, full, i); + add_fde_cfi (elt); + } +} + +extern void debug_cfi_vec (FILE *, cfi_vec v); +void debug_cfi_vec (FILE *f, cfi_vec v) +{ + int ix; + dw_cfi_ref cfi; + + FOR_EACH_VEC_ELT (dw_cfi_ref, v, ix, cfi) + output_cfi_directive (f, cfi); +} + +static bool +switch_note_p (rtx insn) +{ + return NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_SWITCH_TEXT_SECTIONS; +} + +/* From the current starting point in INSN, scan forwards until we hit a + barrier, the end of the function, or a label we've previously used as + a starting point. + UID_LUID is a mapping to linear uids used to map an insn to an entry in + POINT_INFO, if save_point_p is true for a given insn. */ + +static void +scan_until_barrier (rtx insn, jump_target_info *point_info, int *uid_luid, + VEC (rtx, heap) **start_points) +{ + rtx next; + for (; insn != NULL_RTX; insn = next) + { + int uid = INSN_UID (insn); + rtx pat, note; + + next = NEXT_INSN (insn); + if (save_point_p (insn)) { - switch (NOTE_KIND (insn)) - { - case NOTE_INSN_EPILOGUE_BEG: -#if defined (HAVE_epilogue) - dwarf2out_cfi_begin_epilogue (insn); -#endif + int luid = uid_luid[uid]; + jump_target_info *info = point_info + luid; + if (info->used_as_start) + { + if (dump_file) + fprintf (dump_file, + "Stopping scan at insn %d; previously reached\n", + uid); break; - case NOTE_INSN_CFA_RESTORE_STATE: - cfi_insn = insn; - dwarf2out_frame_debug_restore_state (); - cfi_insn = NULL; + } + info->visited = true; + if (BARRIER_P (insn)) + gcc_assert (info->cfis == NULL); + if (switch_note_p (insn)) + { + /* Don't record the state, it was set to a clean slate in + the caller. */ + if (dump_file) + fprintf (dump_file, + "Stopping scan at text section switch %d\n", uid); + break; + } + record_current_state (info); + if (BARRIER_P (insn)) + { + if (dump_file) + fprintf (dump_file, "Stopping scan at barrier %d\n", uid); break; } - continue; } + if (!NONDEBUG_INSN_P (insn)) continue; pat = PATTERN (insn); if (asm_noperands (pat) >= 0) continue; + if (GET_CODE (pat) == SEQUENCE) { - int j; - for (j = 1; j < XVECLEN (pat, 0); j++) - dwarf2out_frame_debug (XVECEXP (pat, 0, j), false); + int i; + for (i = 1; i < XVECLEN (pat, 0); i++) + dwarf2out_frame_debug (XVECEXP (pat, 0, i), false); insn = XVECEXP (pat, 0, 0); } + if (!NONJUMP_INSN_P (insn) || clobbers_queued_reg_save (insn) + || (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_PROLOGUE_END)) + { + cfi_insn = PREV_INSN (insn); + dwarf2out_flush_queued_reg_saves (); + cfi_insn = NULL_RTX; + } + if (CALL_P (insn) && dwarf2out_do_frame ()) dwarf2out_frame_debug (insn, false); if (dwarf2out_do_frame () @@ -3003,115 +2987,463 @@ dwarf2out_frame_debug_after_prologue (vo #endif ) dwarf2out_frame_debug (insn, true); - } - add_cfis_to_fde (); + if (JUMP_P (insn)) + { + rtx label = JUMP_LABEL (insn); + if (label) + { + rtx next = next_real_insn (label); + if (next != NULL_RTX && addr_vec_p (next)) + { + int i; + rtx pat = PATTERN (next); + int eltnum = GET_CODE (pat) == ADDR_DIFF_VEC ? 1 : 0; + + for (i = 0; i < XVECLEN (pat, eltnum); i++) + maybe_record_jump_target (XVECEXP (pat, eltnum, i), + start_points, uid_luid, + point_info); + } + else + maybe_record_jump_target (label, start_points, uid_luid, + point_info); + } + } + note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); + if (note) + { + eh_landing_pad lp; + + lp = get_eh_landing_pad_from_rtx (insn); + if (lp) + maybe_record_jump_target (lp->landing_pad, start_points, + uid_luid, point_info); + } + } } -void -dwarf2out_emit_cfi (dw_cfi_ref cfi) +/* A subroutine of dwarf2out_debug_after_prologue. Given the vector + of potential starting points in *START_POINTS, pick the best one to + use for the next scan. Return NULL_RTX if there's nothing left to + scan. + UID_LUID and START_POINTS are as in scan_until_barrier. */ + +static rtx +find_best_starting_point (jump_target_info *point_info, int *uid_luid, + VEC (rtx, heap) **start_points) { - if (dwarf2out_do_cfi_asm ()) - output_cfi_directive (cfi); + int i; + rtx insn; + int best_idx; + bool best_has_barrier; + jump_target_info *restart_info; + + FOR_EACH_VEC_ELT_REVERSE (rtx, *start_points, i, insn) + { + restart_info = point_info + uid_luid[INSN_UID (insn)]; + if (restart_info->visited) + VEC_ordered_remove (rtx, *start_points, i); + } + + best_idx = -1; + best_has_barrier = false; + FOR_EACH_VEC_ELT (rtx, *start_points, i, insn) + { + rtx prev; + bool this_has_barrier; + + restart_info = point_info + uid_luid[INSN_UID (insn)]; + prev = prev_nonnote_nondebug_insn (insn); + this_has_barrier = (prev + && (BARRIER_P (prev) || switch_note_p (prev))); + if (best_idx < 0 + || (!best_has_barrier && this_has_barrier)) + { + best_idx = i; + best_has_barrier = this_has_barrier; + } + } + + if (best_idx < 0) + { + rtx link; + for (link = forced_labels; link; link = XEXP (link, 1)) + { + insn = XEXP (link, 0); + restart_info = point_info + uid_luid[INSN_UID (insn)]; + if (!restart_info->visited) + return insn; + } + return NULL_RTX; + } + insn = VEC_index (rtx, *start_points, best_idx); + VEC_ordered_remove (rtx, *start_points, best_idx); + return insn; } -/* Determine if we need to save and restore CFI information around - this epilogue. If we do need to save/restore, then emit the save - now, and insert a NOTE_INSN_CFA_RESTORE_STATE at the appropriate - place in the stream. */ +/* After the (optional) text prologue has been written, emit CFI insns + and update the FDE for frame-related instructions. */ void -dwarf2out_cfi_begin_epilogue (rtx insn) +dwarf2out_frame_debug_after_prologue (void) { - bool saw_frp = false; - rtx i; + int max_uid = get_max_uid (); + int i, n_saves_restores, prologue_end_point, switch_note_point; + rtx insn, save_point; + VEC (rtx, heap) *start_points; + int n_points; + int *uid_luid; + bool remember_needed; + jump_target_info *point_info, *save_point_info; + cfi_vec current_vec; + + n_points = 0; + for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn)) + if (save_point_p (insn)) + n_points++; + uid_luid = XCNEWVEC (int, max_uid); + n_points = 0; + prologue_end_point = -1; + switch_note_point = -1; + for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn)) + if (save_point_p (insn)) + { + if (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_PROLOGUE_END) + prologue_end_point = n_points; + else if (switch_note_p (insn)) + switch_note_point = n_points; + uid_luid[INSN_UID (insn)] = n_points++; + } + + point_info = XCNEWVEC (jump_target_info, n_points); + for (i = 0; i < n_points; i++) + point_info[i].args_size = -1; + + start_points = VEC_alloc (rtx, heap, 20); + insn = get_insns (); + current_vec = VEC_alloc (dw_cfi_ref, gc, 10); + + /* At a NOTE_INSN_SWITCH_TEXT_SECTIONS we'll emit a cfi_startproc. + Ensure the state at this note reflects that. */ + if (switch_note_point != -1) + { + cfi_insn_vec = ¤t_vec; + record_current_state (point_info + switch_note_point); + cfi_insn_vec = NULL; + } + args_size = old_args_size = 0; - /* Scan forward to the return insn, noticing if there are possible - frame related insns. */ - for (i = NEXT_INSN (insn); i ; i = NEXT_INSN (i)) + for (;;) { - if (!INSN_P (i)) - continue; + HOST_WIDE_INT offset; + jump_target_info *restart_info; - /* Look for both regular and sibcalls to end the block. Various - optimization passes may cause us to jump to a common epilogue - tail, so we also accept simplejumps. */ - if (returnjump_p (i) || simplejump_p (i)) - break; - if (CALL_P (i) && SIBLING_CALL_P (i)) + /* Scan the insns and emit NOTE_CFIs where necessary. */ + cfi_insn_vec = ¤t_vec; + scan_until_barrier (insn, point_info, uid_luid, &start_points); + cfi_insn_vec = NULL; + + insn = find_best_starting_point (point_info, uid_luid, &start_points); + + if (insn == NULL_RTX) break; - if (GET_CODE (PATTERN (i)) == SEQUENCE) - { - int idx; - rtx seq = PATTERN (i); + if (dump_file) + fprintf (dump_file, "restarting scan at label %d", INSN_UID (insn)); - if (returnjump_p (XVECEXP (seq, 0, 0))) - break; - if (CALL_P (XVECEXP (seq, 0, 0)) - && SIBLING_CALL_P (XVECEXP (seq, 0, 0))) - break; + restart_info = point_info + uid_luid[INSN_UID (insn)]; + restart_info->visited = true; + restart_info->used_as_start = true; + /* If find_best_starting_point returned a forced label, use the + state at the NOTE_INSN_PROLOGUE_END note. */ + if (restart_info->cfis == NULL) + { + cfi_vec *v = &restart_info->cfis; + gcc_assert (prologue_end_point != -1); + restart_info = point_info + prologue_end_point; + *v = copy_cfi_vec_parts (restart_info->cfis); + } + + gcc_assert (LABEL_P (insn)); + current_vec = copy_cfi_vec_parts (restart_info->cfis); + cfa = restart_info->cfa; + old_cfa = restart_info->old_cfa; + cfa_store = restart_info->cfa_store; + offset = restart_info->args_size; + if (offset >= 0) + { + if (dump_file && offset != args_size) + fprintf (dump_file, ", args_size " HOST_WIDE_INT_PRINT_DEC + " -> " HOST_WIDE_INT_PRINT_DEC, + args_size, offset); - for (idx = 0; idx < XVECLEN (seq, 0); idx++) - if (RTX_FRAME_RELATED_P (XVECEXP (seq, 0, idx))) - saw_frp = true; + offset -= args_size; +#ifndef STACK_GROWS_DOWNWARD + offset = -offset; +#endif + if (offset != 0) + { + cfi_insn = prev_nonnote_nondebug_insn (insn); + dwarf2out_stack_adjust (offset); + cfi_insn = NULL_RTX; + } + } + if (dump_file) + { + fprintf (dump_file, "\n"); + if (dump_flags & TDF_DETAILS) + debug_cfi_vec (dump_file, current_vec); } - if (RTX_FRAME_RELATED_P (i)) - saw_frp = true; + insn = NEXT_INSN (insn); } - /* If the port doesn't emit epilogue unwind info, we don't need a - save/restore pair. */ - if (!saw_frp) - return; + VEC_free (rtx, heap, start_points); - /* Otherwise, search forward to see if the return insn was the last - basic block of the function. If so, we don't need save/restore. */ - gcc_assert (i != NULL); - i = next_real_insn (i); - if (i == NULL) - return; + /* Now splice the various CFI fragments together into a coherent whole. */ - /* Insert the restore before that next real insn in the stream, and before - a potential NOTE_INSN_EPILOGUE_BEG -- we do need these notes to be - properly nested. This should be after any label or alignment. This - will be pushed into the CFI stream by the function below. */ - while (1) + /* First, discover discontinuities, and where necessary search for suitable + remember/restore points. */ + save_point = NULL_RTX; + save_point_info = NULL; + n_saves_restores = 0; + for (insn = get_last_insn (); insn; insn = PREV_INSN (insn)) { - rtx p = PREV_INSN (i); - if (!NOTE_P (p)) - break; - if (NOTE_KIND (p) == NOTE_INSN_BASIC_BLOCK) - break; - i = p; + jump_target_info *info, *barrier_info, *candidate_info; + rtx prev; + + if (insn == save_point) + { + save_point = NULL_RTX; + save_point_info = NULL; + info = point_info + uid_luid[INSN_UID (insn)]; + info->n_restores = n_saves_restores; + n_saves_restores = 0; + if (dump_file) + fprintf (dump_file, "finalize save point %d\n", INSN_UID (insn)); + } + + /* Look for labels that were used as starting points and are + preceded by a BARRIER. */ + if (!LABEL_P (insn)) + continue; + + info = point_info + uid_luid[INSN_UID (insn)]; + if (!info->used_as_start) + continue; + barrier_info = NULL; + for (prev = PREV_INSN (insn); prev; prev = PREV_INSN (prev)) + { + if (!BARRIER_P (prev) && !LABEL_P (prev)) + continue; + barrier_info = point_info + uid_luid[INSN_UID (prev)]; + /* Skip through barriers we haven't visited; they may occur + for things like jump tables. */ + if ((BARRIER_P (prev) && barrier_info->visited) + || (LABEL_P (prev) && barrier_info->used_as_start) + || switch_note_p (prev)) + break; + } + if (!BARRIER_P (prev)) + continue; + + if (dump_file) + fprintf (dump_file, "State transition at barrier %d, label %d ... ", + INSN_UID (prev), INSN_UID (insn)); + + /* If the state at the barrier can easily be transformed into the state + at the label, we don't need save/restore points. */ + if (vec_is_prefix_of (barrier_info->cfis, info->cfis)) + { + if (dump_file) + fprintf (dump_file, "prefix\n"); + continue; + } + + /* A save/restore is necessary. Walk backwards to find the best + save point. First see if we know a save point already and if + it's suitable. */ + n_saves_restores++; + if (save_point) + { + prev = save_point; + if (vec_is_prefix_of (save_point_info->cfis, info->cfis)) + { + if (dump_file) + fprintf (dump_file, "reuse save point\n"); + continue; + } + } + + for (;;) + { + prev = PREV_INSN (prev); + /* We should eventually encounter the NOTE_INSN_FUNCTION_BEG, + which must be a suitable save point fo anything. */ + gcc_assert (prev != NULL_RTX); + + if (!save_point_p (prev)) + continue; + + candidate_info = point_info + uid_luid[INSN_UID (prev)]; + /* We don't necessarily get to see this note during + scanning. Record an empty CFI vector for it so that it is + usable as a restore point. */ + if (switch_note_p (prev)) + { + if (candidate_info->cfis == NULL) + candidate_info->cfis = VEC_alloc (dw_cfi_ref, gc, 1); + } + + if (candidate_info->cfis != NULL + && vec_is_prefix_of (candidate_info->cfis, info->cfis) + && (save_point == NULL + || vec_is_prefix_of (candidate_info->cfis, + save_point_info->cfis))) + { + if (dump_file) + fprintf (dump_file, "save point %d\n", INSN_UID (prev)); + save_point = prev; + save_point_info = candidate_info; + break; + } + } } - emit_note_before (NOTE_INSN_CFA_RESTORE_STATE, i); - emit_cfa_remember = true; + save_point = NULL_RTX; + save_point_info = NULL; + remember_needed = false; + + /* This value is now used to distinguish between NOTE_CFI added up + to now and those added by the next loop. */ + max_uid = get_max_uid (); - /* And emulate the state save. */ - gcc_assert (!cfa_remember.in_use); - cfa_remember = cfa; - old_cfa_remember = old_cfa; - cfa_remember.in_use = 1; -} + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + { + jump_target_info *info; -/* A "subroutine" of dwarf2out_cfi_begin_epilogue. Emit the restore - required. */ + if (INSN_UID (insn) < max_uid + && NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_CFI + && remember_needed) + { + cfi_insn = PREV_INSN (insn); + add_cfa_remember (); + cfi_insn = NULL_RTX; + remember_needed = false; + } -static void -dwarf2out_frame_debug_restore_state (void) -{ - dw_cfi_ref cfi = new_cfi (); + if (!save_point_p (insn)) + continue; - cfi->dw_cfi_opc = DW_CFA_restore_state; - add_fde_cfi (cfi); + cfi_insn = insn; + info = point_info + uid_luid[INSN_UID (insn)]; + + if (info->n_restores > 0) + { + gcc_assert (save_point_info == NULL); + save_point_info = info; + remember_needed = true; + } + if (switch_note_p (insn)) + { + jump_target_info *label_info; + rtx next = insn; + + cfi_insn = insn; + if (remember_needed) + add_cfa_remember (); + remember_needed = false; + + /* Find the next label, and emit extra CFIs as necessary to + achieve the correct state. */ + do + { + if (LABEL_P (next)) + { + label_info = point_info + uid_luid[INSN_UID (next)]; + if (label_info->used_as_start) + break; + } + insn = next; + next = NEXT_INSN (next); + } + while (next != NULL_RTX); + if (next == NULL_RTX) + break; + append_extra_cfis (NULL, label_info->cfis); + cfi_insn = NULL_RTX; + } + else if (BARRIER_P (insn)) + { + jump_target_info *label_info; + cfi_vec new_cfi_vec; + cfi_vec barrier_cfi = info->cfis; + rtx next = insn; + + /* Find the start of the next sequence we processed. */ + do + { + if (LABEL_P (next)) + { + label_info = point_info + uid_luid[INSN_UID (next)]; + if (label_info->used_as_start) + break; + } + if (switch_note_p (next)) + break; + insn = next; + next = NEXT_INSN (next); + } + while (next != NULL_RTX); + if (next == NULL_RTX) + break; + if (!LABEL_P (next)) + continue; - gcc_assert (cfa_remember.in_use); - cfa = cfa_remember; - old_cfa = old_cfa_remember; - cfa_remember.in_use = 0; + /* Emit extra CFIs as necessary to achieve the correct state. */ + new_cfi_vec = label_info->cfis; + cfi_insn = next; + if (vec_is_prefix_of (barrier_cfi, new_cfi_vec)) + { + if (VEC_length (dw_cfi_ref, barrier_cfi) + != VEC_length (dw_cfi_ref, new_cfi_vec)) + { + /* If the barrier was a point needing a restore, we must + add the remember here as we ignore the newly added + CFI notes. */ + if (info->n_restores > 0) + add_cfa_remember (); + remember_needed = false; + append_extra_cfis (barrier_cfi, new_cfi_vec); + } + } + else + { + save_point_info->n_restores--; + dwarf2out_frame_debug_restore_state (); + + if (save_point_info->n_restores > 0) + add_cfa_remember (); + gcc_assert (!remember_needed); + append_extra_cfis (save_point_info->cfis, new_cfi_vec); + if (save_point_info->n_restores == 0) + save_point_info = NULL; + } + cfi_insn = NULL_RTX; + } + } + free (uid_luid); + free (point_info); + + add_cfis_to_fde (); +} + +void +dwarf2out_emit_cfi (dw_cfi_ref cfi) +{ + if (dwarf2out_do_cfi_asm ()) + output_cfi_directive (asm_out_file, cfi); } /* Describe for the GTY machinery what parts of dw_cfi_oprnd1 are used. */ @@ -3411,7 +3743,7 @@ output_cfi (dw_cfi_ref cfi, dw_fde_ref f /* Similar, but do it via assembler directives instead. */ static void -output_cfi_directive (dw_cfi_ref cfi) +output_cfi_directive (FILE *f, dw_cfi_ref cfi) { unsigned long r, r2; @@ -3426,82 +3758,96 @@ output_cfi_directive (dw_cfi_ref cfi) /* Should only be created by add_fde_cfi in a code path not followed when emitting via directives. The assembler is going to take care of this for us. */ - gcc_unreachable (); + if (f == asm_out_file) + gcc_unreachable (); + fprintf (f, "\t.cfi_advance_loc\n"); + break; case DW_CFA_offset: case DW_CFA_offset_extended: case DW_CFA_offset_extended_sf: r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1); - fprintf (asm_out_file, "\t.cfi_offset %lu, "HOST_WIDE_INT_PRINT_DEC"\n", + fprintf (f, "\t.cfi_offset %lu, "HOST_WIDE_INT_PRINT_DEC"\n", r, cfi->dw_cfi_oprnd2.dw_cfi_offset); break; case DW_CFA_restore: case DW_CFA_restore_extended: r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1); - fprintf (asm_out_file, "\t.cfi_restore %lu\n", r); + fprintf (f, "\t.cfi_restore %lu\n", r); break; case DW_CFA_undefined: r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1); - fprintf (asm_out_file, "\t.cfi_undefined %lu\n", r); + fprintf (f, "\t.cfi_undefined %lu\n", r); break; case DW_CFA_same_value: r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1); - fprintf (asm_out_file, "\t.cfi_same_value %lu\n", r); + fprintf (f, "\t.cfi_same_value %lu\n", r); break; case DW_CFA_def_cfa: case DW_CFA_def_cfa_sf: r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1); - fprintf (asm_out_file, "\t.cfi_def_cfa %lu, "HOST_WIDE_INT_PRINT_DEC"\n", + fprintf (f, "\t.cfi_def_cfa %lu, "HOST_WIDE_INT_PRINT_DEC"\n", r, cfi->dw_cfi_oprnd2.dw_cfi_offset); break; case DW_CFA_def_cfa_register: r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1); - fprintf (asm_out_file, "\t.cfi_def_cfa_register %lu\n", r); + fprintf (f, "\t.cfi_def_cfa_register %lu\n", r); break; case DW_CFA_register: r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1); r2 = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd2.dw_cfi_reg_num, 1); - fprintf (asm_out_file, "\t.cfi_register %lu, %lu\n", r, r2); + fprintf (f, "\t.cfi_register %lu, %lu\n", r, r2); break; case DW_CFA_def_cfa_offset: case DW_CFA_def_cfa_offset_sf: - fprintf (asm_out_file, "\t.cfi_def_cfa_offset " + fprintf (f, "\t.cfi_def_cfa_offset " HOST_WIDE_INT_PRINT_DEC"\n", cfi->dw_cfi_oprnd1.dw_cfi_offset); break; case DW_CFA_remember_state: - fprintf (asm_out_file, "\t.cfi_remember_state\n"); + fprintf (f, "\t.cfi_remember_state\n"); break; case DW_CFA_restore_state: - fprintf (asm_out_file, "\t.cfi_restore_state\n"); + fprintf (f, "\t.cfi_restore_state\n"); break; case DW_CFA_GNU_args_size: - fprintf (asm_out_file, "\t.cfi_escape %#x,", DW_CFA_GNU_args_size); + if (f != asm_out_file) + { + fprintf (f, "\t.cfi_GNU_args_size"HOST_WIDE_INT_PRINT_DEC "\n", + cfi->dw_cfi_oprnd1.dw_cfi_offset); + break; + } + fprintf (f, "\t.cfi_escape %#x,", DW_CFA_GNU_args_size); dw2_asm_output_data_uleb128_raw (cfi->dw_cfi_oprnd1.dw_cfi_offset); if (flag_debug_asm) - fprintf (asm_out_file, "\t%s args_size "HOST_WIDE_INT_PRINT_DEC, + fprintf (f, "\t%s args_size "HOST_WIDE_INT_PRINT_DEC, ASM_COMMENT_START, cfi->dw_cfi_oprnd1.dw_cfi_offset); - fputc ('\n', asm_out_file); + fputc ('\n', f); break; case DW_CFA_GNU_window_save: - fprintf (asm_out_file, "\t.cfi_window_save\n"); + fprintf (f, "\t.cfi_window_save\n"); break; case DW_CFA_def_cfa_expression: case DW_CFA_expression: - fprintf (asm_out_file, "\t.cfi_escape %#x,", cfi->dw_cfi_opc); + if (f != asm_out_file) + { + fprintf (f, "\t.cfi_cfa_{def_,}expression\n"); + break; + } + fprintf (f, "\t.cfi_escape %#x,", cfi->dw_cfi_opc); output_cfa_loc_raw (cfi); - fputc ('\n', asm_out_file); + fputc ('\n', f); break; default: @@ -3510,14 +3856,11 @@ output_cfi_directive (dw_cfi_ref cfi) } /* Output CFIs from VEC, up to index UPTO, to bring current FDE to the - same state as after executing CFIs in CFI chain. DO_CFI_ASM is - true if .cfi_* directives shall be emitted, false otherwise. If it - is false, FDE and FOR_EH are the other arguments to pass to - output_cfi. */ + same state as after executing CFIs in CFI chain. FDE and FOR_EH + are the other arguments to pass to output_cfi. */ static void -output_cfis (cfi_vec vec, int upto, bool do_cfi_asm, - dw_fde_ref fde, bool for_eh) +output_cfis (cfi_vec vec, int upto, dw_fde_ref fde, bool for_eh) { int ix; struct dw_cfi_struct cfi_buf; @@ -3611,12 +3954,7 @@ output_cfis (cfi_vec vec, int upto, bool if (cfi2 != NULL && cfi2->dw_cfi_opc != DW_CFA_restore && cfi2->dw_cfi_opc != DW_CFA_restore_extended) - { - if (do_cfi_asm) - output_cfi_directive (cfi2); - else - output_cfi (cfi2, fde, for_eh); - } + output_cfi (cfi2, fde, for_eh); } if (cfi_cfa && cfi_cfa_offset && cfi_cfa_offset != cfi_cfa) { @@ -3645,30 +3983,20 @@ output_cfis (cfi_vec vec, int upto, bool else if (cfi_cfa_offset) cfi_cfa = cfi_cfa_offset; if (cfi_cfa) - { - if (do_cfi_asm) - output_cfi_directive (cfi_cfa); - else - output_cfi (cfi_cfa, fde, for_eh); - } + output_cfi (cfi_cfa, fde, for_eh); + cfi_cfa = NULL; cfi_cfa_offset = NULL; if (cfi_args_size && cfi_args_size->dw_cfi_oprnd1.dw_cfi_offset) - { - if (do_cfi_asm) - output_cfi_directive (cfi_args_size); - else - output_cfi (cfi_args_size, fde, for_eh); - } + output_cfi (cfi_args_size, fde, for_eh); + cfi_args_size = NULL; if (cfi == NULL) { VEC_free (dw_cfi_ref, heap, regs); return; } - else if (do_cfi_asm) - output_cfi_directive (cfi); else output_cfi (cfi, fde, for_eh); break; @@ -3678,14 +4006,6 @@ output_cfis (cfi_vec vec, int upto, bool } } -/* Like output_cfis, but emit all CFIs in the vector. */ -static void -output_all_cfis (cfi_vec vec, bool do_cfi_asm, - dw_fde_ref fde, bool for_eh) -{ - output_cfis (vec, VEC_length (dw_cfi_ref, vec), do_cfi_asm, fde, for_eh); -} - /* Output one FDE. */ static void @@ -3801,7 +4121,7 @@ output_fde (dw_fde_ref fde, bool for_eh, if (fde->dw_fde_switch_cfi_index > 0) { from = fde->dw_fde_switch_cfi_index; - output_cfis (fde->dw_fde_cfi, from, false, fde, for_eh); + output_cfis (fde->dw_fde_cfi, from, fde, for_eh); } for (i = from; i < until; i++) output_cfi (VEC_index (dw_cfi_ref, fde->dw_fde_cfi, i), @@ -4379,13 +4699,8 @@ dwarf2out_switch_text_section (void) || (cold_text_section && sect == cold_text_section)); if (dwarf2out_do_cfi_asm ()) - { - dwarf2out_do_cfi_startproc (true); - /* As this is a different FDE, insert all current CFI instructions - again. */ - output_all_cfis (fde->dw_fde_cfi, true, fde, true); - } - fde->dw_fde_switch_cfi_index = VEC_length (dw_cfi_ref, fde->dw_fde_cfi); + dwarf2out_do_cfi_startproc (true); + var_location_switch_text_section (); set_cur_line_info_table (sect); @@ -5490,7 +5805,7 @@ output_loc_operands_raw (dw_loc_descr_re dw2_asm_output_data_uleb128_raw (r); } break; - + case DW_OP_constu: case DW_OP_plus_uconst: case DW_OP_piece: @@ -12472,7 +12787,7 @@ output_one_line_info_table (dw_line_info dw2_asm_output_data (1, DW_LNS_set_prologue_end, "set prologue end"); break; - + case LI_set_epilogue_begin: dw2_asm_output_data (1, DW_LNS_set_epilogue_begin, "set epilogue begin"); @@ -14799,7 +15114,7 @@ static bool decl_by_reference_p (tree decl) { return ((TREE_CODE (decl) == PARM_DECL || TREE_CODE (decl) == RESULT_DECL - || TREE_CODE (decl) == VAR_DECL) + || TREE_CODE (decl) == VAR_DECL) && DECL_BY_REFERENCE (decl)); } @@ -20724,7 +21039,7 @@ gen_type_die_with_usage (tree type, dw_d if (DECL_CONTEXT (TYPE_NAME (type)) && TREE_CODE (DECL_CONTEXT (TYPE_NAME (type))) == NAMESPACE_DECL) context_die = get_context_die (DECL_CONTEXT (TYPE_NAME (type))); - + gen_decl_die (TYPE_NAME (type), NULL, context_die); return; } @@ -21954,7 +22269,7 @@ gen_scheduled_generic_parms_dies (void) if (generic_type_instances == NULL) return; - + FOR_EACH_VEC_ELT (tree, generic_type_instances, i, t) gen_generic_params_dies (t); } @@ -23863,7 +24178,7 @@ dwarf2out_finish (const char *filename) if (!VEC_empty (pubname_entry, pubtype_table)) { bool empty = false; - + if (flag_eliminate_unused_debug_types) { /* The pubtypes table might be emptied by pruning unused items. */ Index: gcc/jump.c =================================================================== --- gcc.orig/jump.c +++ gcc/jump.c @@ -709,6 +709,15 @@ comparison_dominates_p (enum rtx_code co return 0; } +/* Return true if INSN is an ADDR_VEC or ADDR_DIFF_VEC. */ +bool +addr_vec_p (const_rtx insn) +{ + return (JUMP_P (insn) + && (GET_CODE (PATTERN (insn)) == ADDR_VEC + || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC)); +} + /* Return 1 if INSN is an unconditional jump and nothing else. */ int Index: gcc/rtl.h =================================================================== --- gcc.orig/rtl.h +++ gcc/rtl.h @@ -2307,6 +2307,7 @@ extern int any_condjump_p (const_rtx); extern int any_uncondjump_p (const_rtx); extern rtx pc_set (const_rtx); extern rtx condjump_label (const_rtx); +extern bool addr_vec_p (const_rtx); extern int simplejump_p (const_rtx); extern int returnjump_p (rtx); extern int eh_returnjump_p (rtx);