#define DWARF2_FRAME_REG_OUT(REGNO, FOR_EH) (REGNO)
#endif
+/* Save the result of dwarf2out_do_frame across PCH. */
+static GTY(()) bool saved_do_cfi_asm = 0;
+
/* Decide whether we want to emit frame unwind information for the current
translation unit. */
we're not going to output frame or unwind info. */
return (write_symbols == DWARF2_DEBUG
|| write_symbols == VMS_AND_DWARF2_DEBUG
- || DWARF2_FRAME_INFO
+ || DWARF2_FRAME_INFO || saved_do_cfi_asm
#ifdef DWARF2_UNWIND_INFO
|| (DWARF2_UNWIND_INFO
&& (flag_unwind_tables
#endif
if (!flag_dwarf2_cfi_asm || !dwarf2out_do_frame ())
return false;
- if (!eh_personality_libfunc)
+ if (saved_do_cfi_asm || !eh_personality_libfunc)
return true;
if (!HAVE_GAS_CFI_PERSONALITY_DIRECTIVE)
return false;
if ((enc & 0x70) != 0 && (enc & 0x70) != DW_EH_PE_pcrel)
return false;
+ saved_do_cfi_asm = true;
return true;
}
#ifdef DWARF2_UNWIND_INFO
static void initial_return_save (rtx);
#endif
-static HOST_WIDE_INT stack_adjust_offset (const_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_call_frame_info (int);
contains. */
static HOST_WIDE_INT
-stack_adjust_offset (const_rtx pattern)
+stack_adjust_offset (const_rtx pattern, HOST_WIDE_INT cur_args_size,
+ HOST_WIDE_INT cur_offset)
{
const_rtx src = SET_SRC (pattern);
const_rtx dest = SET_DEST (pattern);
if (dest == stack_pointer_rtx)
{
- /* (set (reg sp) (plus (reg sp) (const_int))) */
code = GET_CODE (src);
+
+ /* Assume (set (reg sp) (reg whatever)) sets args_size
+ level to 0. */
+ if (code == REG && src != stack_pointer_rtx)
+ {
+ offset = -cur_args_size;
+#ifndef STACK_GROWS_DOWNWARD
+ offset = -offset;
+#endif
+ return offset - cur_offset;
+ }
+
if (! (code == PLUS || code == MINUS)
|| XEXP (src, 0) != stack_pointer_rtx
|| GET_CODE (XEXP (src, 1)) != CONST_INT)
return 0;
+ /* (set (reg sp) (plus (reg sp) (const_int))) */
offset = INTVAL (XEXP (src, 1));
if (code == PLUS)
offset = -offset;
+ return offset;
}
- else if (MEM_P (dest))
+
+ if (MEM_P (src) && !MEM_P (dest))
+ dest = src;
+ if (MEM_P (dest))
{
/* (set (mem (pre_dec (reg sp))) (foo)) */
src = XEXP (dest, 0);
|| sibcall_epilogue_contains (insn))
/* Nothing */;
else if (GET_CODE (PATTERN (insn)) == SET)
- offset = stack_adjust_offset (PATTERN (insn));
+ offset = stack_adjust_offset (PATTERN (insn), cur_args_size, 0);
else if (GET_CODE (PATTERN (insn)) == PARALLEL
|| GET_CODE (PATTERN (insn)) == SEQUENCE)
{
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));
+ offset += stack_adjust_offset (XVECEXP (PATTERN (insn), 0, i),
+ cur_args_size, offset);
}
}
else
rtx elem = XVECEXP (expr, 0, i);
if (GET_CODE (elem) == SET && !RTX_FRAME_RELATED_P (elem))
- offset += stack_adjust_offset (elem);
+ offset += stack_adjust_offset (elem, cur_args_size, offset);
}
}
}
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, i),
+ = compute_barrier_args_size_1 (XVECEXP (body, 0, 0),
cur_args_size, &next);
- cur_args_size
- = compute_barrier_args_size_1 (XVECEXP (body, 0, 0),
- cur_args_size, &next);
}
else
cur_args_size
if (prologue_epilogue_contains (insn) || sibcall_epilogue_contains (insn))
return;
+ /* If INSN is an instruction from target of an annulled branch, the
+ effects are for the target only and so current argument size
+ shouldn't change at all. */
+ if (final_sequence
+ && INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0))
+ && INSN_FROM_TARGET_P (insn))
+ return;
+
/* If only calls can throw, and we have a frame pointer,
save up adjustments until we see the CALL_INSN. */
if (!flag_asynchronous_unwind_tables && cfa.reg != STACK_POINTER_REGNUM)
#endif
}
else if (GET_CODE (PATTERN (insn)) == SET)
- offset = stack_adjust_offset (PATTERN (insn));
+ offset = stack_adjust_offset (PATTERN (insn), args_size, 0);
else if (GET_CODE (PATTERN (insn)) == PARALLEL
|| GET_CODE (PATTERN (insn)) == SEQUENCE)
{
for them. */
for (offset = 0, 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));
+ offset += stack_adjust_offset (XVECEXP (PATTERN (insn), 0, i),
+ args_size, offset);
}
else
return;
{
/* Stack adjustment combining might combine some post-prologue
stack adjustment into a prologue stack adjustment. */
- HOST_WIDE_INT offset = stack_adjust_offset (elem);
+ HOST_WIDE_INT offset = stack_adjust_offset (elem, args_size, 0);
if (offset != 0)
dwarf2out_args_size_adjust (offset, label);
The key is a DECL_UID() which is a unique number identifying each decl. */
static GTY ((param_is (struct die_struct))) htab_t decl_die_table;
+/* A hash table of references to DIE's that describe COMMON blocks.
+ The key is DECL_UID() ^ die_parent. */
+static GTY ((param_is (struct die_struct))) htab_t common_block_die_table;
+
/* Node of the variable location list. */
struct var_loc_node GTY ((chain_next ("%h.next")))
{
static hashval_t decl_die_table_hash (const void *);
static int decl_die_table_eq (const void *, const void *);
static dw_die_ref lookup_decl_die (tree);
+static hashval_t common_block_die_table_hash (const void *);
+static int common_block_die_table_eq (const void *, const void *);
static hashval_t decl_loc_table_hash (const void *);
static int decl_loc_table_eq (const void *, const void *);
static var_loc_list *lookup_decl_loc (const_tree);
distinction between OP_REG and OP_BASEREG. */
if (REGNO (rtl) < FIRST_PSEUDO_REGISTER)
mem_loc_result = based_loc_descr (rtl, 0, VAR_INIT_STATUS_INITIALIZED);
+ else if (stack_realign_drap
+ && crtl->drap_reg
+ && crtl->args.internal_arg_pointer == rtl
+ && REGNO (crtl->drap_reg) < FIRST_PSEUDO_REGISTER)
+ {
+ /* If RTL is internal_arg_pointer, which has been optimized
+ out, use DRAP instead. */
+ mem_loc_result = based_loc_descr (crtl->drap_reg, 0,
+ VAR_INIT_STATUS_INITIALIZED);
+ }
break;
case MEM:
}
+/* Returns a hash value for X (which really is a die_struct). */
+
+static hashval_t
+common_block_die_table_hash (const void *x)
+{
+ const_dw_die_ref d = (const_dw_die_ref) x;
+ return (hashval_t) d->decl_id ^ htab_hash_pointer (d->die_parent);
+}
+
+/* Return nonzero if decl_id and die_parent of die_struct X is the same
+ as decl_id and die_parent of die_struct Y. */
+
+static int
+common_block_die_table_eq (const void *x, const void *y)
+{
+ const_dw_die_ref d = (const_dw_die_ref) x;
+ const_dw_die_ref e = (const_dw_die_ref) y;
+ return d->decl_id == e->decl_id && d->die_parent == e->die_parent;
+}
+
/* Generate a DIE to represent a declared data object. */
static void
tree field;
dw_die_ref com_die;
dw_loc_descr_ref loc;
+ die_node com_die_arg;
var_die = lookup_decl_die (decl);
if (var_die)
if (loc)
{
if (off)
- add_loc_descr (&loc, new_loc_descr (DW_OP_plus_uconst,
+ {
+ /* Optimize the common case. */
+ if (loc->dw_loc_opc == DW_OP_addr
+ && loc->dw_loc_next == NULL
+ && GET_CODE (loc->dw_loc_oprnd1.v.val_addr)
+ == SYMBOL_REF)
+ loc->dw_loc_oprnd1.v.val_addr
+ = plus_constant (loc->dw_loc_oprnd1.v.val_addr, off);
+ else
+ add_loc_descr (&loc,
+ new_loc_descr (DW_OP_plus_uconst,
off, 0));
+ }
add_AT_loc (var_die, DW_AT_location, loc);
remove_AT (var_die, DW_AT_declaration);
}
}
return;
}
+
+ if (common_block_die_table == NULL)
+ common_block_die_table
+ = htab_create_ggc (10, common_block_die_table_hash,
+ common_block_die_table_eq, NULL);
+
field = TREE_OPERAND (DECL_VALUE_EXPR (decl), 0);
- com_die = lookup_decl_die (com_decl);
+ com_die_arg.decl_id = DECL_UID (com_decl);
+ com_die_arg.die_parent = context_die;
+ com_die = (dw_die_ref) htab_find (common_block_die_table, &com_die_arg);
loc = loc_descriptor_from_tree (com_decl);
if (com_die == NULL)
{
const char *cnam
= IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (com_decl));
+ void **slot;
com_die = new_die (DW_TAG_common_block, context_die, decl);
add_name_and_src_coords_attributes (com_die, com_decl);
else if (DECL_EXTERNAL (decl))
add_AT_flag (com_die, DW_AT_declaration, 1);
add_pubname_string (cnam, com_die); /* ??? needed? */
- equate_decl_number_to_die (com_decl, com_die);
+ com_die->decl_id = DECL_UID (com_decl);
+ slot = htab_find_slot (common_block_die_table, com_die, INSERT);
+ *slot = (void *) com_die;
}
else if (get_AT (com_die, DW_AT_location) == NULL && loc)
{
if (loc)
{
if (off)
- add_loc_descr (&loc, new_loc_descr (DW_OP_plus_uconst, off, 0));
+ {
+ /* Optimize the common case. */
+ if (loc->dw_loc_opc == DW_OP_addr
+ && loc->dw_loc_next == NULL
+ && GET_CODE (loc->dw_loc_oprnd1.v.val_addr) == SYMBOL_REF)
+ loc->dw_loc_oprnd1.v.val_addr
+ = plus_constant (loc->dw_loc_oprnd1.v.val_addr, off);
+ else
+ add_loc_descr (&loc, new_loc_descr (DW_OP_plus_uconst,
+ off, 0));
+ }
add_AT_loc (var_die, DW_AT_location, loc);
}
else if (DECL_EXTERNAL (decl))
}
}
+/* For local classes, look if any static member functions were emitted
+ and if so, mark them. */
+
+static void
+prune_unused_types_walk_local_classes (dw_die_ref die)
+{
+ dw_die_ref c;
+
+ if (die->die_mark == 2)
+ return;
+
+ switch (die->die_tag)
+ {
+ case DW_TAG_structure_type:
+ case DW_TAG_union_type:
+ case DW_TAG_class_type:
+ break;
+
+ case DW_TAG_subprogram:
+ if (!get_AT_flag (die, DW_AT_declaration)
+ || die->die_definition != NULL)
+ prune_unused_types_mark (die, 1);
+ return;
+
+ default:
+ return;
+ }
+
+ /* Mark children. */
+ FOR_EACH_CHILD (die, c, prune_unused_types_walk_local_classes (c));
+}
/* Walk the tree DIE and mark types that we actually use. */
{
dw_die_ref c;
- /* Don't do anything if this node is already marked. */
- if (die->die_mark)
+ /* Don't do anything if this node is already marked and
+ children have been marked as well. */
+ if (die->die_mark == 2)
return;
switch (die->die_tag)
{
+ case DW_TAG_structure_type:
+ case DW_TAG_union_type:
+ case DW_TAG_class_type:
+ if (die->die_perennial_p)
+ break;
+
+ for (c = die->die_parent; c; c = c->die_parent)
+ if (c->die_tag == DW_TAG_subprogram)
+ break;
+
+ /* Finding used static member functions inside of classes
+ is needed just for local classes, because for other classes
+ static member function DIEs with DW_AT_specification
+ are emitted outside of the DW_TAG_*_type. If we ever change
+ it, we'd need to call this even for non-local classes. */
+ if (c)
+ prune_unused_types_walk_local_classes (die);
+
+ /* It's a type node --- don't mark it. */
+ return;
+
case DW_TAG_const_type:
case DW_TAG_packed_type:
case DW_TAG_pointer_type:
case DW_TAG_volatile_type:
case DW_TAG_typedef:
case DW_TAG_array_type:
- case DW_TAG_structure_type:
- case DW_TAG_union_type:
- case DW_TAG_class_type:
case DW_TAG_interface_type:
case DW_TAG_friend:
case DW_TAG_variant_part:
break;
}
- die->die_mark = 1;
+ if (die->die_mark == 0)
+ {
+ die->die_mark = 1;
+
+ /* Now, mark any dies referenced from here. */
+ prune_unused_types_walk_attribs (die);
+ }
- /* Now, mark any dies referenced from here. */
- prune_unused_types_walk_attribs (die);
+ die->die_mark = 2;
/* Mark children. */
FOR_EACH_CHILD (die, c, prune_unused_types_walk (c));