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:
parent
43215a89ec
commit
829bdd4b09
@ -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.
|
||||
|
@ -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)
|
||||
|
840
gcc/dwarf2cfi.c
840
gcc/dwarf2cfi.c
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user