dwarf2cfi: Generate and connect traces.

This kinda-sorta corresponds to Bernd's 007-dw2cfi patch.  Certainly
the same concepts of splitting the instruction stream into extended
basic blocks is the same.  This patch does a bit better job with the
documentation.  Also, I'm a bit more explicit about matching things
up with the similar code from the regular CFG routines.

What's missing at this point is any attempt to use DW_CFA_remember_state.
I've deferred that for the moment because it's easy to test the state
change code across epilogues, whereas the shrink-wrapping code is not
in this tree and section switching is difficult to force.

        * dwarf2cfi.c: Include basic-block.h.
        (dw_label_info): Remove.
        (trace_work_list, trace_index): New.
        (remember_row, emit_cfa_remember): Remove.
        (dw_trace_info_hash, dw_trace_info_eq): New.
        (get_trace_index, get_trace_info): New.
        (save_point_p): New.
        (free_cfi_row): Remove.
        (add_cfi): Do not emit DW_CFA_remember_state.
        (cfa_row_equal_p): New.
        (barrier_args_size): Remove.
        (compute_barrier_args_size_1, compute_barrier_args_size): Remove.
        (dwarf2out_notice_stack_adjust): Don't compute_barrier_args_size.
        (maybe_record_trace_start, create_trace_edges, scan_trace): New.
        (dwarf2out_cfi_begin_epilogue): Remove.
        (dwarf2out_frame_debug_restore_state): Remove.
        (connect_traces, create_pseudo_cfg): New.
        (create_cfi_notes, execute_dwarf2_frame): Rewrite using traces.
        * Makefile.in (dwarf2cfi.o): Update.

From-SVN: r176705
This commit is contained in:
Richard Henderson 2011-07-23 13:49:33 -07:00 committed by Richard Henderson
parent 43215a89ec
commit 829bdd4b09
3 changed files with 451 additions and 413 deletions

View File

@ -1,3 +1,25 @@
2011-07-23 Richard Henderson <rth@redhat.com>
* dwarf2cfi.c: Include basic-block.h.
(dw_label_info): Remove.
(trace_work_list, trace_index): New.
(remember_row, emit_cfa_remember): Remove.
(dw_trace_info_hash, dw_trace_info_eq): New.
(get_trace_index, get_trace_info): New.
(save_point_p): New.
(free_cfi_row): Remove.
(add_cfi): Do not emit DW_CFA_remember_state.
(cfa_row_equal_p): New.
(barrier_args_size): Remove.
(compute_barrier_args_size_1, compute_barrier_args_size): Remove.
(dwarf2out_notice_stack_adjust): Don't compute_barrier_args_size.
(maybe_record_trace_start, create_trace_edges, scan_trace): New.
(dwarf2out_cfi_begin_epilogue): Remove.
(dwarf2out_frame_debug_restore_state): Remove.
(connect_traces, create_pseudo_cfg): New.
(create_cfi_notes, execute_dwarf2_frame): Rewrite using traces.
* Makefile.in (dwarf2cfi.o): Update.
2011-07-23 Richard Henderson <rth@redhat.com>
* dwarf2cfi.c (dw_trace_info): New.

View File

@ -2957,7 +2957,7 @@ dwarf2out.o : dwarf2out.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
tree-pretty-print.h $(COMMON_TARGET_H) $(OPTS_H)
dwarf2cfi.o : dwarf2cfi.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
version.h $(RTL_H) $(FUNCTION_H) $(DWARF2_H) dwarf2asm.h dwarf2out.h \
$(GGC_H) $(TM_P_H) $(TARGET_H) $(TREE_PASS_H)
$(GGC_H) $(TM_P_H) $(TARGET_H) $(TREE_PASS_H) $(BASIC_BLOCK_H)
dwarf2asm.o : dwarf2asm.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
$(FLAGS_H) $(RTL_H) $(TREE_H) output.h dwarf2asm.h $(TM_P_H) $(GGC_H) \
gt-dwarf2asm.h $(DWARF2_H) $(SPLAY_TREE_H) $(TARGET_H)

View File

@ -27,6 +27,7 @@ along with GCC; see the file COPYING3. If not see
#include "flags.h"
#include "rtl.h"
#include "function.h"
#include "basic-block.h"
#include "dwarf2.h"
#include "dwarf2out.h"
#include "dwarf2asm.h"
@ -86,34 +87,30 @@ DEF_VEC_ALLOC_O (reg_saved_in_data, heap);
/* Since we no longer have a proper CFG, we're going to create a facsimile
of one on the fly while processing the frame-related insns.
We create dw_trace structures for each instruction trace beginning at
at a label following a barrier (or beginning of the function), and
ending at a barrier (or the end of the function).
We create dw_trace_info structures for each extended basic block beginning
and ending at a "save point". Save points are labels, barriers, certain
notes, and of course the beginning and end of the function.
As we encounter control transfer insns, we propagate the "current"
row state across the edges to the starts of traces. If an edge goes
to a label that is not the start of a trace, we ignore it. This
assumes that previous compiler transformations were correct, and that
we will reach the same row state from any source. (We can perform some
limited validation of this assumption, but without the full CFG we
cannot be sure of full validation coverage. It is expensive, so we
only do so with checking enabled.)
row state across the edges to the starts of traces. When checking is
enabled, we validate that we propagate the same data from all sources.
All traces are members of the TRACE_INFO array, in the order in which
they appear in the instruction stream.
All labels are given an LUID that indexes the LABEL_INFO array. If
the label is the start of a trace, the TRACE pointer will be non-NULL
and point into the TRACE_INFO array. */
All save points are present in the TRACE_INDEX hash, mapping the insn
starting a trace to the dw_trace_info describing the trace. */
typedef struct
{
/* The label that begins the trace. This will be NULL for the first
trace beginning at function entry. */
rtx label;
/* The insn that begins the trace. */
rtx head;
/* The row state at the beginning and end of the trace. */
dw_cfi_row *enter_row, *exit_row;
dw_cfi_row *beg_row, *end_row;
/* True if this trace immediately follows NOTE_INSN_SWITCH_TEXT_SECTIONS. */
bool switch_sections;
/* The following variables contain data used in interpreting frame related
expressions. These are not part of the "real" row state as defined by
@ -147,24 +144,15 @@ typedef struct
DEF_VEC_O (dw_trace_info);
DEF_VEC_ALLOC_O (dw_trace_info, heap);
typedef struct
{
dw_trace_info *trace;
typedef dw_trace_info *dw_trace_info_ref;
#ifdef ENABLE_CHECKING
dw_cfi_row *check_row;
#endif
} dw_label_info;
DEF_VEC_O (dw_label_info);
DEF_VEC_ALLOC_O (dw_label_info, heap);
DEF_VEC_P (dw_trace_info_ref);
DEF_VEC_ALLOC_P (dw_trace_info_ref, heap);
/* The variables making up the pseudo-cfg, as described above. */
#if 0
static VEC (int, heap) *uid_luid;
static VEC (dw_label_info, heap) *label_info;
static VEC (dw_trace_info, heap) *trace_info;
#endif
static VEC (dw_trace_info_ref, heap) *trace_work_list;
static htab_t trace_index;
/* A vector of call frame insns for the CIE. */
cfi_vec cie_cfi_vec;
@ -189,9 +177,6 @@ static dw_trace_info *cur_trace;
/* The current, i.e. most recently generated, row of the CFI table. */
static dw_cfi_row *cur_row;
/* The row state from a preceeding DW_CFA_remember_state. */
static dw_cfi_row *remember_row;
/* We delay emitting a register save until either (a) we reach the end
of the prologue or (b) the register is clobbered. This clusters
register saves so that there are fewer pc advances. */
@ -211,20 +196,12 @@ static VEC(queued_reg_save, heap) *queued_reg_saves;
emitting this data, i.e. updating CUR_ROW, without async unwind. */
static HOST_WIDE_INT queued_args_size;
/* 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;
/* Short-hand for commonly used register numbers. */
static unsigned dw_stack_pointer_regnum;
static unsigned dw_frame_pointer_regnum;
static void dwarf2out_cfi_begin_epilogue (rtx insn);
static void dwarf2out_frame_debug_restore_state (void);
/* Hook used by __throw. */
@ -295,6 +272,59 @@ expand_builtin_init_dwarf_reg_sizes (tree address)
targetm.init_dwarf_reg_sizes_extra (address);
}
static hashval_t
dw_trace_info_hash (const void *ptr)
{
const dw_trace_info *ti = (const dw_trace_info *) ptr;
return INSN_UID (ti->head);
}
static int
dw_trace_info_eq (const void *ptr_a, const void *ptr_b)
{
const dw_trace_info *a = (const dw_trace_info *) ptr_a;
const dw_trace_info *b = (const dw_trace_info *) ptr_b;
return a->head == b->head;
}
static unsigned
get_trace_index (dw_trace_info *trace)
{
return trace - VEC_address (dw_trace_info, trace_info);
}
static dw_trace_info *
get_trace_info (rtx insn)
{
dw_trace_info dummy;
dummy.head = insn;
return (dw_trace_info *)
htab_find_with_hash (trace_index, &dummy, INSN_UID (insn));
}
static bool
save_point_p (rtx insn)
{
/* Labels, except those that are really jump tables. */
if (LABEL_P (insn))
return inside_basic_block_p (insn);
/* We split traces at the prologue/epilogue notes because those
are points at which the unwind info is usually stable. This
makes it easier to find spots with identical unwind info so
that we can use remember/restore_state opcodes. */
if (NOTE_P (insn))
switch (NOTE_KIND (insn))
{
case NOTE_INSN_PROLOGUE_END:
case NOTE_INSN_EPILOGUE_BEG:
return true;
}
return false;
}
/* Divide OFF by DWARF_CIE_DATA_ALIGNMENT, asserting no remainder. */
static inline HOST_WIDE_INT
@ -352,18 +382,6 @@ copy_cfi_row (dw_cfi_row *src)
return dst;
}
/* Free an allocated CFI row. */
static void
free_cfi_row (dw_cfi_row *row)
{
if (row != NULL)
{
VEC_free (dw_cfi_ref, gc, row->reg_save);
ggc_free (row);
}
}
/* Generate a new label for the CFI info to refer to. */
static char *
@ -382,17 +400,6 @@ dwarf2out_cfi_label (void)
static void
add_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_cfi (cfi_remember);
}
any_cfis_emitted = true;
if (add_cfi_insn != NULL)
@ -645,6 +652,44 @@ cfi_equal_p (dw_cfi_ref a, dw_cfi_ref b)
&a->dw_cfi_oprnd2, &b->dw_cfi_oprnd2));
}
/* Determine if two CFI_ROW structures are identical. */
static bool
cfi_row_equal_p (dw_cfi_row *a, dw_cfi_row *b)
{
size_t i, n_a, n_b, n_max;
if (a->cfa_cfi)
{
if (!cfi_equal_p (a->cfa_cfi, b->cfa_cfi))
return false;
}
else if (!cfa_equal_p (&a->cfa, &b->cfa))
return false;
if (a->args_size != b->args_size)
return false;
n_a = VEC_length (dw_cfi_ref, a->reg_save);
n_b = VEC_length (dw_cfi_ref, b->reg_save);
n_max = MAX (n_a, n_b);
for (i = 0; i < n_max; ++i)
{
dw_cfi_ref r_a = NULL, r_b = NULL;
if (i < n_a)
r_a = VEC_index (dw_cfi_ref, a->reg_save, i);
if (i < n_b)
r_b = VEC_index (dw_cfi_ref, b->reg_save, i);
if (!cfi_equal_p (r_a, r_b))
return false;
}
return true;
}
/* The CFA is now calculated from NEW_CFA. Consider OLD_CFA in determining
what opcode to emit. Returns the CFI opcode to effect the change, or
NULL if NEW_CFA == OLD_CFA. */
@ -878,179 +923,6 @@ stack_adjust_offset (const_rtx pattern, HOST_WIDE_INT cur_args_size,
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. */
@ -1150,25 +1022,7 @@ dwarf2out_notice_stack_adjust (rtx insn, bool after_p)
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 -= queued_args_size;
#ifndef STACK_GROWS_DOWNWARD
offset = -offset;
#endif
}
return;
else if (GET_CODE (PATTERN (insn)) == SET)
offset = stack_adjust_offset (PATTERN (insn), queued_args_size, 0);
else if (GET_CODE (PATTERN (insn)) == PARALLEL
@ -2531,184 +2385,357 @@ add_cfis_to_fde (void)
}
}
/* Scan the function and create the initial set of CFI notes. */
/* If LABEL is the start of a trace, then initialize the state of that
trace from CUR_TRACE and CUR_ROW. */
static void
create_cfi_notes (void)
maybe_record_trace_start (rtx start, rtx origin)
{
rtx insn;
dw_trace_info *ti;
for (insn = get_insns (); insn ; insn = NEXT_INSN (insn))
/* Sync queued data before propagating to a destination,
lest we propagate out-of-date data. */
dwarf2out_flush_queued_reg_saves ();
dwarf2out_args_size (queued_args_size);
ti = get_trace_info (start);
gcc_assert (ti != NULL);
if (dump_file)
{
fprintf (dump_file, " saw edge from trace %u to %u (via %s %d)\n",
get_trace_index (cur_trace), get_trace_index (ti),
(origin ? rtx_name[(int) GET_CODE (origin)] : "fallthru"),
(origin ? INSN_UID (origin) : 0));
}
if (ti->beg_row == NULL)
{
/* This is the first time we've encountered this trace. Propagate
state across the edge and push the trace onto the work list. */
ti->beg_row = copy_cfi_row (cur_row);
ti->cfa_store = cur_trace->cfa_store;
ti->cfa_temp = cur_trace->cfa_temp;
ti->regs_saved_in_regs = VEC_copy (reg_saved_in_data, heap,
cur_trace->regs_saved_in_regs);
VEC_safe_push (dw_trace_info_ref, heap, trace_work_list, ti);
if (dump_file)
fprintf (dump_file, "\tpush trace %u to worklist\n",
get_trace_index (ti));
}
else
{
/* We ought to have the same state incoming to a given trace no
matter how we arrive at the trace. Anything else means we've
got some kind of optimization error. */
gcc_checking_assert (cfi_row_equal_p (cur_row, ti->beg_row));
}
}
/* Propagate CUR_TRACE state to the destinations implied by INSN. */
/* ??? Sadly, this is in large part a duplicate of make_edges. */
static void
create_trace_edges (rtx insn)
{
rtx tmp, lab;
int i, n;
if (JUMP_P (insn))
{
if (find_reg_note (insn, REG_NON_LOCAL_GOTO, NULL_RTX))
;
else if (tablejump_p (insn, NULL, &tmp))
{
rtvec vec;
tmp = PATTERN (tmp);
vec = XVEC (tmp, GET_CODE (tmp) == ADDR_DIFF_VEC);
n = GET_NUM_ELEM (vec);
for (i = 0; i < n; ++i)
{
lab = XEXP (RTVEC_ELT (vec, i), 0);
maybe_record_trace_start (lab, insn);
}
}
else if (computed_jump_p (insn))
{
for (lab = forced_labels; lab; lab = XEXP (lab, 1))
maybe_record_trace_start (XEXP (lab, 0), insn);
}
else if (returnjump_p (insn))
;
else if ((tmp = extract_asm_operands (PATTERN (insn))) != NULL)
{
n = ASM_OPERANDS_LABEL_LENGTH (tmp);
for (i = 0; i < n; ++i)
{
lab = XEXP (ASM_OPERANDS_LABEL (tmp, i), 0);
maybe_record_trace_start (lab, insn);
}
}
else
{
lab = JUMP_LABEL (insn);
gcc_assert (lab != NULL);
maybe_record_trace_start (lab, insn);
}
}
else if (CALL_P (insn))
{
/* Sibling calls don't have edges inside this function. */
if (SIBLING_CALL_P (insn))
return;
/* Process non-local goto edges. */
if (can_nonlocal_goto (insn))
for (lab = nonlocal_goto_handler_labels; lab; lab = XEXP (lab, 1))
maybe_record_trace_start (XEXP (lab, 0), insn);
}
/* Process EH edges. */
if (CALL_P (insn) || cfun->can_throw_non_call_exceptions)
{
eh_landing_pad lp = get_eh_landing_pad_from_rtx (insn);
if (lp)
maybe_record_trace_start (lp->landing_pad, insn);
}
}
/* Scan the trace beginning at INSN and create the CFI notes for the
instructions therein. */
static void
scan_trace (dw_trace_info *trace)
{
rtx insn = trace->head;
if (dump_file)
fprintf (dump_file, "Processing trace %u : start at %s %d\n",
get_trace_index (trace), rtx_name[(int) GET_CODE (insn)],
INSN_UID (insn));
trace->end_row = copy_cfi_row (trace->beg_row);
cur_trace = trace;
cur_row = trace->end_row;
queued_args_size = cur_row->args_size;
for (insn = NEXT_INSN (insn); insn ; insn = NEXT_INSN (insn))
{
rtx pat;
add_cfi_insn = PREV_INSN (insn);
if (BARRIER_P (insn))
/* Notice the end of a trace. */
if (BARRIER_P (insn) || save_point_p (insn))
{
dwarf2out_frame_debug (insn, false);
continue;
}
dwarf2out_flush_queued_reg_saves ();
dwarf2out_args_size (queued_args_size);
if (NOTE_P (insn))
{
switch (NOTE_KIND (insn))
{
case NOTE_INSN_PROLOGUE_END:
dwarf2out_flush_queued_reg_saves ();
break;
case NOTE_INSN_EPILOGUE_BEG:
#if defined(HAVE_epilogue)
dwarf2out_cfi_begin_epilogue (insn);
#endif
break;
case NOTE_INSN_CFA_RESTORE_STATE:
add_cfi_insn = insn;
dwarf2out_frame_debug_restore_state ();
break;
case NOTE_INSN_SWITCH_TEXT_SECTIONS:
/* 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.
Emit whatever CFIs are necessary to make CUR_ROW current. */
add_cfi_insn = insn;
change_cfi_row (cie_cfi_row, cur_row);
break;
}
continue;
/* Propagate across fallthru edges. */
if (!BARRIER_P (insn))
maybe_record_trace_start (insn, NULL);
break;
}
if (!NONDEBUG_INSN_P (insn))
if (DEBUG_INSN_P (insn) || !inside_basic_block_p (insn))
continue;
pat = PATTERN (insn);
if (asm_noperands (pat) >= 0)
{
dwarf2out_frame_debug (insn, false);
continue;
add_cfi_insn = insn;
}
if (GET_CODE (pat) == SEQUENCE)
else
{
int i, n = XVECLEN (pat, 0);
for (i = 1; i < n; ++i)
dwarf2out_frame_debug (XVECEXP (pat, 0, i), false);
if (GET_CODE (pat) == SEQUENCE)
{
int i, n = XVECLEN (pat, 0);
for (i = 1; i < n; ++i)
dwarf2out_frame_debug (XVECEXP (pat, 0, i), false);
}
if (CALL_P (insn))
dwarf2out_frame_debug (insn, false);
else if (find_reg_note (insn, REG_CFA_FLUSH_QUEUE, NULL)
|| (cfun->can_throw_non_call_exceptions
&& can_throw_internal (insn)))
dwarf2out_flush_queued_reg_saves ();
/* Do not separate tablejump insns from their ADDR_DIFF_VEC.
Putting the note after the VEC should be ok. */
if (!tablejump_p (insn, NULL, &add_cfi_insn))
add_cfi_insn = insn;
dwarf2out_frame_debug (insn, true);
}
if (CALL_P (insn)
|| find_reg_note (insn, REG_CFA_FLUSH_QUEUE, NULL))
dwarf2out_frame_debug (insn, false);
/* Do not separate tablejump insns from their ADDR_DIFF_VEC.
Putting the note after the VEC should be ok. */
if (!tablejump_p (insn, NULL, &add_cfi_insn))
add_cfi_insn = insn;
dwarf2out_frame_debug (insn, true);
/* Note that a test for control_flow_insn_p does exactly the
same tests as are done to actually create the edges. So
always call the routine and let it not create edges for
non-control-flow insns. */
create_trace_edges (insn);
}
add_cfi_insn = NULL;
cur_row = NULL;
cur_trace = NULL;
}
/* Determine if we need to save and restore CFI information around this
epilogue. If SIBCALL is true, then this is a sibcall 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. */
/* Scan the function and create the initial set of CFI notes. */
static void
dwarf2out_cfi_begin_epilogue (rtx insn)
create_cfi_notes (void)
{
bool saw_frp = false;
rtx i;
dw_trace_info *ti;
/* Scan forward to the return insn, noticing if there are possible
frame related insns. */
for (i = NEXT_INSN (insn); i ; i = NEXT_INSN (i))
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))
{
if (!INSN_P (i))
continue;
ti = VEC_pop (dw_trace_info_ref, trace_work_list);
scan_trace (ti);
}
/* Look for both regular and sibcalls to end the block. */
if (returnjump_p (i))
break;
if (CALL_P (i) && SIBLING_CALL_P (i))
break;
VEC_free (queued_reg_save, heap, queued_reg_saves);
VEC_free (dw_trace_info_ref, heap, trace_work_list);
}
if (GET_CODE (PATTERN (i)) == SEQUENCE)
/* Insert CFI notes between traces to properly change state between them. */
/* ??? TODO: Make use of remember/restore_state. */
static void
connect_traces (void)
{
unsigned i, n = VEC_length (dw_trace_info, trace_info);
dw_trace_info *prev_ti, *ti;
prev_ti = VEC_index (dw_trace_info, trace_info, 0);
for (i = 1; i < n; ++i, prev_ti = ti)
{
dw_cfi_row *old_row;
ti = VEC_index (dw_trace_info, trace_info, i);
/* We must have both queued and processed every trace. */
gcc_assert (ti->beg_row && ti->end_row);
/* 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;
add_cfi_insn = ti->head;
change_cfi_row (old_row, ti->beg_row);
if (dump_file && add_cfi_insn != ti->head)
{
int idx;
rtx seq = PATTERN (i);
rtx note;
if (returnjump_p (XVECEXP (seq, 0, 0)))
break;
if (CALL_P (XVECEXP (seq, 0, 0))
&& SIBLING_CALL_P (XVECEXP (seq, 0, 0)))
break;
fprintf (dump_file, "Fixup between trace %u and %u:\n", i - 1, i);
for (idx = 0; idx < XVECLEN (seq, 0); idx++)
if (RTX_FRAME_RELATED_P (XVECEXP (seq, 0, idx)))
saw_frp = true;
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);
}
if (RTX_FRAME_RELATED_P (i))
saw_frp = true;
}
/* If the port doesn't emit epilogue unwind info, we don't need a
save/restore pair. */
if (!saw_frp)
return;
/* 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;
/* 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)
{
rtx p = PREV_INSN (i);
if (!NOTE_P (p))
break;
if (NOTE_KIND (p) == NOTE_INSN_BASIC_BLOCK)
break;
i = p;
}
emit_note_before (NOTE_INSN_CFA_RESTORE_STATE, i);
emit_cfa_remember = true;
/* And emulate the state save. */
gcc_assert (remember_row == NULL);
remember_row = copy_cfi_row (cur_row);
}
/* A "subroutine" of dwarf2out_cfi_begin_epilogue. Emit the restore
required. */
/* Set up the pseudo-cfg of instruction traces, as described at the
block comment at the top of the file. */
static void
dwarf2out_frame_debug_restore_state (void)
create_pseudo_cfg (void)
{
dw_cfi_ref cfi = new_cfi ();
bool saw_barrier, switch_sections;
dw_trace_info *ti;
rtx insn;
unsigned i;
cfi->dw_cfi_opc = DW_CFA_restore_state;
add_cfi (cfi);
/* 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);
gcc_assert (remember_row != NULL);
free_cfi_row (cur_row);
cur_row = remember_row;
remember_row = 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;
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;
}
}
/* Record the initial position of the return address. RTL is
INCOMING_RETURN_ADDR_RTX. */
@ -2832,42 +2859,31 @@ create_cie_data (void)
static unsigned int
execute_dwarf2_frame (void)
{
dw_trace_info dummy_trace;
gcc_checking_assert (queued_reg_saves == NULL);
/* The first time we're called, compute the incoming frame state. */
if (cie_cfi_vec == NULL)
create_cie_data ();
memset (&dummy_trace, 0, sizeof(dummy_trace));
cur_trace = &dummy_trace;
/* Set up state for generating call frame debug info. */
cur_row = copy_cfi_row (cie_cfi_row);
if (cie_return_save)
VEC_safe_push (reg_saved_in_data, heap,
cur_trace->regs_saved_in_regs, cie_return_save);
cur_trace->cfa_store = cur_row->cfa;
cur_trace->cfa_temp.reg = INVALID_REGNUM;
queued_args_size = 0;
dwarf2out_alloc_current_fde ();
create_pseudo_cfg ();
/* Do the work. */
create_cfi_notes ();
connect_traces ();
add_cfis_to_fde ();
/* Reset all function-specific information, particularly for GC. */
XDELETEVEC (barrier_args_size);
barrier_args_size = NULL;
VEC_free (reg_saved_in_data, heap, cur_trace->regs_saved_in_regs);
VEC_free (queued_reg_save, heap, queued_reg_saves);
/* Free all the data we allocated. */
{
size_t i;
dw_trace_info *ti;
free_cfi_row (cur_row);
cur_row = NULL;
cur_trace = NULL;
FOR_EACH_VEC_ELT (dw_trace_info, trace_info, i, ti)
VEC_free (reg_saved_in_data, heap, ti->regs_saved_in_regs);
}
VEC_free (dw_trace_info, heap, trace_info);
htab_delete (trace_index);
trace_index = NULL;
return 0;
}