+
+/* Scan the function and create the initial set of CFI notes. */
+
+static void
+create_cfi_notes (void)
+{
+ dw_trace_info *ti;
+
+ gcc_checking_assert (queued_reg_saves == NULL);
+ gcc_checking_assert (trace_work_list == NULL);
+
+ /* Always begin at the entry trace. */
+ ti = VEC_index (dw_trace_info, trace_info, 0);
+ scan_trace (ti);
+
+ while (!VEC_empty (dw_trace_info_ref, trace_work_list))
+ {
+ ti = VEC_pop (dw_trace_info_ref, trace_work_list);
+ scan_trace (ti);
+ }
+
+ VEC_free (queued_reg_save, heap, queued_reg_saves);
+ VEC_free (dw_trace_info_ref, heap, trace_work_list);
+}
+
+/* Return the insn before the first NOTE_INSN_CFI after START. */
+
+static rtx
+before_next_cfi_note (rtx start)
+{
+ rtx prev = start;
+ while (start)
+ {
+ if (NOTE_P (start) && NOTE_KIND (start) == NOTE_INSN_CFI)
+ return prev;
+ prev = start;
+ start = NEXT_INSN (start);
+ }
+ gcc_unreachable ();
+}
+
+/* Insert CFI notes between traces to properly change state between them. */
+
+static void
+connect_traces (void)
+{
+ unsigned i, n = VEC_length (dw_trace_info, trace_info);
+ dw_trace_info *prev_ti, *ti;
+
+ /* ??? Ideally, we should have both queued and processed every trace.
+ However the current representation of constant pools on various targets
+ is indistinguishable from unreachable code. Assume for the moment that
+ we can simply skip over such traces. */
+ /* ??? Consider creating a DATA_INSN rtx code to indicate that
+ these are not "real" instructions, and should not be considered.
+ This could be generically useful for tablejump data as well. */
+ /* Remove all unprocessed traces from the list. */
+ for (i = n - 1; i > 0; --i)
+ {
+ ti = VEC_index (dw_trace_info, trace_info, i);
+ if (ti->beg_row == NULL)
+ {
+ VEC_ordered_remove (dw_trace_info, trace_info, i);
+ n -= 1;
+ }
+ else
+ gcc_assert (ti->end_row != NULL);
+ }
+
+ /* Work from the end back to the beginning. This lets us easily insert
+ remember/restore_state notes in the correct order wrt other notes. */
+ prev_ti = VEC_index (dw_trace_info, trace_info, n - 1);
+ for (i = n - 1; i > 0; --i)
+ {
+ dw_cfi_row *old_row;
+
+ ti = prev_ti;
+ prev_ti = VEC_index (dw_trace_info, trace_info, i - 1);
+
+ add_cfi_insn = ti->head;
+
+ /* In dwarf2out_switch_text_section, we'll begin a new FDE
+ for the portion of the function in the alternate text
+ section. The row state at the very beginning of that
+ new FDE will be exactly the row state from the CIE. */
+ if (ti->switch_sections)
+ old_row = cie_cfi_row;
+ else
+ {
+ old_row = prev_ti->end_row;
+ /* If there's no change from the previous end state, fine. */
+ if (cfi_row_equal_p (old_row, ti->beg_row))
+ ;
+ /* Otherwise check for the common case of sharing state with
+ the beginning of an epilogue, but not the end. Insert
+ remember/restore opcodes in that case. */
+ else if (cfi_row_equal_p (prev_ti->beg_row, ti->beg_row))
+ {
+ dw_cfi_ref cfi;
+
+ /* Note that if we blindly insert the remember at the
+ start of the trace, we can wind up increasing the
+ size of the unwind info due to extra advance opcodes.
+ Instead, put the remember immediately before the next
+ state change. We know there must be one, because the
+ state at the beginning and head of the trace differ. */
+ add_cfi_insn = before_next_cfi_note (prev_ti->head);
+ cfi = new_cfi ();
+ cfi->dw_cfi_opc = DW_CFA_remember_state;
+ add_cfi (cfi);
+
+ add_cfi_insn = ti->head;
+ cfi = new_cfi ();
+ cfi->dw_cfi_opc = DW_CFA_restore_state;
+ add_cfi (cfi);
+
+ old_row = prev_ti->beg_row;
+ }
+ /* Otherwise, we'll simply change state from the previous end. */
+ }
+
+ change_cfi_row (old_row, ti->beg_row);
+
+ if (dump_file && add_cfi_insn != ti->head)
+ {
+ rtx note;
+
+ fprintf (dump_file, "Fixup between trace %u and %u:\n",
+ prev_ti->id, ti->id);
+
+ note = ti->head;
+ do
+ {
+ note = NEXT_INSN (note);
+ gcc_assert (NOTE_P (note) && NOTE_KIND (note) == NOTE_INSN_CFI);
+ output_cfi_directive (dump_file, NOTE_CFI (note));
+ }
+ while (note != add_cfi_insn);
+ }
+ }
+
+ /* Connect args_size between traces that have can_throw_internal insns. */
+ if (cfun->eh->lp_array != NULL)
+ {
+ HOST_WIDE_INT prev_args_size = 0;
+
+ for (i = 0; i < n; ++i)
+ {
+ ti = VEC_index (dw_trace_info, trace_info, i);
+
+ if (ti->switch_sections)
+ prev_args_size = 0;
+ if (ti->eh_head == NULL)
+ continue;
+ gcc_assert (!ti->args_size_undefined);
+
+ if (ti->beg_delay_args_size != prev_args_size)
+ {
+ /* ??? Search back to previous CFI note. */
+ add_cfi_insn = PREV_INSN (ti->eh_head);
+ add_cfi_args_size (ti->beg_delay_args_size);
+ }
+
+ prev_args_size = ti->end_delay_args_size;
+ }
+ }
+}
+
+/* Set up the pseudo-cfg of instruction traces, as described at the
+ block comment at the top of the file. */
+
+static void
+create_pseudo_cfg (void)
+{
+ bool saw_barrier, switch_sections;
+ dw_trace_info *ti;
+ rtx insn;
+ unsigned i;
+
+ /* The first trace begins at the start of the function,
+ and begins with the CIE row state. */
+ trace_info = VEC_alloc (dw_trace_info, heap, 16);
+ ti = VEC_quick_push (dw_trace_info, trace_info, NULL);
+
+ memset (ti, 0, sizeof (*ti));
+ ti->head = get_insns ();
+ ti->beg_row = cie_cfi_row;
+ ti->cfa_store = cie_cfi_row->cfa;
+ ti->cfa_temp.reg = INVALID_REGNUM;
+ if (cie_return_save)
+ VEC_safe_push (reg_saved_in_data, heap,
+ ti->regs_saved_in_regs, cie_return_save);
+
+ /* Walk all the insns, collecting start of trace locations. */
+ saw_barrier = false;
+ switch_sections = false;
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ {
+ if (BARRIER_P (insn))
+ saw_barrier = true;
+ else if (NOTE_P (insn)
+ && NOTE_KIND (insn) == NOTE_INSN_SWITCH_TEXT_SECTIONS)
+ {
+ /* We should have just seen a barrier. */
+ gcc_assert (saw_barrier);
+ switch_sections = true;
+ }
+ /* Watch out for save_point notes between basic blocks.
+ In particular, a note after a barrier. Do not record these,
+ delaying trace creation until the label. */
+ else if (save_point_p (insn)
+ && (LABEL_P (insn) || !saw_barrier))
+ {
+ ti = VEC_safe_push (dw_trace_info, heap, trace_info, NULL);
+ memset (ti, 0, sizeof (*ti));
+ ti->head = insn;
+ ti->switch_sections = switch_sections;
+ ti->id = VEC_length (dw_trace_info, trace_info) - 1;
+
+ saw_barrier = false;
+ switch_sections = false;
+ }
+ }
+
+ /* Create the trace index after we've finished building trace_info,
+ avoiding stale pointer problems due to reallocation. */
+ trace_index = htab_create (VEC_length (dw_trace_info, trace_info),
+ dw_trace_info_hash, dw_trace_info_eq, NULL);
+ FOR_EACH_VEC_ELT (dw_trace_info, trace_info, i, ti)
+ {
+ void **slot;
+
+ if (dump_file)
+ fprintf (dump_file, "Creating trace %u : start at %s %d%s\n", i,
+ rtx_name[(int) GET_CODE (ti->head)], INSN_UID (ti->head),
+ ti->switch_sections ? " (section switch)" : "");
+
+ slot = htab_find_slot_with_hash (trace_index, ti,
+ INSN_UID (ti->head), INSERT);
+ gcc_assert (*slot == NULL);
+ *slot = (void *) ti;
+ }
+}
+