2657 lines
70 KiB
C
2657 lines
70 KiB
C
|
/* Dwarf2 Call Frame Information helper routines.
|
|||
|
Copyright (C) 1992, 1993, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
|
|||
|
2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
|
|||
|
Free Software Foundation, Inc.
|
|||
|
|
|||
|
This file is part of GCC.
|
|||
|
|
|||
|
GCC is free software; you can redistribute it and/or modify it under
|
|||
|
the terms of the GNU General Public License as published by the Free
|
|||
|
Software Foundation; either version 3, or (at your option) any later
|
|||
|
version.
|
|||
|
|
|||
|
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
|||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|||
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|||
|
for more details.
|
|||
|
|
|||
|
You should have received a copy of the GNU General Public License
|
|||
|
along with GCC; see the file COPYING3. If not see
|
|||
|
<http://www.gnu.org/licenses/>. */
|
|||
|
|
|||
|
#include "config.h"
|
|||
|
#include "system.h"
|
|||
|
#include "coretypes.h"
|
|||
|
#include "tm.h"
|
|||
|
#include "version.h"
|
|||
|
#include "flags.h"
|
|||
|
#include "rtl.h"
|
|||
|
#include "function.h"
|
|||
|
#include "dwarf2.h"
|
|||
|
#include "dwarf2out.h"
|
|||
|
#include "dwarf2asm.h"
|
|||
|
#include "ggc.h"
|
|||
|
#include "tm_p.h"
|
|||
|
#include "target.h"
|
|||
|
#include "common/common-target.h"
|
|||
|
#include "tree-pass.h"
|
|||
|
|
|||
|
#include "except.h" /* expand_builtin_dwarf_sp_column */
|
|||
|
#include "expr.h" /* init_return_column_size */
|
|||
|
#include "regs.h" /* expand_builtin_init_dwarf_reg_sizes */
|
|||
|
#include "output.h" /* asm_out_file */
|
|||
|
#include "debug.h" /* dwarf2out_do_frame, dwarf2out_do_cfi_asm */
|
|||
|
|
|||
|
|
|||
|
/* ??? Poison these here until it can be done generically. They've been
|
|||
|
totally replaced in this file; make sure it stays that way. */
|
|||
|
#undef DWARF2_UNWIND_INFO
|
|||
|
#undef DWARF2_FRAME_INFO
|
|||
|
#if (GCC_VERSION >= 3000)
|
|||
|
#pragma GCC poison DWARF2_UNWIND_INFO DWARF2_FRAME_INFO
|
|||
|
#endif
|
|||
|
|
|||
|
#ifndef INCOMING_RETURN_ADDR_RTX
|
|||
|
#define INCOMING_RETURN_ADDR_RTX (gcc_unreachable (), NULL_RTX)
|
|||
|
#endif
|
|||
|
|
|||
|
/* The size of the target's pointer type. */
|
|||
|
#ifndef PTR_SIZE
|
|||
|
#define PTR_SIZE (POINTER_SIZE / BITS_PER_UNIT)
|
|||
|
#endif
|
|||
|
|
|||
|
/* Maximum size (in bytes) of an artificially generated label. */
|
|||
|
#define MAX_ARTIFICIAL_LABEL_BYTES 30
|
|||
|
|
|||
|
/* The size of addresses as they appear in the Dwarf 2 data.
|
|||
|
Some architectures use word addresses to refer to code locations,
|
|||
|
but Dwarf 2 info always uses byte addresses. On such machines,
|
|||
|
Dwarf 2 addresses need to be larger than the architecture's
|
|||
|
pointers. */
|
|||
|
#ifndef DWARF2_ADDR_SIZE
|
|||
|
#define DWARF2_ADDR_SIZE (POINTER_SIZE / BITS_PER_UNIT)
|
|||
|
#endif
|
|||
|
|
|||
|
/* The size in bytes of a DWARF field indicating an offset or length
|
|||
|
relative to a debug info section, specified to be 4 bytes in the
|
|||
|
DWARF-2 specification. The SGI/MIPS ABI defines it to be the same
|
|||
|
as PTR_SIZE. */
|
|||
|
|
|||
|
#ifndef DWARF_OFFSET_SIZE
|
|||
|
#define DWARF_OFFSET_SIZE 4
|
|||
|
#endif
|
|||
|
|
|||
|
/* According to the (draft) DWARF 3 specification, the initial length
|
|||
|
should either be 4 or 12 bytes. When it's 12 bytes, the first 4
|
|||
|
bytes are 0xffffffff, followed by the length stored in the next 8
|
|||
|
bytes.
|
|||
|
|
|||
|
However, the SGI/MIPS ABI uses an initial length which is equal to
|
|||
|
DWARF_OFFSET_SIZE. It is defined (elsewhere) accordingly. */
|
|||
|
|
|||
|
#ifndef DWARF_INITIAL_LENGTH_SIZE
|
|||
|
#define DWARF_INITIAL_LENGTH_SIZE (DWARF_OFFSET_SIZE == 4 ? 4 : 12)
|
|||
|
#endif
|
|||
|
|
|||
|
/* Round SIZE up to the nearest BOUNDARY. */
|
|||
|
#define DWARF_ROUND(SIZE,BOUNDARY) \
|
|||
|
((((SIZE) + (BOUNDARY) - 1) / (BOUNDARY)) * (BOUNDARY))
|
|||
|
|
|||
|
/* Offsets recorded in opcodes are a multiple of this alignment factor. */
|
|||
|
#ifndef DWARF_CIE_DATA_ALIGNMENT
|
|||
|
#ifdef STACK_GROWS_DOWNWARD
|
|||
|
#define DWARF_CIE_DATA_ALIGNMENT (-((int) UNITS_PER_WORD))
|
|||
|
#else
|
|||
|
#define DWARF_CIE_DATA_ALIGNMENT ((int) UNITS_PER_WORD)
|
|||
|
#endif
|
|||
|
#endif
|
|||
|
|
|||
|
/* CIE identifier. */
|
|||
|
#if HOST_BITS_PER_WIDE_INT >= 64
|
|||
|
#define DWARF_CIE_ID \
|
|||
|
(unsigned HOST_WIDE_INT) (DWARF_OFFSET_SIZE == 4 ? DW_CIE_ID : DW64_CIE_ID)
|
|||
|
#else
|
|||
|
#define DWARF_CIE_ID DW_CIE_ID
|
|||
|
#endif
|
|||
|
|
|||
|
/* The DWARF 2 CFA column which tracks the return address. Normally this
|
|||
|
is the column for PC, or the first column after all of the hard
|
|||
|
registers. */
|
|||
|
#ifndef DWARF_FRAME_RETURN_COLUMN
|
|||
|
#ifdef PC_REGNUM
|
|||
|
#define DWARF_FRAME_RETURN_COLUMN DWARF_FRAME_REGNUM (PC_REGNUM)
|
|||
|
#else
|
|||
|
#define DWARF_FRAME_RETURN_COLUMN DWARF_FRAME_REGISTERS
|
|||
|
#endif
|
|||
|
#endif
|
|||
|
|
|||
|
/* The mapping from gcc register number to DWARF 2 CFA column number. By
|
|||
|
default, we just provide columns for all registers. */
|
|||
|
#ifndef DWARF_FRAME_REGNUM
|
|||
|
#define DWARF_FRAME_REGNUM(REG) DBX_REGISTER_NUMBER (REG)
|
|||
|
#endif
|
|||
|
|
|||
|
/* Map register numbers held in the call frame info that gcc has
|
|||
|
collected using DWARF_FRAME_REGNUM to those that should be output in
|
|||
|
.debug_frame and .eh_frame. */
|
|||
|
#ifndef DWARF2_FRAME_REG_OUT
|
|||
|
#define DWARF2_FRAME_REG_OUT(REGNO, FOR_EH) (REGNO)
|
|||
|
#endif
|
|||
|
|
|||
|
/* A vector of call frame insns for the CIE. */
|
|||
|
cfi_vec cie_cfi_vec;
|
|||
|
|
|||
|
static GTY(()) unsigned long dwarf2out_cfi_label_num;
|
|||
|
|
|||
|
|
|||
|
/* Hook used by __throw. */
|
|||
|
|
|||
|
rtx
|
|||
|
expand_builtin_dwarf_sp_column (void)
|
|||
|
{
|
|||
|
unsigned int dwarf_regnum = DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM);
|
|||
|
return GEN_INT (DWARF2_FRAME_REG_OUT (dwarf_regnum, 1));
|
|||
|
}
|
|||
|
|
|||
|
/* MEM is a memory reference for the register size table, each element of
|
|||
|
which has mode MODE. Initialize column C as a return address column. */
|
|||
|
|
|||
|
static void
|
|||
|
init_return_column_size (enum machine_mode mode, rtx mem, unsigned int c)
|
|||
|
{
|
|||
|
HOST_WIDE_INT offset = c * GET_MODE_SIZE (mode);
|
|||
|
HOST_WIDE_INT size = GET_MODE_SIZE (Pmode);
|
|||
|
emit_move_insn (adjust_address (mem, mode, offset), GEN_INT (size));
|
|||
|
}
|
|||
|
|
|||
|
/* Generate code to initialize the register size table. */
|
|||
|
|
|||
|
void
|
|||
|
expand_builtin_init_dwarf_reg_sizes (tree address)
|
|||
|
{
|
|||
|
unsigned int i;
|
|||
|
enum machine_mode mode = TYPE_MODE (char_type_node);
|
|||
|
rtx addr = expand_normal (address);
|
|||
|
rtx mem = gen_rtx_MEM (BLKmode, addr);
|
|||
|
bool wrote_return_column = false;
|
|||
|
|
|||
|
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
|||
|
{
|
|||
|
int rnum = DWARF2_FRAME_REG_OUT (DWARF_FRAME_REGNUM (i), 1);
|
|||
|
|
|||
|
if (rnum < DWARF_FRAME_REGISTERS)
|
|||
|
{
|
|||
|
HOST_WIDE_INT offset = rnum * GET_MODE_SIZE (mode);
|
|||
|
enum machine_mode save_mode = reg_raw_mode[i];
|
|||
|
HOST_WIDE_INT size;
|
|||
|
|
|||
|
if (HARD_REGNO_CALL_PART_CLOBBERED (i, save_mode))
|
|||
|
save_mode = choose_hard_reg_mode (i, 1, true);
|
|||
|
if (DWARF_FRAME_REGNUM (i) == DWARF_FRAME_RETURN_COLUMN)
|
|||
|
{
|
|||
|
if (save_mode == VOIDmode)
|
|||
|
continue;
|
|||
|
wrote_return_column = true;
|
|||
|
}
|
|||
|
size = GET_MODE_SIZE (save_mode);
|
|||
|
if (offset < 0)
|
|||
|
continue;
|
|||
|
|
|||
|
emit_move_insn (adjust_address (mem, mode, offset),
|
|||
|
gen_int_mode (size, mode));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!wrote_return_column)
|
|||
|
init_return_column_size (mode, mem, DWARF_FRAME_RETURN_COLUMN);
|
|||
|
|
|||
|
#ifdef DWARF_ALT_FRAME_RETURN_COLUMN
|
|||
|
init_return_column_size (mode, mem, DWARF_ALT_FRAME_RETURN_COLUMN);
|
|||
|
#endif
|
|||
|
|
|||
|
targetm.init_dwarf_reg_sizes_extra (address);
|
|||
|
}
|
|||
|
|
|||
|
/* Divide OFF by DWARF_CIE_DATA_ALIGNMENT, asserting no remainder. */
|
|||
|
|
|||
|
static inline HOST_WIDE_INT
|
|||
|
div_data_align (HOST_WIDE_INT off)
|
|||
|
{
|
|||
|
HOST_WIDE_INT r = off / DWARF_CIE_DATA_ALIGNMENT;
|
|||
|
gcc_assert (r * DWARF_CIE_DATA_ALIGNMENT == off);
|
|||
|
return r;
|
|||
|
}
|
|||
|
|
|||
|
/* Return true if we need a signed version of a given opcode
|
|||
|
(e.g. DW_CFA_offset_extended_sf vs DW_CFA_offset_extended). */
|
|||
|
|
|||
|
static inline bool
|
|||
|
need_data_align_sf_opcode (HOST_WIDE_INT off)
|
|||
|
{
|
|||
|
return DWARF_CIE_DATA_ALIGNMENT < 0 ? off > 0 : off < 0;
|
|||
|
}
|
|||
|
|
|||
|
/* Return a pointer to a newly allocated Call Frame Instruction. */
|
|||
|
|
|||
|
static inline dw_cfi_ref
|
|||
|
new_cfi (void)
|
|||
|
{
|
|||
|
dw_cfi_ref cfi = ggc_alloc_dw_cfi_node ();
|
|||
|
|
|||
|
cfi->dw_cfi_oprnd1.dw_cfi_reg_num = 0;
|
|||
|
cfi->dw_cfi_oprnd2.dw_cfi_reg_num = 0;
|
|||
|
|
|||
|
return cfi;
|
|||
|
}
|
|||
|
|
|||
|
/* Add a Call Frame Instruction to list of instructions. */
|
|||
|
|
|||
|
static inline void
|
|||
|
add_cfi (cfi_vec *vec, dw_cfi_ref cfi)
|
|||
|
{
|
|||
|
dw_fde_ref fde = current_fde ();
|
|||
|
|
|||
|
/* When DRAP is used, CFA is defined with an expression. Redefine
|
|||
|
CFA may lead to a different CFA value. */
|
|||
|
/* ??? Of course, this heuristic fails when we're annotating epilogues,
|
|||
|
because of course we'll always want to redefine the CFA back to the
|
|||
|
stack pointer on the way out. Where should we move this check? */
|
|||
|
if (0 && fde && fde->drap_reg != INVALID_REGNUM)
|
|||
|
switch (cfi->dw_cfi_opc)
|
|||
|
{
|
|||
|
case DW_CFA_def_cfa_register:
|
|||
|
case DW_CFA_def_cfa_offset:
|
|||
|
case DW_CFA_def_cfa_offset_sf:
|
|||
|
case DW_CFA_def_cfa:
|
|||
|
case DW_CFA_def_cfa_sf:
|
|||
|
gcc_unreachable ();
|
|||
|
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
VEC_safe_push (dw_cfi_ref, gc, *vec, cfi);
|
|||
|
}
|
|||
|
|
|||
|
/* Generate a new label for the CFI info to refer to. FORCE is true
|
|||
|
if a label needs to be output even when using .cfi_* directives. */
|
|||
|
|
|||
|
static char *
|
|||
|
dwarf2out_cfi_label (bool force)
|
|||
|
{
|
|||
|
static char label[20];
|
|||
|
|
|||
|
if (!force && dwarf2out_do_cfi_asm ())
|
|||
|
{
|
|||
|
/* In this case, we will be emitting the asm directive instead of
|
|||
|
the label, so just return a placeholder to keep the rest of the
|
|||
|
interfaces happy. */
|
|||
|
strcpy (label, "<do not output>");
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
int num = dwarf2out_cfi_label_num++;
|
|||
|
ASM_GENERATE_INTERNAL_LABEL (label, "LCFI", num);
|
|||
|
ASM_OUTPUT_DEBUG_LABEL (asm_out_file, "LCFI", num);
|
|||
|
}
|
|||
|
|
|||
|
return label;
|
|||
|
}
|
|||
|
|
|||
|
/* 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;
|
|||
|
|
|||
|
/* 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 (const char *label, dw_cfi_ref cfi)
|
|||
|
{
|
|||
|
cfi_vec *vec;
|
|||
|
|
|||
|
if (cie_cfi_vec == NULL)
|
|||
|
cie_cfi_vec = VEC_alloc (dw_cfi_ref, gc, 20);
|
|||
|
|
|||
|
vec = &cie_cfi_vec;
|
|||
|
|
|||
|
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 (label, cfi_remember);
|
|||
|
}
|
|||
|
|
|||
|
if (dwarf2out_do_cfi_asm ())
|
|||
|
{
|
|||
|
if (label)
|
|||
|
{
|
|||
|
dw_fde_ref fde = current_fde ();
|
|||
|
|
|||
|
gcc_assert (fde != NULL);
|
|||
|
|
|||
|
/* We still have to add the cfi to the list so that lookup_cfa
|
|||
|
works later on. When -g2 and above we even need to force
|
|||
|
emitting of CFI labels and add to list a DW_CFA_set_loc for
|
|||
|
convert_cfa_to_fb_loc_list purposes. If we're generating
|
|||
|
DWARF3 output we use DW_OP_call_frame_cfa and so don't use
|
|||
|
convert_cfa_to_fb_loc_list. */
|
|||
|
if (dwarf_version == 2
|
|||
|
&& debug_info_level > DINFO_LEVEL_TERSE
|
|||
|
&& (write_symbols == DWARF2_DEBUG
|
|||
|
|| write_symbols == VMS_AND_DWARF2_DEBUG))
|
|||
|
{
|
|||
|
switch (cfi->dw_cfi_opc)
|
|||
|
{
|
|||
|
case DW_CFA_def_cfa_offset:
|
|||
|
case DW_CFA_def_cfa_offset_sf:
|
|||
|
case DW_CFA_def_cfa_register:
|
|||
|
case DW_CFA_def_cfa:
|
|||
|
case DW_CFA_def_cfa_sf:
|
|||
|
case DW_CFA_def_cfa_expression:
|
|||
|
case DW_CFA_restore_state:
|
|||
|
if (*label == 0 || strcmp (label, "<do not output>") == 0)
|
|||
|
label = dwarf2out_cfi_label (true);
|
|||
|
|
|||
|
if (fde->dw_fde_current_label == NULL
|
|||
|
|| strcmp (label, fde->dw_fde_current_label) != 0)
|
|||
|
{
|
|||
|
dw_cfi_ref xcfi;
|
|||
|
|
|||
|
label = xstrdup (label);
|
|||
|
|
|||
|
/* Set the location counter to the new label. */
|
|||
|
xcfi = new_cfi ();
|
|||
|
/* It doesn't metter whether DW_CFA_set_loc
|
|||
|
or DW_CFA_advance_loc4 is added here, those aren't
|
|||
|
emitted into assembly, only looked up by
|
|||
|
convert_cfa_to_fb_loc_list. */
|
|||
|
xcfi->dw_cfi_opc = DW_CFA_set_loc;
|
|||
|
xcfi->dw_cfi_oprnd1.dw_cfi_addr = label;
|
|||
|
add_cfi (&fde->dw_fde_cfi, xcfi);
|
|||
|
fde->dw_fde_current_label = label;
|
|||
|
}
|
|||
|
break;
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
output_cfi_directive (cfi);
|
|||
|
|
|||
|
vec = &fde->dw_fde_cfi;
|
|||
|
any_cfis_emitted = true;
|
|||
|
}
|
|||
|
/* ??? If this is a CFI for the CIE, we don't emit. This
|
|||
|
assumes that the standard CIE contents that the assembler
|
|||
|
uses matches the standard CIE contents that the compiler
|
|||
|
uses. This is probably a bad assumption. I'm not quite
|
|||
|
sure how to address this for now. */
|
|||
|
}
|
|||
|
else if (label)
|
|||
|
{
|
|||
|
dw_fde_ref fde = current_fde ();
|
|||
|
|
|||
|
gcc_assert (fde != NULL);
|
|||
|
|
|||
|
if (*label == 0)
|
|||
|
label = dwarf2out_cfi_label (false);
|
|||
|
|
|||
|
if (fde->dw_fde_current_label == NULL
|
|||
|
|| strcmp (label, fde->dw_fde_current_label) != 0)
|
|||
|
{
|
|||
|
dw_cfi_ref xcfi;
|
|||
|
|
|||
|
label = xstrdup (label);
|
|||
|
|
|||
|
/* Set the location counter to the new label. */
|
|||
|
xcfi = new_cfi ();
|
|||
|
/* If we have a current label, advance from there, otherwise
|
|||
|
set the location directly using set_loc. */
|
|||
|
xcfi->dw_cfi_opc = fde->dw_fde_current_label
|
|||
|
? DW_CFA_advance_loc4
|
|||
|
: DW_CFA_set_loc;
|
|||
|
xcfi->dw_cfi_oprnd1.dw_cfi_addr = label;
|
|||
|
add_cfi (&fde->dw_fde_cfi, xcfi);
|
|||
|
|
|||
|
fde->dw_fde_current_label = label;
|
|||
|
}
|
|||
|
|
|||
|
vec = &fde->dw_fde_cfi;
|
|||
|
any_cfis_emitted = true;
|
|||
|
}
|
|||
|
|
|||
|
add_cfi (vec, cfi);
|
|||
|
}
|
|||
|
|
|||
|
/* This function fills in aa dw_cfa_location structure from a dwarf location
|
|||
|
descriptor sequence. */
|
|||
|
|
|||
|
static void
|
|||
|
get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_struct *loc)
|
|||
|
{
|
|||
|
struct dw_loc_descr_struct *ptr;
|
|||
|
cfa->offset = 0;
|
|||
|
cfa->base_offset = 0;
|
|||
|
cfa->indirect = 0;
|
|||
|
cfa->reg = -1;
|
|||
|
|
|||
|
for (ptr = loc; ptr != NULL; ptr = ptr->dw_loc_next)
|
|||
|
{
|
|||
|
enum dwarf_location_atom op = ptr->dw_loc_opc;
|
|||
|
|
|||
|
switch (op)
|
|||
|
{
|
|||
|
case DW_OP_reg0:
|
|||
|
case DW_OP_reg1:
|
|||
|
case DW_OP_reg2:
|
|||
|
case DW_OP_reg3:
|
|||
|
case DW_OP_reg4:
|
|||
|
case DW_OP_reg5:
|
|||
|
case DW_OP_reg6:
|
|||
|
case DW_OP_reg7:
|
|||
|
case DW_OP_reg8:
|
|||
|
case DW_OP_reg9:
|
|||
|
case DW_OP_reg10:
|
|||
|
case DW_OP_reg11:
|
|||
|
case DW_OP_reg12:
|
|||
|
case DW_OP_reg13:
|
|||
|
case DW_OP_reg14:
|
|||
|
case DW_OP_reg15:
|
|||
|
case DW_OP_reg16:
|
|||
|
case DW_OP_reg17:
|
|||
|
case DW_OP_reg18:
|
|||
|
case DW_OP_reg19:
|
|||
|
case DW_OP_reg20:
|
|||
|
case DW_OP_reg21:
|
|||
|
case DW_OP_reg22:
|
|||
|
case DW_OP_reg23:
|
|||
|
case DW_OP_reg24:
|
|||
|
case DW_OP_reg25:
|
|||
|
case DW_OP_reg26:
|
|||
|
case DW_OP_reg27:
|
|||
|
case DW_OP_reg28:
|
|||
|
case DW_OP_reg29:
|
|||
|
case DW_OP_reg30:
|
|||
|
case DW_OP_reg31:
|
|||
|
cfa->reg = op - DW_OP_reg0;
|
|||
|
break;
|
|||
|
case DW_OP_regx:
|
|||
|
cfa->reg = ptr->dw_loc_oprnd1.v.val_int;
|
|||
|
break;
|
|||
|
case DW_OP_breg0:
|
|||
|
case DW_OP_breg1:
|
|||
|
case DW_OP_breg2:
|
|||
|
case DW_OP_breg3:
|
|||
|
case DW_OP_breg4:
|
|||
|
case DW_OP_breg5:
|
|||
|
case DW_OP_breg6:
|
|||
|
case DW_OP_breg7:
|
|||
|
case DW_OP_breg8:
|
|||
|
case DW_OP_breg9:
|
|||
|
case DW_OP_breg10:
|
|||
|
case DW_OP_breg11:
|
|||
|
case DW_OP_breg12:
|
|||
|
case DW_OP_breg13:
|
|||
|
case DW_OP_breg14:
|
|||
|
case DW_OP_breg15:
|
|||
|
case DW_OP_breg16:
|
|||
|
case DW_OP_breg17:
|
|||
|
case DW_OP_breg18:
|
|||
|
case DW_OP_breg19:
|
|||
|
case DW_OP_breg20:
|
|||
|
case DW_OP_breg21:
|
|||
|
case DW_OP_breg22:
|
|||
|
case DW_OP_breg23:
|
|||
|
case DW_OP_breg24:
|
|||
|
case DW_OP_breg25:
|
|||
|
case DW_OP_breg26:
|
|||
|
case DW_OP_breg27:
|
|||
|
case DW_OP_breg28:
|
|||
|
case DW_OP_breg29:
|
|||
|
case DW_OP_breg30:
|
|||
|
case DW_OP_breg31:
|
|||
|
cfa->reg = op - DW_OP_breg0;
|
|||
|
cfa->base_offset = ptr->dw_loc_oprnd1.v.val_int;
|
|||
|
break;
|
|||
|
case DW_OP_bregx:
|
|||
|
cfa->reg = ptr->dw_loc_oprnd1.v.val_int;
|
|||
|
cfa->base_offset = ptr->dw_loc_oprnd2.v.val_int;
|
|||
|
break;
|
|||
|
case DW_OP_deref:
|
|||
|
cfa->indirect = 1;
|
|||
|
break;
|
|||
|
case DW_OP_plus_uconst:
|
|||
|
cfa->offset = ptr->dw_loc_oprnd1.v.val_unsigned;
|
|||
|
break;
|
|||
|
default:
|
|||
|
gcc_unreachable ();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Subroutine of lookup_cfa. */
|
|||
|
|
|||
|
void
|
|||
|
lookup_cfa_1 (dw_cfi_ref cfi, dw_cfa_location *loc, dw_cfa_location *remember)
|
|||
|
{
|
|||
|
switch (cfi->dw_cfi_opc)
|
|||
|
{
|
|||
|
case DW_CFA_def_cfa_offset:
|
|||
|
case DW_CFA_def_cfa_offset_sf:
|
|||
|
loc->offset = cfi->dw_cfi_oprnd1.dw_cfi_offset;
|
|||
|
break;
|
|||
|
case DW_CFA_def_cfa_register:
|
|||
|
loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num;
|
|||
|
break;
|
|||
|
case DW_CFA_def_cfa:
|
|||
|
case DW_CFA_def_cfa_sf:
|
|||
|
loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num;
|
|||
|
loc->offset = cfi->dw_cfi_oprnd2.dw_cfi_offset;
|
|||
|
break;
|
|||
|
case DW_CFA_def_cfa_expression:
|
|||
|
get_cfa_from_loc_descr (loc, cfi->dw_cfi_oprnd1.dw_cfi_loc);
|
|||
|
break;
|
|||
|
|
|||
|
case DW_CFA_remember_state:
|
|||
|
gcc_assert (!remember->in_use);
|
|||
|
*remember = *loc;
|
|||
|
remember->in_use = 1;
|
|||
|
break;
|
|||
|
case DW_CFA_restore_state:
|
|||
|
gcc_assert (remember->in_use);
|
|||
|
*loc = *remember;
|
|||
|
remember->in_use = 0;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Find the previous value for the CFA. */
|
|||
|
|
|||
|
static void
|
|||
|
lookup_cfa (dw_cfa_location *loc)
|
|||
|
{
|
|||
|
int ix;
|
|||
|
dw_cfi_ref cfi;
|
|||
|
dw_fde_ref fde;
|
|||
|
dw_cfa_location remember;
|
|||
|
|
|||
|
memset (loc, 0, sizeof (*loc));
|
|||
|
loc->reg = INVALID_REGNUM;
|
|||
|
remember = *loc;
|
|||
|
|
|||
|
FOR_EACH_VEC_ELT (dw_cfi_ref, cie_cfi_vec, ix, cfi)
|
|||
|
lookup_cfa_1 (cfi, loc, &remember);
|
|||
|
|
|||
|
fde = current_fde ();
|
|||
|
if (fde)
|
|||
|
FOR_EACH_VEC_ELT (dw_cfi_ref, fde->dw_fde_cfi, ix, cfi)
|
|||
|
lookup_cfa_1 (cfi, loc, &remember);
|
|||
|
}
|
|||
|
|
|||
|
/* The current rule for calculating the DWARF2 canonical frame address. */
|
|||
|
static dw_cfa_location cfa;
|
|||
|
|
|||
|
/* The register used for saving registers to the stack, and its offset
|
|||
|
from the CFA. */
|
|||
|
static dw_cfa_location cfa_store;
|
|||
|
|
|||
|
/* The current save location around an epilogue. */
|
|||
|
static dw_cfa_location cfa_remember;
|
|||
|
|
|||
|
/* The running total of the size of arguments pushed onto the stack. */
|
|||
|
static HOST_WIDE_INT args_size;
|
|||
|
|
|||
|
/* The last args_size we actually output. */
|
|||
|
static HOST_WIDE_INT old_args_size;
|
|||
|
|
|||
|
/* Determine if two dw_cfa_location structures define the same data. */
|
|||
|
|
|||
|
bool
|
|||
|
cfa_equal_p (const dw_cfa_location *loc1, const dw_cfa_location *loc2)
|
|||
|
{
|
|||
|
return (loc1->reg == loc2->reg
|
|||
|
&& loc1->offset == loc2->offset
|
|||
|
&& loc1->indirect == loc2->indirect
|
|||
|
&& (loc1->indirect == 0
|
|||
|
|| loc1->base_offset == loc2->base_offset));
|
|||
|
}
|
|||
|
|
|||
|
/* This routine does the actual work. The CFA is now calculated from
|
|||
|
the dw_cfa_location structure. */
|
|||
|
|
|||
|
static void
|
|||
|
def_cfa_1 (const char *label, dw_cfa_location *loc_p)
|
|||
|
{
|
|||
|
dw_cfi_ref cfi;
|
|||
|
dw_cfa_location old_cfa, loc;
|
|||
|
|
|||
|
cfa = *loc_p;
|
|||
|
loc = *loc_p;
|
|||
|
|
|||
|
if (cfa_store.reg == loc.reg && loc.indirect == 0)
|
|||
|
cfa_store.offset = loc.offset;
|
|||
|
|
|||
|
loc.reg = DWARF_FRAME_REGNUM (loc.reg);
|
|||
|
lookup_cfa (&old_cfa);
|
|||
|
|
|||
|
/* If nothing changed, no need to issue any call frame instructions. */
|
|||
|
if (cfa_equal_p (&loc, &old_cfa))
|
|||
|
return;
|
|||
|
|
|||
|
cfi = new_cfi ();
|
|||
|
|
|||
|
if (loc.reg == old_cfa.reg && !loc.indirect && !old_cfa.indirect)
|
|||
|
{
|
|||
|
/* Construct a "DW_CFA_def_cfa_offset <offset>" instruction, indicating
|
|||
|
the CFA register did not change but the offset did. The data
|
|||
|
factoring for DW_CFA_def_cfa_offset_sf happens in output_cfi, or
|
|||
|
in the assembler via the .cfi_def_cfa_offset directive. */
|
|||
|
if (loc.offset < 0)
|
|||
|
cfi->dw_cfi_opc = DW_CFA_def_cfa_offset_sf;
|
|||
|
else
|
|||
|
cfi->dw_cfi_opc = DW_CFA_def_cfa_offset;
|
|||
|
cfi->dw_cfi_oprnd1.dw_cfi_offset = loc.offset;
|
|||
|
}
|
|||
|
|
|||
|
#ifndef MIPS_DEBUGGING_INFO /* SGI dbx thinks this means no offset. */
|
|||
|
else if (loc.offset == old_cfa.offset
|
|||
|
&& old_cfa.reg != INVALID_REGNUM
|
|||
|
&& !loc.indirect
|
|||
|
&& !old_cfa.indirect)
|
|||
|
{
|
|||
|
/* Construct a "DW_CFA_def_cfa_register <register>" instruction,
|
|||
|
indicating the CFA register has changed to <register> but the
|
|||
|
offset has not changed. */
|
|||
|
cfi->dw_cfi_opc = DW_CFA_def_cfa_register;
|
|||
|
cfi->dw_cfi_oprnd1.dw_cfi_reg_num = loc.reg;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
else if (loc.indirect == 0)
|
|||
|
{
|
|||
|
/* Construct a "DW_CFA_def_cfa <register> <offset>" instruction,
|
|||
|
indicating the CFA register has changed to <register> with
|
|||
|
the specified offset. The data factoring for DW_CFA_def_cfa_sf
|
|||
|
happens in output_cfi, or in the assembler via the .cfi_def_cfa
|
|||
|
directive. */
|
|||
|
if (loc.offset < 0)
|
|||
|
cfi->dw_cfi_opc = DW_CFA_def_cfa_sf;
|
|||
|
else
|
|||
|
cfi->dw_cfi_opc = DW_CFA_def_cfa;
|
|||
|
cfi->dw_cfi_oprnd1.dw_cfi_reg_num = loc.reg;
|
|||
|
cfi->dw_cfi_oprnd2.dw_cfi_offset = loc.offset;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* Construct a DW_CFA_def_cfa_expression instruction to
|
|||
|
calculate the CFA using a full location expression since no
|
|||
|
register-offset pair is available. */
|
|||
|
struct dw_loc_descr_struct *loc_list;
|
|||
|
|
|||
|
cfi->dw_cfi_opc = DW_CFA_def_cfa_expression;
|
|||
|
loc_list = build_cfa_loc (&loc, 0);
|
|||
|
cfi->dw_cfi_oprnd1.dw_cfi_loc = loc_list;
|
|||
|
}
|
|||
|
|
|||
|
add_fde_cfi (label, cfi);
|
|||
|
}
|
|||
|
|
|||
|
/* Add the CFI for saving a register. REG is the CFA column number.
|
|||
|
LABEL is passed to add_fde_cfi.
|
|||
|
If SREG is -1, the register is saved at OFFSET from the CFA;
|
|||
|
otherwise it is saved in SREG. */
|
|||
|
|
|||
|
static void
|
|||
|
reg_save (const char *label, unsigned int reg, unsigned int sreg,
|
|||
|
HOST_WIDE_INT offset)
|
|||
|
{
|
|||
|
dw_cfi_ref cfi = new_cfi ();
|
|||
|
dw_fde_ref fde = current_fde ();
|
|||
|
|
|||
|
cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg;
|
|||
|
|
|||
|
/* When stack is aligned, store REG using DW_CFA_expression with FP. */
|
|||
|
if (fde
|
|||
|
&& fde->stack_realign
|
|||
|
&& sreg == INVALID_REGNUM)
|
|||
|
{
|
|||
|
cfi->dw_cfi_opc = DW_CFA_expression;
|
|||
|
cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg;
|
|||
|
cfi->dw_cfi_oprnd2.dw_cfi_loc
|
|||
|
= build_cfa_aligned_loc (&cfa, offset, fde->stack_realignment);
|
|||
|
}
|
|||
|
else if (sreg == INVALID_REGNUM)
|
|||
|
{
|
|||
|
if (need_data_align_sf_opcode (offset))
|
|||
|
cfi->dw_cfi_opc = DW_CFA_offset_extended_sf;
|
|||
|
else if (reg & ~0x3f)
|
|||
|
cfi->dw_cfi_opc = DW_CFA_offset_extended;
|
|||
|
else
|
|||
|
cfi->dw_cfi_opc = DW_CFA_offset;
|
|||
|
cfi->dw_cfi_oprnd2.dw_cfi_offset = offset;
|
|||
|
}
|
|||
|
else if (sreg == reg)
|
|||
|
cfi->dw_cfi_opc = DW_CFA_same_value;
|
|||
|
else
|
|||
|
{
|
|||
|
cfi->dw_cfi_opc = DW_CFA_register;
|
|||
|
cfi->dw_cfi_oprnd2.dw_cfi_reg_num = sreg;
|
|||
|
}
|
|||
|
|
|||
|
add_fde_cfi (label, cfi);
|
|||
|
}
|
|||
|
|
|||
|
/* Record the initial position of the return address. RTL is
|
|||
|
INCOMING_RETURN_ADDR_RTX. */
|
|||
|
|
|||
|
static void
|
|||
|
initial_return_save (rtx rtl)
|
|||
|
{
|
|||
|
unsigned int reg = INVALID_REGNUM;
|
|||
|
HOST_WIDE_INT offset = 0;
|
|||
|
|
|||
|
switch (GET_CODE (rtl))
|
|||
|
{
|
|||
|
case REG:
|
|||
|
/* RA is in a register. */
|
|||
|
reg = DWARF_FRAME_REGNUM (REGNO (rtl));
|
|||
|
break;
|
|||
|
|
|||
|
case MEM:
|
|||
|
/* RA is on the stack. */
|
|||
|
rtl = XEXP (rtl, 0);
|
|||
|
switch (GET_CODE (rtl))
|
|||
|
{
|
|||
|
case REG:
|
|||
|
gcc_assert (REGNO (rtl) == STACK_POINTER_REGNUM);
|
|||
|
offset = 0;
|
|||
|
break;
|
|||
|
|
|||
|
case PLUS:
|
|||
|
gcc_assert (REGNO (XEXP (rtl, 0)) == STACK_POINTER_REGNUM);
|
|||
|
offset = INTVAL (XEXP (rtl, 1));
|
|||
|
break;
|
|||
|
|
|||
|
case MINUS:
|
|||
|
gcc_assert (REGNO (XEXP (rtl, 0)) == STACK_POINTER_REGNUM);
|
|||
|
offset = -INTVAL (XEXP (rtl, 1));
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
gcc_unreachable ();
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case PLUS:
|
|||
|
/* The return address is at some offset from any value we can
|
|||
|
actually load. For instance, on the SPARC it is in %i7+8. Just
|
|||
|
ignore the offset for now; it doesn't matter for unwinding frames. */
|
|||
|
gcc_assert (CONST_INT_P (XEXP (rtl, 1)));
|
|||
|
initial_return_save (XEXP (rtl, 0));
|
|||
|
return;
|
|||
|
|
|||
|
default:
|
|||
|
gcc_unreachable ();
|
|||
|
}
|
|||
|
|
|||
|
if (reg != DWARF_FRAME_RETURN_COLUMN)
|
|||
|
reg_save (NULL, DWARF_FRAME_RETURN_COLUMN, reg, offset - cfa.offset);
|
|||
|
}
|
|||
|
|
|||
|
/* Given a SET, calculate the amount of stack adjustment it
|
|||
|
contains. */
|
|||
|
|
|||
|
static HOST_WIDE_INT
|
|||
|
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);
|
|||
|
HOST_WIDE_INT offset = 0;
|
|||
|
enum rtx_code code;
|
|||
|
|
|||
|
if (dest == stack_pointer_rtx)
|
|||
|
{
|
|||
|
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
|
|||
|
|| !CONST_INT_P (XEXP (src, 1)))
|
|||
|
return 0;
|
|||
|
|
|||
|
/* (set (reg sp) (plus (reg sp) (const_int))) */
|
|||
|
offset = INTVAL (XEXP (src, 1));
|
|||
|
if (code == PLUS)
|
|||
|
offset = -offset;
|
|||
|
return offset;
|
|||
|
}
|
|||
|
|
|||
|
if (MEM_P (src) && !MEM_P (dest))
|
|||
|
dest = src;
|
|||
|
if (MEM_P (dest))
|
|||
|
{
|
|||
|
/* (set (mem (pre_dec (reg sp))) (foo)) */
|
|||
|
src = XEXP (dest, 0);
|
|||
|
code = GET_CODE (src);
|
|||
|
|
|||
|
switch (code)
|
|||
|
{
|
|||
|
case PRE_MODIFY:
|
|||
|
case POST_MODIFY:
|
|||
|
if (XEXP (src, 0) == stack_pointer_rtx)
|
|||
|
{
|
|||
|
rtx val = XEXP (XEXP (src, 1), 1);
|
|||
|
/* We handle only adjustments by constant amount. */
|
|||
|
gcc_assert (GET_CODE (XEXP (src, 1)) == PLUS
|
|||
|
&& CONST_INT_P (val));
|
|||
|
offset = -INTVAL (val);
|
|||
|
break;
|
|||
|
}
|
|||
|
return 0;
|
|||
|
|
|||
|
case PRE_DEC:
|
|||
|
case POST_DEC:
|
|||
|
if (XEXP (src, 0) == stack_pointer_rtx)
|
|||
|
{
|
|||
|
offset = GET_MODE_SIZE (GET_MODE (dest));
|
|||
|
break;
|
|||
|
}
|
|||
|
return 0;
|
|||
|
|
|||
|
case PRE_INC:
|
|||
|
case POST_INC:
|
|||
|
if (XEXP (src, 0) == stack_pointer_rtx)
|
|||
|
{
|
|||
|
offset = -GET_MODE_SIZE (GET_MODE (dest));
|
|||
|
break;
|
|||
|
}
|
|||
|
return 0;
|
|||
|
|
|||
|
default:
|
|||
|
return 0;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
return 0;
|
|||
|
|
|||
|
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. */
|
|||
|
|
|||
|
static void
|
|||
|
dwarf2out_args_size (const char *label, HOST_WIDE_INT size)
|
|||
|
{
|
|||
|
dw_cfi_ref cfi;
|
|||
|
|
|||
|
if (size == old_args_size)
|
|||
|
return;
|
|||
|
|
|||
|
old_args_size = size;
|
|||
|
|
|||
|
cfi = new_cfi ();
|
|||
|
cfi->dw_cfi_opc = DW_CFA_GNU_args_size;
|
|||
|
cfi->dw_cfi_oprnd1.dw_cfi_offset = size;
|
|||
|
add_fde_cfi (label, cfi);
|
|||
|
}
|
|||
|
|
|||
|
/* Record a stack adjustment of OFFSET bytes. */
|
|||
|
|
|||
|
static void
|
|||
|
dwarf2out_stack_adjust (HOST_WIDE_INT offset, const char *label)
|
|||
|
{
|
|||
|
if (cfa.reg == STACK_POINTER_REGNUM)
|
|||
|
cfa.offset += offset;
|
|||
|
|
|||
|
if (cfa_store.reg == STACK_POINTER_REGNUM)
|
|||
|
cfa_store.offset += offset;
|
|||
|
|
|||
|
if (ACCUMULATE_OUTGOING_ARGS)
|
|||
|
return;
|
|||
|
|
|||
|
#ifndef STACK_GROWS_DOWNWARD
|
|||
|
offset = -offset;
|
|||
|
#endif
|
|||
|
|
|||
|
args_size += offset;
|
|||
|
if (args_size < 0)
|
|||
|
args_size = 0;
|
|||
|
|
|||
|
def_cfa_1 (label, &cfa);
|
|||
|
if (flag_asynchronous_unwind_tables)
|
|||
|
dwarf2out_args_size (label, args_size);
|
|||
|
}
|
|||
|
|
|||
|
/* Check INSN to see if it looks like a push or a stack adjustment, and
|
|||
|
make a note of it if it does. EH uses this information to find out
|
|||
|
how much extra space it needs to pop off the stack. */
|
|||
|
|
|||
|
static void
|
|||
|
dwarf2out_notice_stack_adjust (rtx insn, bool after_p)
|
|||
|
{
|
|||
|
HOST_WIDE_INT offset;
|
|||
|
const char *label;
|
|||
|
int i;
|
|||
|
|
|||
|
/* Don't handle epilogues at all. Certainly it would be wrong to do so
|
|||
|
with this function. Proper support would require all frame-related
|
|||
|
insns to be marked, and to be able to handle saving state around
|
|||
|
epilogues textually in the middle of the function. */
|
|||
|
if (prologue_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)
|
|||
|
{
|
|||
|
if (CALL_P (insn) && !after_p)
|
|||
|
{
|
|||
|
/* Extract the size of the args from the CALL rtx itself. */
|
|||
|
insn = PATTERN (insn);
|
|||
|
if (GET_CODE (insn) == PARALLEL)
|
|||
|
insn = XVECEXP (insn, 0, 0);
|
|||
|
if (GET_CODE (insn) == SET)
|
|||
|
insn = SET_SRC (insn);
|
|||
|
gcc_assert (GET_CODE (insn) == CALL);
|
|||
|
dwarf2out_args_size ("", INTVAL (XEXP (insn, 1)));
|
|||
|
}
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (CALL_P (insn) && !after_p)
|
|||
|
{
|
|||
|
if (!flag_asynchronous_unwind_tables)
|
|||
|
dwarf2out_args_size ("", args_size);
|
|||
|
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
|
|||
|
}
|
|||
|
else if (GET_CODE (PATTERN (insn)) == SET)
|
|||
|
offset = stack_adjust_offset (PATTERN (insn), 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 (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),
|
|||
|
args_size, offset);
|
|||
|
}
|
|||
|
else
|
|||
|
return;
|
|||
|
|
|||
|
if (offset == 0)
|
|||
|
return;
|
|||
|
|
|||
|
label = dwarf2out_cfi_label (false);
|
|||
|
dwarf2out_stack_adjust (offset, label);
|
|||
|
}
|
|||
|
|
|||
|
/* 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. */
|
|||
|
|
|||
|
struct GTY(()) queued_reg_save {
|
|||
|
struct queued_reg_save *next;
|
|||
|
rtx reg;
|
|||
|
HOST_WIDE_INT cfa_offset;
|
|||
|
rtx saved_reg;
|
|||
|
};
|
|||
|
|
|||
|
static GTY(()) struct queued_reg_save *queued_reg_saves;
|
|||
|
|
|||
|
/* The caller's ORIG_REG is saved in SAVED_IN_REG. */
|
|||
|
typedef struct GTY(()) reg_saved_in_data {
|
|||
|
rtx orig_reg;
|
|||
|
rtx saved_in_reg;
|
|||
|
} reg_saved_in_data;
|
|||
|
|
|||
|
DEF_VEC_O (reg_saved_in_data);
|
|||
|
DEF_VEC_ALLOC_O (reg_saved_in_data, gc);
|
|||
|
|
|||
|
/* A set of registers saved in other registers. This is implemented as
|
|||
|
a flat array because it normally contains zero or 1 entry, depending
|
|||
|
on the target. IA-64 is the big spender here, using a maximum of
|
|||
|
5 entries. */
|
|||
|
static GTY(()) VEC(reg_saved_in_data, gc) *regs_saved_in_regs;
|
|||
|
|
|||
|
/* Compare X and Y for equivalence. The inputs may be REGs or PC_RTX. */
|
|||
|
|
|||
|
static bool
|
|||
|
compare_reg_or_pc (rtx x, rtx y)
|
|||
|
{
|
|||
|
if (REG_P (x) && REG_P (y))
|
|||
|
return REGNO (x) == REGNO (y);
|
|||
|
return x == y;
|
|||
|
}
|
|||
|
|
|||
|
/* Record SRC as being saved in DEST. DEST may be null to delete an
|
|||
|
existing entry. SRC may be a register or PC_RTX. */
|
|||
|
|
|||
|
static void
|
|||
|
record_reg_saved_in_reg (rtx dest, rtx src)
|
|||
|
{
|
|||
|
reg_saved_in_data *elt;
|
|||
|
size_t i;
|
|||
|
|
|||
|
FOR_EACH_VEC_ELT (reg_saved_in_data, regs_saved_in_regs, i, elt)
|
|||
|
if (compare_reg_or_pc (elt->orig_reg, src))
|
|||
|
{
|
|||
|
if (dest == NULL)
|
|||
|
VEC_unordered_remove(reg_saved_in_data, regs_saved_in_regs, i);
|
|||
|
else
|
|||
|
elt->saved_in_reg = dest;
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (dest == NULL)
|
|||
|
return;
|
|||
|
|
|||
|
elt = VEC_safe_push(reg_saved_in_data, gc, regs_saved_in_regs, NULL);
|
|||
|
elt->orig_reg = src;
|
|||
|
elt->saved_in_reg = dest;
|
|||
|
}
|
|||
|
|
|||
|
static const char *last_reg_save_label;
|
|||
|
|
|||
|
/* Add an entry to QUEUED_REG_SAVES saying that REG is now saved at
|
|||
|
SREG, or if SREG is NULL then it is saved at OFFSET to the CFA. */
|
|||
|
|
|||
|
static void
|
|||
|
queue_reg_save (const char *label, rtx reg, rtx sreg, HOST_WIDE_INT offset)
|
|||
|
{
|
|||
|
struct queued_reg_save *q;
|
|||
|
|
|||
|
/* Duplicates waste space, but it's also necessary to remove them
|
|||
|
for correctness, since the queue gets output in reverse
|
|||
|
order. */
|
|||
|
for (q = queued_reg_saves; q != NULL; q = q->next)
|
|||
|
if (REGNO (q->reg) == REGNO (reg))
|
|||
|
break;
|
|||
|
|
|||
|
if (q == NULL)
|
|||
|
{
|
|||
|
q = ggc_alloc_queued_reg_save ();
|
|||
|
q->next = queued_reg_saves;
|
|||
|
queued_reg_saves = q;
|
|||
|
}
|
|||
|
|
|||
|
q->reg = reg;
|
|||
|
q->cfa_offset = offset;
|
|||
|
q->saved_reg = sreg;
|
|||
|
|
|||
|
last_reg_save_label = label;
|
|||
|
}
|
|||
|
|
|||
|
/* Output all the entries in QUEUED_REG_SAVES. */
|
|||
|
|
|||
|
static void
|
|||
|
dwarf2out_flush_queued_reg_saves (void)
|
|||
|
{
|
|||
|
struct queued_reg_save *q;
|
|||
|
|
|||
|
for (q = queued_reg_saves; q; q = q->next)
|
|||
|
{
|
|||
|
unsigned int reg, sreg;
|
|||
|
|
|||
|
record_reg_saved_in_reg (q->saved_reg, q->reg);
|
|||
|
|
|||
|
reg = DWARF_FRAME_REGNUM (REGNO (q->reg));
|
|||
|
if (q->saved_reg)
|
|||
|
sreg = DWARF_FRAME_REGNUM (REGNO (q->saved_reg));
|
|||
|
else
|
|||
|
sreg = INVALID_REGNUM;
|
|||
|
reg_save (last_reg_save_label, reg, sreg, q->cfa_offset);
|
|||
|
}
|
|||
|
|
|||
|
queued_reg_saves = NULL;
|
|||
|
last_reg_save_label = NULL;
|
|||
|
}
|
|||
|
|
|||
|
/* Does INSN clobber any register which QUEUED_REG_SAVES lists a saved
|
|||
|
location for? Or, does it clobber a register which we've previously
|
|||
|
said that some other register is saved in, and for which we now
|
|||
|
have a new location for? */
|
|||
|
|
|||
|
static bool
|
|||
|
clobbers_queued_reg_save (const_rtx insn)
|
|||
|
{
|
|||
|
struct queued_reg_save *q;
|
|||
|
|
|||
|
for (q = queued_reg_saves; q; q = q->next)
|
|||
|
{
|
|||
|
size_t i;
|
|||
|
reg_saved_in_data *rir;
|
|||
|
|
|||
|
if (modified_in_p (q->reg, insn))
|
|||
|
return true;
|
|||
|
|
|||
|
FOR_EACH_VEC_ELT (reg_saved_in_data, regs_saved_in_regs, i, rir)
|
|||
|
if (compare_reg_or_pc (q->reg, rir->orig_reg)
|
|||
|
&& modified_in_p (rir->saved_in_reg, insn))
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* What register, if any, is currently saved in REG? */
|
|||
|
|
|||
|
static rtx
|
|||
|
reg_saved_in (rtx reg)
|
|||
|
{
|
|||
|
unsigned int regn = REGNO (reg);
|
|||
|
struct queued_reg_save *q;
|
|||
|
reg_saved_in_data *rir;
|
|||
|
size_t i;
|
|||
|
|
|||
|
for (q = queued_reg_saves; q; q = q->next)
|
|||
|
if (q->saved_reg && regn == REGNO (q->saved_reg))
|
|||
|
return q->reg;
|
|||
|
|
|||
|
FOR_EACH_VEC_ELT (reg_saved_in_data, regs_saved_in_regs, i, rir)
|
|||
|
if (regn == REGNO (rir->saved_in_reg))
|
|||
|
return rir->orig_reg;
|
|||
|
|
|||
|
return NULL_RTX;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* A temporary register holding an integral value used in adjusting SP
|
|||
|
or setting up the store_reg. The "offset" field holds the integer
|
|||
|
value, not an offset. */
|
|||
|
static dw_cfa_location cfa_temp;
|
|||
|
|
|||
|
/* A subroutine of dwarf2out_frame_debug, process a REG_DEF_CFA note. */
|
|||
|
|
|||
|
static void
|
|||
|
dwarf2out_frame_debug_def_cfa (rtx pat, const char *label)
|
|||
|
{
|
|||
|
memset (&cfa, 0, sizeof (cfa));
|
|||
|
|
|||
|
switch (GET_CODE (pat))
|
|||
|
{
|
|||
|
case PLUS:
|
|||
|
cfa.reg = REGNO (XEXP (pat, 0));
|
|||
|
cfa.offset = INTVAL (XEXP (pat, 1));
|
|||
|
break;
|
|||
|
|
|||
|
case REG:
|
|||
|
cfa.reg = REGNO (pat);
|
|||
|
break;
|
|||
|
|
|||
|
case MEM:
|
|||
|
cfa.indirect = 1;
|
|||
|
pat = XEXP (pat, 0);
|
|||
|
if (GET_CODE (pat) == PLUS)
|
|||
|
{
|
|||
|
cfa.base_offset = INTVAL (XEXP (pat, 1));
|
|||
|
pat = XEXP (pat, 0);
|
|||
|
}
|
|||
|
cfa.reg = REGNO (pat);
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
/* Recurse and define an expression. */
|
|||
|
gcc_unreachable ();
|
|||
|
}
|
|||
|
|
|||
|
def_cfa_1 (label, &cfa);
|
|||
|
}
|
|||
|
|
|||
|
/* A subroutine of dwarf2out_frame_debug, process a REG_ADJUST_CFA note. */
|
|||
|
|
|||
|
static void
|
|||
|
dwarf2out_frame_debug_adjust_cfa (rtx pat, const char *label)
|
|||
|
{
|
|||
|
rtx src, dest;
|
|||
|
|
|||
|
gcc_assert (GET_CODE (pat) == SET);
|
|||
|
dest = XEXP (pat, 0);
|
|||
|
src = XEXP (pat, 1);
|
|||
|
|
|||
|
switch (GET_CODE (src))
|
|||
|
{
|
|||
|
case PLUS:
|
|||
|
gcc_assert (REGNO (XEXP (src, 0)) == cfa.reg);
|
|||
|
cfa.offset -= INTVAL (XEXP (src, 1));
|
|||
|
break;
|
|||
|
|
|||
|
case REG:
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
gcc_unreachable ();
|
|||
|
}
|
|||
|
|
|||
|
cfa.reg = REGNO (dest);
|
|||
|
gcc_assert (cfa.indirect == 0);
|
|||
|
|
|||
|
def_cfa_1 (label, &cfa);
|
|||
|
}
|
|||
|
|
|||
|
/* A subroutine of dwarf2out_frame_debug, process a REG_CFA_OFFSET note. */
|
|||
|
|
|||
|
static void
|
|||
|
dwarf2out_frame_debug_cfa_offset (rtx set, const char *label)
|
|||
|
{
|
|||
|
HOST_WIDE_INT offset;
|
|||
|
rtx src, addr, span;
|
|||
|
unsigned int sregno;
|
|||
|
|
|||
|
src = XEXP (set, 1);
|
|||
|
addr = XEXP (set, 0);
|
|||
|
gcc_assert (MEM_P (addr));
|
|||
|
addr = XEXP (addr, 0);
|
|||
|
|
|||
|
/* As documented, only consider extremely simple addresses. */
|
|||
|
switch (GET_CODE (addr))
|
|||
|
{
|
|||
|
case REG:
|
|||
|
gcc_assert (REGNO (addr) == cfa.reg);
|
|||
|
offset = -cfa.offset;
|
|||
|
break;
|
|||
|
case PLUS:
|
|||
|
gcc_assert (REGNO (XEXP (addr, 0)) == cfa.reg);
|
|||
|
offset = INTVAL (XEXP (addr, 1)) - cfa.offset;
|
|||
|
break;
|
|||
|
default:
|
|||
|
gcc_unreachable ();
|
|||
|
}
|
|||
|
|
|||
|
if (src == pc_rtx)
|
|||
|
{
|
|||
|
span = NULL;
|
|||
|
sregno = DWARF_FRAME_RETURN_COLUMN;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
span = targetm.dwarf_register_span (src);
|
|||
|
sregno = DWARF_FRAME_REGNUM (REGNO (src));
|
|||
|
}
|
|||
|
|
|||
|
/* ??? We'd like to use queue_reg_save, but we need to come up with
|
|||
|
a different flushing heuristic for epilogues. */
|
|||
|
if (!span)
|
|||
|
reg_save (label, sregno, INVALID_REGNUM, offset);
|
|||
|
else
|
|||
|
{
|
|||
|
/* We have a PARALLEL describing where the contents of SRC live.
|
|||
|
Queue register saves for each piece of the PARALLEL. */
|
|||
|
int par_index;
|
|||
|
int limit;
|
|||
|
HOST_WIDE_INT span_offset = offset;
|
|||
|
|
|||
|
gcc_assert (GET_CODE (span) == PARALLEL);
|
|||
|
|
|||
|
limit = XVECLEN (span, 0);
|
|||
|
for (par_index = 0; par_index < limit; par_index++)
|
|||
|
{
|
|||
|
rtx elem = XVECEXP (span, 0, par_index);
|
|||
|
|
|||
|
sregno = DWARF_FRAME_REGNUM (REGNO (src));
|
|||
|
reg_save (label, sregno, INVALID_REGNUM, span_offset);
|
|||
|
span_offset += GET_MODE_SIZE (GET_MODE (elem));
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* A subroutine of dwarf2out_frame_debug, process a REG_CFA_REGISTER note. */
|
|||
|
|
|||
|
static void
|
|||
|
dwarf2out_frame_debug_cfa_register (rtx set, const char *label)
|
|||
|
{
|
|||
|
rtx src, dest;
|
|||
|
unsigned sregno, dregno;
|
|||
|
|
|||
|
src = XEXP (set, 1);
|
|||
|
dest = XEXP (set, 0);
|
|||
|
|
|||
|
if (src == pc_rtx)
|
|||
|
sregno = DWARF_FRAME_RETURN_COLUMN;
|
|||
|
else
|
|||
|
{
|
|||
|
record_reg_saved_in_reg (dest, src);
|
|||
|
sregno = DWARF_FRAME_REGNUM (REGNO (src));
|
|||
|
}
|
|||
|
|
|||
|
dregno = DWARF_FRAME_REGNUM (REGNO (dest));
|
|||
|
|
|||
|
/* ??? We'd like to use queue_reg_save, but we need to come up with
|
|||
|
a different flushing heuristic for epilogues. */
|
|||
|
reg_save (label, sregno, dregno, 0);
|
|||
|
}
|
|||
|
|
|||
|
/* A subroutine of dwarf2out_frame_debug, process a REG_CFA_EXPRESSION note. */
|
|||
|
|
|||
|
static void
|
|||
|
dwarf2out_frame_debug_cfa_expression (rtx set, const char *label)
|
|||
|
{
|
|||
|
rtx src, dest, span;
|
|||
|
dw_cfi_ref cfi = new_cfi ();
|
|||
|
|
|||
|
dest = SET_DEST (set);
|
|||
|
src = SET_SRC (set);
|
|||
|
|
|||
|
gcc_assert (REG_P (src));
|
|||
|
gcc_assert (MEM_P (dest));
|
|||
|
|
|||
|
span = targetm.dwarf_register_span (src);
|
|||
|
gcc_assert (!span);
|
|||
|
|
|||
|
cfi->dw_cfi_opc = DW_CFA_expression;
|
|||
|
cfi->dw_cfi_oprnd1.dw_cfi_reg_num = DWARF_FRAME_REGNUM (REGNO (src));
|
|||
|
cfi->dw_cfi_oprnd2.dw_cfi_loc
|
|||
|
= mem_loc_descriptor (XEXP (dest, 0), get_address_mode (dest),
|
|||
|
GET_MODE (dest), VAR_INIT_STATUS_INITIALIZED);
|
|||
|
|
|||
|
/* ??? We'd like to use queue_reg_save, were the interface different,
|
|||
|
and, as above, we could manage flushing for epilogues. */
|
|||
|
add_fde_cfi (label, cfi);
|
|||
|
}
|
|||
|
|
|||
|
/* A subroutine of dwarf2out_frame_debug, process a REG_CFA_RESTORE note. */
|
|||
|
|
|||
|
static void
|
|||
|
dwarf2out_frame_debug_cfa_restore (rtx reg, const char *label)
|
|||
|
{
|
|||
|
dw_cfi_ref cfi = new_cfi ();
|
|||
|
unsigned int regno = DWARF_FRAME_REGNUM (REGNO (reg));
|
|||
|
|
|||
|
cfi->dw_cfi_opc = (regno & ~0x3f ? DW_CFA_restore_extended : DW_CFA_restore);
|
|||
|
cfi->dw_cfi_oprnd1.dw_cfi_reg_num = regno;
|
|||
|
|
|||
|
add_fde_cfi (label, cfi);
|
|||
|
}
|
|||
|
|
|||
|
/* A subroutine of dwarf2out_frame_debug, process a REG_CFA_WINDOW_SAVE.
|
|||
|
??? Perhaps we should note in the CIE where windows are saved (instead of
|
|||
|
assuming 0(cfa)) and what registers are in the window. */
|
|||
|
|
|||
|
static void
|
|||
|
dwarf2out_frame_debug_cfa_window_save (const char *label)
|
|||
|
{
|
|||
|
dw_cfi_ref cfi = new_cfi ();
|
|||
|
|
|||
|
cfi->dw_cfi_opc = DW_CFA_GNU_window_save;
|
|||
|
add_fde_cfi (label, 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.
|
|||
|
LABEL indicates the address of EXPR.
|
|||
|
|
|||
|
This function encodes a state machine mapping rtxes to actions on
|
|||
|
cfa, cfa_store, and cfa_temp.reg. We describe these rules so
|
|||
|
users need not read the source code.
|
|||
|
|
|||
|
The High-Level Picture
|
|||
|
|
|||
|
Changes in the register we use to calculate the CFA: Currently we
|
|||
|
assume that if you copy the CFA register into another register, we
|
|||
|
should take the other one as the new CFA register; this seems to
|
|||
|
work pretty well. If it's wrong for some target, it's simple
|
|||
|
enough not to set RTX_FRAME_RELATED_P on the insn in question.
|
|||
|
|
|||
|
Changes in the register we use for saving registers to the stack:
|
|||
|
This is usually SP, but not always. Again, we deduce that if you
|
|||
|
copy SP into another register (and SP is not the CFA register),
|
|||
|
then the new register is the one we will be using for register
|
|||
|
saves. This also seems to work.
|
|||
|
|
|||
|
Register saves: There's not much guesswork about this one; if
|
|||
|
RTX_FRAME_RELATED_P is set on an insn which modifies memory, it's a
|
|||
|
register save, and the register used to calculate the destination
|
|||
|
had better be the one we think we're using for this purpose.
|
|||
|
It's also assumed that a copy from a call-saved register to another
|
|||
|
register is saving that register if RTX_FRAME_RELATED_P is set on
|
|||
|
that instruction. If the copy is from a call-saved register to
|
|||
|
the *same* register, that means that the register is now the same
|
|||
|
value as in the caller.
|
|||
|
|
|||
|
Except: If the register being saved is the CFA register, and the
|
|||
|
offset is nonzero, we are saving the CFA, so we assume we have to
|
|||
|
use DW_CFA_def_cfa_expression. If the offset is 0, we assume that
|
|||
|
the intent is to save the value of SP from the previous frame.
|
|||
|
|
|||
|
In addition, if a register has previously been saved to a different
|
|||
|
register,
|
|||
|
|
|||
|
Invariants / Summaries of Rules
|
|||
|
|
|||
|
cfa current rule for calculating the CFA. It usually
|
|||
|
consists of a register and an offset.
|
|||
|
cfa_store register used by prologue code to save things to the stack
|
|||
|
cfa_store.offset is the offset from the value of
|
|||
|
cfa_store.reg to the actual CFA
|
|||
|
cfa_temp register holding an integral value. cfa_temp.offset
|
|||
|
stores the value, which will be used to adjust the
|
|||
|
stack pointer. cfa_temp is also used like cfa_store,
|
|||
|
to track stores to the stack via fp or a temp reg.
|
|||
|
|
|||
|
Rules 1- 4: Setting a register's value to cfa.reg or an expression
|
|||
|
with cfa.reg as the first operand changes the cfa.reg and its
|
|||
|
cfa.offset. Rule 1 and 4 also set cfa_temp.reg and
|
|||
|
cfa_temp.offset.
|
|||
|
|
|||
|
Rules 6- 9: Set a non-cfa.reg register value to a constant or an
|
|||
|
expression yielding a constant. This sets cfa_temp.reg
|
|||
|
and cfa_temp.offset.
|
|||
|
|
|||
|
Rule 5: Create a new register cfa_store used to save items to the
|
|||
|
stack.
|
|||
|
|
|||
|
Rules 10-14: Save a register to the stack. Define offset as the
|
|||
|
difference of the original location and cfa_store's
|
|||
|
location (or cfa_temp's location if cfa_temp is used).
|
|||
|
|
|||
|
Rules 16-20: If AND operation happens on sp in prologue, we assume
|
|||
|
stack is realigned. We will use a group of DW_OP_XXX
|
|||
|
expressions to represent the location of the stored
|
|||
|
register instead of CFA+offset.
|
|||
|
|
|||
|
The Rules
|
|||
|
|
|||
|
"{a,b}" indicates a choice of a xor b.
|
|||
|
"<reg>:cfa.reg" indicates that <reg> must equal cfa.reg.
|
|||
|
|
|||
|
Rule 1:
|
|||
|
(set <reg1> <reg2>:cfa.reg)
|
|||
|
effects: cfa.reg = <reg1>
|
|||
|
cfa.offset unchanged
|
|||
|
cfa_temp.reg = <reg1>
|
|||
|
cfa_temp.offset = cfa.offset
|
|||
|
|
|||
|
Rule 2:
|
|||
|
(set sp ({minus,plus,losum} {sp,fp}:cfa.reg
|
|||
|
{<const_int>,<reg>:cfa_temp.reg}))
|
|||
|
effects: cfa.reg = sp if fp used
|
|||
|
cfa.offset += {+/- <const_int>, cfa_temp.offset} if cfa.reg==sp
|
|||
|
cfa_store.offset += {+/- <const_int>, cfa_temp.offset}
|
|||
|
if cfa_store.reg==sp
|
|||
|
|
|||
|
Rule 3:
|
|||
|
(set fp ({minus,plus,losum} <reg>:cfa.reg <const_int>))
|
|||
|
effects: cfa.reg = fp
|
|||
|
cfa_offset += +/- <const_int>
|
|||
|
|
|||
|
Rule 4:
|
|||
|
(set <reg1> ({plus,losum} <reg2>:cfa.reg <const_int>))
|
|||
|
constraints: <reg1> != fp
|
|||
|
<reg1> != sp
|
|||
|
effects: cfa.reg = <reg1>
|
|||
|
cfa_temp.reg = <reg1>
|
|||
|
cfa_temp.offset = cfa.offset
|
|||
|
|
|||
|
Rule 5:
|
|||
|
(set <reg1> (plus <reg2>:cfa_temp.reg sp:cfa.reg))
|
|||
|
constraints: <reg1> != fp
|
|||
|
<reg1> != sp
|
|||
|
effects: cfa_store.reg = <reg1>
|
|||
|
cfa_store.offset = cfa.offset - cfa_temp.offset
|
|||
|
|
|||
|
Rule 6:
|
|||
|
(set <reg> <const_int>)
|
|||
|
effects: cfa_temp.reg = <reg>
|
|||
|
cfa_temp.offset = <const_int>
|
|||
|
|
|||
|
Rule 7:
|
|||
|
(set <reg1>:cfa_temp.reg (ior <reg2>:cfa_temp.reg <const_int>))
|
|||
|
effects: cfa_temp.reg = <reg1>
|
|||
|
cfa_temp.offset |= <const_int>
|
|||
|
|
|||
|
Rule 8:
|
|||
|
(set <reg> (high <exp>))
|
|||
|
effects: none
|
|||
|
|
|||
|
Rule 9:
|
|||
|
(set <reg> (lo_sum <exp> <const_int>))
|
|||
|
effects: cfa_temp.reg = <reg>
|
|||
|
cfa_temp.offset = <const_int>
|
|||
|
|
|||
|
Rule 10:
|
|||
|
(set (mem ({pre,post}_modify sp:cfa_store (???? <reg1> <const_int>))) <reg2>)
|
|||
|
effects: cfa_store.offset -= <const_int>
|
|||
|
cfa.offset = cfa_store.offset if cfa.reg == sp
|
|||
|
cfa.reg = sp
|
|||
|
cfa.base_offset = -cfa_store.offset
|
|||
|
|
|||
|
Rule 11:
|
|||
|
(set (mem ({pre_inc,pre_dec,post_dec} sp:cfa_store.reg)) <reg>)
|
|||
|
effects: cfa_store.offset += -/+ mode_size(mem)
|
|||
|
cfa.offset = cfa_store.offset if cfa.reg == sp
|
|||
|
cfa.reg = sp
|
|||
|
cfa.base_offset = -cfa_store.offset
|
|||
|
|
|||
|
Rule 12:
|
|||
|
(set (mem ({minus,plus,losum} <reg1>:{cfa_store,cfa_temp} <const_int>))
|
|||
|
|
|||
|
<reg2>)
|
|||
|
effects: cfa.reg = <reg1>
|
|||
|
cfa.base_offset = -/+ <const_int> - {cfa_store,cfa_temp}.offset
|
|||
|
|
|||
|
Rule 13:
|
|||
|
(set (mem <reg1>:{cfa_store,cfa_temp}) <reg2>)
|
|||
|
effects: cfa.reg = <reg1>
|
|||
|
cfa.base_offset = -{cfa_store,cfa_temp}.offset
|
|||
|
|
|||
|
Rule 14:
|
|||
|
(set (mem (post_inc <reg1>:cfa_temp <const_int>)) <reg2>)
|
|||
|
effects: cfa.reg = <reg1>
|
|||
|
cfa.base_offset = -cfa_temp.offset
|
|||
|
cfa_temp.offset -= mode_size(mem)
|
|||
|
|
|||
|
Rule 15:
|
|||
|
(set <reg> {unspec, unspec_volatile})
|
|||
|
effects: target-dependent
|
|||
|
|
|||
|
Rule 16:
|
|||
|
(set sp (and: sp <const_int>))
|
|||
|
constraints: cfa_store.reg == sp
|
|||
|
effects: current_fde.stack_realign = 1
|
|||
|
cfa_store.offset = 0
|
|||
|
fde->drap_reg = cfa.reg if cfa.reg != sp and cfa.reg != fp
|
|||
|
|
|||
|
Rule 17:
|
|||
|
(set (mem ({pre_inc, pre_dec} sp)) (mem (plus (cfa.reg) (const_int))))
|
|||
|
effects: cfa_store.offset += -/+ mode_size(mem)
|
|||
|
|
|||
|
Rule 18:
|
|||
|
(set (mem ({pre_inc, pre_dec} sp)) fp)
|
|||
|
constraints: fde->stack_realign == 1
|
|||
|
effects: cfa_store.offset = 0
|
|||
|
cfa.reg != HARD_FRAME_POINTER_REGNUM
|
|||
|
|
|||
|
Rule 19:
|
|||
|
(set (mem ({pre_inc, pre_dec} sp)) cfa.reg)
|
|||
|
constraints: fde->stack_realign == 1
|
|||
|
&& cfa.offset == 0
|
|||
|
&& cfa.indirect == 0
|
|||
|
&& cfa.reg != HARD_FRAME_POINTER_REGNUM
|
|||
|
effects: Use DW_CFA_def_cfa_expression to define cfa
|
|||
|
cfa.reg == fde->drap_reg */
|
|||
|
|
|||
|
static void
|
|||
|
dwarf2out_frame_debug_expr (rtx expr, const char *label)
|
|||
|
{
|
|||
|
rtx src, dest, span;
|
|||
|
HOST_WIDE_INT offset;
|
|||
|
dw_fde_ref fde;
|
|||
|
|
|||
|
/* If RTX_FRAME_RELATED_P is set on a PARALLEL, process each member of
|
|||
|
the PARALLEL independently. The first element is always processed if
|
|||
|
it is a SET. This is for backward compatibility. Other elements
|
|||
|
are processed only if they are SETs and the RTX_FRAME_RELATED_P
|
|||
|
flag is set in them. */
|
|||
|
if (GET_CODE (expr) == PARALLEL || GET_CODE (expr) == SEQUENCE)
|
|||
|
{
|
|||
|
int par_index;
|
|||
|
int limit = XVECLEN (expr, 0);
|
|||
|
rtx elem;
|
|||
|
|
|||
|
/* PARALLELs have strict read-modify-write semantics, so we
|
|||
|
ought to evaluate every rvalue before changing any lvalue.
|
|||
|
It's cumbersome to do that in general, but there's an
|
|||
|
easy approximation that is enough for all current users:
|
|||
|
handle register saves before register assignments. */
|
|||
|
if (GET_CODE (expr) == PARALLEL)
|
|||
|
for (par_index = 0; par_index < limit; par_index++)
|
|||
|
{
|
|||
|
elem = XVECEXP (expr, 0, par_index);
|
|||
|
if (GET_CODE (elem) == SET
|
|||
|
&& MEM_P (SET_DEST (elem))
|
|||
|
&& (RTX_FRAME_RELATED_P (elem) || par_index == 0))
|
|||
|
dwarf2out_frame_debug_expr (elem, label);
|
|||
|
}
|
|||
|
|
|||
|
for (par_index = 0; par_index < limit; par_index++)
|
|||
|
{
|
|||
|
elem = XVECEXP (expr, 0, par_index);
|
|||
|
if (GET_CODE (elem) == SET
|
|||
|
&& (!MEM_P (SET_DEST (elem)) || GET_CODE (expr) == SEQUENCE)
|
|||
|
&& (RTX_FRAME_RELATED_P (elem) || par_index == 0))
|
|||
|
dwarf2out_frame_debug_expr (elem, label);
|
|||
|
else if (GET_CODE (elem) == SET
|
|||
|
&& par_index != 0
|
|||
|
&& !RTX_FRAME_RELATED_P (elem))
|
|||
|
{
|
|||
|
/* Stack adjustment combining might combine some post-prologue
|
|||
|
stack adjustment into a prologue stack adjustment. */
|
|||
|
HOST_WIDE_INT offset = stack_adjust_offset (elem, args_size, 0);
|
|||
|
|
|||
|
if (offset != 0)
|
|||
|
dwarf2out_stack_adjust (offset, label);
|
|||
|
}
|
|||
|
}
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
gcc_assert (GET_CODE (expr) == SET);
|
|||
|
|
|||
|
src = SET_SRC (expr);
|
|||
|
dest = SET_DEST (expr);
|
|||
|
|
|||
|
if (REG_P (src))
|
|||
|
{
|
|||
|
rtx rsi = reg_saved_in (src);
|
|||
|
if (rsi)
|
|||
|
src = rsi;
|
|||
|
}
|
|||
|
|
|||
|
fde = current_fde ();
|
|||
|
|
|||
|
switch (GET_CODE (dest))
|
|||
|
{
|
|||
|
case REG:
|
|||
|
switch (GET_CODE (src))
|
|||
|
{
|
|||
|
/* Setting FP from SP. */
|
|||
|
case REG:
|
|||
|
if (cfa.reg == (unsigned) REGNO (src))
|
|||
|
{
|
|||
|
/* Rule 1 */
|
|||
|
/* Update the CFA rule wrt SP or FP. Make sure src is
|
|||
|
relative to the current CFA register.
|
|||
|
|
|||
|
We used to require that dest be either SP or FP, but the
|
|||
|
ARM copies SP to a temporary register, and from there to
|
|||
|
FP. So we just rely on the backends to only set
|
|||
|
RTX_FRAME_RELATED_P on appropriate insns. */
|
|||
|
cfa.reg = REGNO (dest);
|
|||
|
cfa_temp.reg = cfa.reg;
|
|||
|
cfa_temp.offset = cfa.offset;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* Saving a register in a register. */
|
|||
|
gcc_assert (!fixed_regs [REGNO (dest)]
|
|||
|
/* For the SPARC and its register window. */
|
|||
|
|| (DWARF_FRAME_REGNUM (REGNO (src))
|
|||
|
== DWARF_FRAME_RETURN_COLUMN));
|
|||
|
|
|||
|
/* After stack is aligned, we can only save SP in FP
|
|||
|
if drap register is used. In this case, we have
|
|||
|
to restore stack pointer with the CFA value and we
|
|||
|
don't generate this DWARF information. */
|
|||
|
if (fde
|
|||
|
&& fde->stack_realign
|
|||
|
&& REGNO (src) == STACK_POINTER_REGNUM)
|
|||
|
gcc_assert (REGNO (dest) == HARD_FRAME_POINTER_REGNUM
|
|||
|
&& fde->drap_reg != INVALID_REGNUM
|
|||
|
&& cfa.reg != REGNO (src));
|
|||
|
else
|
|||
|
queue_reg_save (label, src, dest, 0);
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case PLUS:
|
|||
|
case MINUS:
|
|||
|
case LO_SUM:
|
|||
|
if (dest == stack_pointer_rtx)
|
|||
|
{
|
|||
|
/* Rule 2 */
|
|||
|
/* Adjusting SP. */
|
|||
|
switch (GET_CODE (XEXP (src, 1)))
|
|||
|
{
|
|||
|
case CONST_INT:
|
|||
|
offset = INTVAL (XEXP (src, 1));
|
|||
|
break;
|
|||
|
case REG:
|
|||
|
gcc_assert ((unsigned) REGNO (XEXP (src, 1))
|
|||
|
== cfa_temp.reg);
|
|||
|
offset = cfa_temp.offset;
|
|||
|
break;
|
|||
|
default:
|
|||
|
gcc_unreachable ();
|
|||
|
}
|
|||
|
|
|||
|
if (XEXP (src, 0) == hard_frame_pointer_rtx)
|
|||
|
{
|
|||
|
/* Restoring SP from FP in the epilogue. */
|
|||
|
gcc_assert (cfa.reg == (unsigned) HARD_FRAME_POINTER_REGNUM);
|
|||
|
cfa.reg = STACK_POINTER_REGNUM;
|
|||
|
}
|
|||
|
else if (GET_CODE (src) == LO_SUM)
|
|||
|
/* Assume we've set the source reg of the LO_SUM from sp. */
|
|||
|
;
|
|||
|
else
|
|||
|
gcc_assert (XEXP (src, 0) == stack_pointer_rtx);
|
|||
|
|
|||
|
if (GET_CODE (src) != MINUS)
|
|||
|
offset = -offset;
|
|||
|
if (cfa.reg == STACK_POINTER_REGNUM)
|
|||
|
cfa.offset += offset;
|
|||
|
if (cfa_store.reg == STACK_POINTER_REGNUM)
|
|||
|
cfa_store.offset += offset;
|
|||
|
}
|
|||
|
else if (dest == hard_frame_pointer_rtx)
|
|||
|
{
|
|||
|
/* Rule 3 */
|
|||
|
/* Either setting the FP from an offset of the SP,
|
|||
|
or adjusting the FP */
|
|||
|
gcc_assert (frame_pointer_needed);
|
|||
|
|
|||
|
gcc_assert (REG_P (XEXP (src, 0))
|
|||
|
&& (unsigned) REGNO (XEXP (src, 0)) == cfa.reg
|
|||
|
&& CONST_INT_P (XEXP (src, 1)));
|
|||
|
offset = INTVAL (XEXP (src, 1));
|
|||
|
if (GET_CODE (src) != MINUS)
|
|||
|
offset = -offset;
|
|||
|
cfa.offset += offset;
|
|||
|
cfa.reg = HARD_FRAME_POINTER_REGNUM;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
gcc_assert (GET_CODE (src) != MINUS);
|
|||
|
|
|||
|
/* Rule 4 */
|
|||
|
if (REG_P (XEXP (src, 0))
|
|||
|
&& REGNO (XEXP (src, 0)) == cfa.reg
|
|||
|
&& CONST_INT_P (XEXP (src, 1)))
|
|||
|
{
|
|||
|
/* Setting a temporary CFA register that will be copied
|
|||
|
into the FP later on. */
|
|||
|
offset = - INTVAL (XEXP (src, 1));
|
|||
|
cfa.offset += offset;
|
|||
|
cfa.reg = REGNO (dest);
|
|||
|
/* Or used to save regs to the stack. */
|
|||
|
cfa_temp.reg = cfa.reg;
|
|||
|
cfa_temp.offset = cfa.offset;
|
|||
|
}
|
|||
|
|
|||
|
/* Rule 5 */
|
|||
|
else if (REG_P (XEXP (src, 0))
|
|||
|
&& REGNO (XEXP (src, 0)) == cfa_temp.reg
|
|||
|
&& XEXP (src, 1) == stack_pointer_rtx)
|
|||
|
{
|
|||
|
/* Setting a scratch register that we will use instead
|
|||
|
of SP for saving registers to the stack. */
|
|||
|
gcc_assert (cfa.reg == STACK_POINTER_REGNUM);
|
|||
|
cfa_store.reg = REGNO (dest);
|
|||
|
cfa_store.offset = cfa.offset - cfa_temp.offset;
|
|||
|
}
|
|||
|
|
|||
|
/* Rule 9 */
|
|||
|
else if (GET_CODE (src) == LO_SUM
|
|||
|
&& CONST_INT_P (XEXP (src, 1)))
|
|||
|
{
|
|||
|
cfa_temp.reg = REGNO (dest);
|
|||
|
cfa_temp.offset = INTVAL (XEXP (src, 1));
|
|||
|
}
|
|||
|
else
|
|||
|
gcc_unreachable ();
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
/* Rule 6 */
|
|||
|
case CONST_INT:
|
|||
|
cfa_temp.reg = REGNO (dest);
|
|||
|
cfa_temp.offset = INTVAL (src);
|
|||
|
break;
|
|||
|
|
|||
|
/* Rule 7 */
|
|||
|
case IOR:
|
|||
|
gcc_assert (REG_P (XEXP (src, 0))
|
|||
|
&& (unsigned) REGNO (XEXP (src, 0)) == cfa_temp.reg
|
|||
|
&& CONST_INT_P (XEXP (src, 1)));
|
|||
|
|
|||
|
if ((unsigned) REGNO (dest) != cfa_temp.reg)
|
|||
|
cfa_temp.reg = REGNO (dest);
|
|||
|
cfa_temp.offset |= INTVAL (XEXP (src, 1));
|
|||
|
break;
|
|||
|
|
|||
|
/* Skip over HIGH, assuming it will be followed by a LO_SUM,
|
|||
|
which will fill in all of the bits. */
|
|||
|
/* Rule 8 */
|
|||
|
case HIGH:
|
|||
|
break;
|
|||
|
|
|||
|
/* Rule 15 */
|
|||
|
case UNSPEC:
|
|||
|
case UNSPEC_VOLATILE:
|
|||
|
gcc_assert (targetm.dwarf_handle_frame_unspec);
|
|||
|
targetm.dwarf_handle_frame_unspec (label, expr, XINT (src, 1));
|
|||
|
return;
|
|||
|
|
|||
|
/* Rule 16 */
|
|||
|
case AND:
|
|||
|
/* If this AND operation happens on stack pointer in prologue,
|
|||
|
we assume the stack is realigned and we extract the
|
|||
|
alignment. */
|
|||
|
if (fde && XEXP (src, 0) == stack_pointer_rtx)
|
|||
|
{
|
|||
|
/* We interpret reg_save differently with stack_realign set.
|
|||
|
Thus we must flush whatever we have queued first. */
|
|||
|
dwarf2out_flush_queued_reg_saves ();
|
|||
|
|
|||
|
gcc_assert (cfa_store.reg == REGNO (XEXP (src, 0)));
|
|||
|
fde->stack_realign = 1;
|
|||
|
fde->stack_realignment = INTVAL (XEXP (src, 1));
|
|||
|
cfa_store.offset = 0;
|
|||
|
|
|||
|
if (cfa.reg != STACK_POINTER_REGNUM
|
|||
|
&& cfa.reg != HARD_FRAME_POINTER_REGNUM)
|
|||
|
fde->drap_reg = cfa.reg;
|
|||
|
}
|
|||
|
return;
|
|||
|
|
|||
|
default:
|
|||
|
gcc_unreachable ();
|
|||
|
}
|
|||
|
|
|||
|
def_cfa_1 (label, &cfa);
|
|||
|
break;
|
|||
|
|
|||
|
case MEM:
|
|||
|
|
|||
|
/* Saving a register to the stack. Make sure dest is relative to the
|
|||
|
CFA register. */
|
|||
|
switch (GET_CODE (XEXP (dest, 0)))
|
|||
|
{
|
|||
|
/* Rule 10 */
|
|||
|
/* With a push. */
|
|||
|
case PRE_MODIFY:
|
|||
|
case POST_MODIFY:
|
|||
|
/* We can't handle variable size modifications. */
|
|||
|
gcc_assert (GET_CODE (XEXP (XEXP (XEXP (dest, 0), 1), 1))
|
|||
|
== CONST_INT);
|
|||
|
offset = -INTVAL (XEXP (XEXP (XEXP (dest, 0), 1), 1));
|
|||
|
|
|||
|
gcc_assert (REGNO (XEXP (XEXP (dest, 0), 0)) == STACK_POINTER_REGNUM
|
|||
|
&& cfa_store.reg == STACK_POINTER_REGNUM);
|
|||
|
|
|||
|
cfa_store.offset += offset;
|
|||
|
if (cfa.reg == STACK_POINTER_REGNUM)
|
|||
|
cfa.offset = cfa_store.offset;
|
|||
|
|
|||
|
if (GET_CODE (XEXP (dest, 0)) == POST_MODIFY)
|
|||
|
offset -= cfa_store.offset;
|
|||
|
else
|
|||
|
offset = -cfa_store.offset;
|
|||
|
break;
|
|||
|
|
|||
|
/* Rule 11 */
|
|||
|
case PRE_INC:
|
|||
|
case PRE_DEC:
|
|||
|
case POST_DEC:
|
|||
|
offset = GET_MODE_SIZE (GET_MODE (dest));
|
|||
|
if (GET_CODE (XEXP (dest, 0)) == PRE_INC)
|
|||
|
offset = -offset;
|
|||
|
|
|||
|
gcc_assert ((REGNO (XEXP (XEXP (dest, 0), 0))
|
|||
|
== STACK_POINTER_REGNUM)
|
|||
|
&& cfa_store.reg == STACK_POINTER_REGNUM);
|
|||
|
|
|||
|
cfa_store.offset += offset;
|
|||
|
|
|||
|
/* Rule 18: If stack is aligned, we will use FP as a
|
|||
|
reference to represent the address of the stored
|
|||
|
regiser. */
|
|||
|
if (fde
|
|||
|
&& fde->stack_realign
|
|||
|
&& src == hard_frame_pointer_rtx)
|
|||
|
{
|
|||
|
gcc_assert (cfa.reg != HARD_FRAME_POINTER_REGNUM);
|
|||
|
cfa_store.offset = 0;
|
|||
|
}
|
|||
|
|
|||
|
if (cfa.reg == STACK_POINTER_REGNUM)
|
|||
|
cfa.offset = cfa_store.offset;
|
|||
|
|
|||
|
if (GET_CODE (XEXP (dest, 0)) == POST_DEC)
|
|||
|
offset += -cfa_store.offset;
|
|||
|
else
|
|||
|
offset = -cfa_store.offset;
|
|||
|
break;
|
|||
|
|
|||
|
/* Rule 12 */
|
|||
|
/* With an offset. */
|
|||
|
case PLUS:
|
|||
|
case MINUS:
|
|||
|
case LO_SUM:
|
|||
|
{
|
|||
|
int regno;
|
|||
|
|
|||
|
gcc_assert (CONST_INT_P (XEXP (XEXP (dest, 0), 1))
|
|||
|
&& REG_P (XEXP (XEXP (dest, 0), 0)));
|
|||
|
offset = INTVAL (XEXP (XEXP (dest, 0), 1));
|
|||
|
if (GET_CODE (XEXP (dest, 0)) == MINUS)
|
|||
|
offset = -offset;
|
|||
|
|
|||
|
regno = REGNO (XEXP (XEXP (dest, 0), 0));
|
|||
|
|
|||
|
if (cfa.reg == (unsigned) regno)
|
|||
|
offset -= cfa.offset;
|
|||
|
else if (cfa_store.reg == (unsigned) regno)
|
|||
|
offset -= cfa_store.offset;
|
|||
|
else
|
|||
|
{
|
|||
|
gcc_assert (cfa_temp.reg == (unsigned) regno);
|
|||
|
offset -= cfa_temp.offset;
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
/* Rule 13 */
|
|||
|
/* Without an offset. */
|
|||
|
case REG:
|
|||
|
{
|
|||
|
int regno = REGNO (XEXP (dest, 0));
|
|||
|
|
|||
|
if (cfa.reg == (unsigned) regno)
|
|||
|
offset = -cfa.offset;
|
|||
|
else if (cfa_store.reg == (unsigned) regno)
|
|||
|
offset = -cfa_store.offset;
|
|||
|
else
|
|||
|
{
|
|||
|
gcc_assert (cfa_temp.reg == (unsigned) regno);
|
|||
|
offset = -cfa_temp.offset;
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
/* Rule 14 */
|
|||
|
case POST_INC:
|
|||
|
gcc_assert (cfa_temp.reg
|
|||
|
== (unsigned) REGNO (XEXP (XEXP (dest, 0), 0)));
|
|||
|
offset = -cfa_temp.offset;
|
|||
|
cfa_temp.offset -= GET_MODE_SIZE (GET_MODE (dest));
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
gcc_unreachable ();
|
|||
|
}
|
|||
|
|
|||
|
/* Rule 17 */
|
|||
|
/* If the source operand of this MEM operation is not a
|
|||
|
register, basically the source is return address. Here
|
|||
|
we only care how much stack grew and we don't save it. */
|
|||
|
if (!REG_P (src))
|
|||
|
break;
|
|||
|
|
|||
|
if (REGNO (src) != STACK_POINTER_REGNUM
|
|||
|
&& REGNO (src) != HARD_FRAME_POINTER_REGNUM
|
|||
|
&& (unsigned) REGNO (src) == cfa.reg)
|
|||
|
{
|
|||
|
/* We're storing the current CFA reg into the stack. */
|
|||
|
|
|||
|
if (cfa.offset == 0)
|
|||
|
{
|
|||
|
/* Rule 19 */
|
|||
|
/* If stack is aligned, putting CFA reg into stack means
|
|||
|
we can no longer use reg + offset to represent CFA.
|
|||
|
Here we use DW_CFA_def_cfa_expression instead. The
|
|||
|
result of this expression equals to the original CFA
|
|||
|
value. */
|
|||
|
if (fde
|
|||
|
&& fde->stack_realign
|
|||
|
&& cfa.indirect == 0
|
|||
|
&& cfa.reg != HARD_FRAME_POINTER_REGNUM)
|
|||
|
{
|
|||
|
dw_cfa_location cfa_exp;
|
|||
|
|
|||
|
gcc_assert (fde->drap_reg == cfa.reg);
|
|||
|
|
|||
|
cfa_exp.indirect = 1;
|
|||
|
cfa_exp.reg = HARD_FRAME_POINTER_REGNUM;
|
|||
|
cfa_exp.base_offset = offset;
|
|||
|
cfa_exp.offset = 0;
|
|||
|
|
|||
|
fde->drap_reg_saved = 1;
|
|||
|
|
|||
|
def_cfa_1 (label, &cfa_exp);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
/* If the source register is exactly the CFA, assume
|
|||
|
we're saving SP like any other register; this happens
|
|||
|
on the ARM. */
|
|||
|
def_cfa_1 (label, &cfa);
|
|||
|
queue_reg_save (label, stack_pointer_rtx, NULL_RTX, offset);
|
|||
|
break;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* Otherwise, we'll need to look in the stack to
|
|||
|
calculate the CFA. */
|
|||
|
rtx x = XEXP (dest, 0);
|
|||
|
|
|||
|
if (!REG_P (x))
|
|||
|
x = XEXP (x, 0);
|
|||
|
gcc_assert (REG_P (x));
|
|||
|
|
|||
|
cfa.reg = REGNO (x);
|
|||
|
cfa.base_offset = offset;
|
|||
|
cfa.indirect = 1;
|
|||
|
def_cfa_1 (label, &cfa);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
def_cfa_1 (label, &cfa);
|
|||
|
{
|
|||
|
span = targetm.dwarf_register_span (src);
|
|||
|
|
|||
|
if (!span)
|
|||
|
queue_reg_save (label, src, NULL_RTX, offset);
|
|||
|
else
|
|||
|
{
|
|||
|
/* We have a PARALLEL describing where the contents of SRC
|
|||
|
live. Queue register saves for each piece of the
|
|||
|
PARALLEL. */
|
|||
|
int par_index;
|
|||
|
int limit;
|
|||
|
HOST_WIDE_INT span_offset = offset;
|
|||
|
|
|||
|
gcc_assert (GET_CODE (span) == PARALLEL);
|
|||
|
|
|||
|
limit = XVECLEN (span, 0);
|
|||
|
for (par_index = 0; par_index < limit; par_index++)
|
|||
|
{
|
|||
|
rtx elem = XVECEXP (span, 0, par_index);
|
|||
|
|
|||
|
queue_reg_save (label, elem, NULL_RTX, span_offset);
|
|||
|
span_offset += GET_MODE_SIZE (GET_MODE (elem));
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
gcc_unreachable ();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Record call frame debugging information for INSN, which either
|
|||
|
sets SP or FP (adjusting how we calculate the frame address) or saves a
|
|||
|
register to the stack. If INSN is NULL_RTX, initialize our state.
|
|||
|
|
|||
|
If AFTER_P is false, we're being called before the insn is emitted,
|
|||
|
otherwise after. Call instructions get invoked twice. */
|
|||
|
|
|||
|
void
|
|||
|
dwarf2out_frame_debug (rtx insn, bool after_p)
|
|||
|
{
|
|||
|
const char *label;
|
|||
|
rtx note, n;
|
|||
|
bool handled_one = false;
|
|||
|
bool need_flush = false;
|
|||
|
|
|||
|
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
|
|||
|
matter if the stack pointer is not the CFA register anymore but
|
|||
|
is still used to save registers. */
|
|||
|
if (!ACCUMULATE_OUTGOING_ARGS)
|
|||
|
dwarf2out_notice_stack_adjust (insn, after_p);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
label = dwarf2out_cfi_label (false);
|
|||
|
any_cfis_emitted = false;
|
|||
|
|
|||
|
for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
|
|||
|
switch (REG_NOTE_KIND (note))
|
|||
|
{
|
|||
|
case REG_FRAME_RELATED_EXPR:
|
|||
|
insn = XEXP (note, 0);
|
|||
|
goto do_frame_expr;
|
|||
|
|
|||
|
case REG_CFA_DEF_CFA:
|
|||
|
dwarf2out_frame_debug_def_cfa (XEXP (note, 0), label);
|
|||
|
handled_one = true;
|
|||
|
break;
|
|||
|
|
|||
|
case REG_CFA_ADJUST_CFA:
|
|||
|
n = XEXP (note, 0);
|
|||
|
if (n == NULL)
|
|||
|
{
|
|||
|
n = PATTERN (insn);
|
|||
|
if (GET_CODE (n) == PARALLEL)
|
|||
|
n = XVECEXP (n, 0, 0);
|
|||
|
}
|
|||
|
dwarf2out_frame_debug_adjust_cfa (n, label);
|
|||
|
handled_one = true;
|
|||
|
break;
|
|||
|
|
|||
|
case REG_CFA_OFFSET:
|
|||
|
n = XEXP (note, 0);
|
|||
|
if (n == NULL)
|
|||
|
n = single_set (insn);
|
|||
|
dwarf2out_frame_debug_cfa_offset (n, label);
|
|||
|
handled_one = true;
|
|||
|
break;
|
|||
|
|
|||
|
case REG_CFA_REGISTER:
|
|||
|
n = XEXP (note, 0);
|
|||
|
if (n == NULL)
|
|||
|
{
|
|||
|
n = PATTERN (insn);
|
|||
|
if (GET_CODE (n) == PARALLEL)
|
|||
|
n = XVECEXP (n, 0, 0);
|
|||
|
}
|
|||
|
dwarf2out_frame_debug_cfa_register (n, label);
|
|||
|
handled_one = true;
|
|||
|
break;
|
|||
|
|
|||
|
case REG_CFA_EXPRESSION:
|
|||
|
n = XEXP (note, 0);
|
|||
|
if (n == NULL)
|
|||
|
n = single_set (insn);
|
|||
|
dwarf2out_frame_debug_cfa_expression (n, label);
|
|||
|
handled_one = true;
|
|||
|
break;
|
|||
|
|
|||
|
case REG_CFA_RESTORE:
|
|||
|
n = XEXP (note, 0);
|
|||
|
if (n == NULL)
|
|||
|
{
|
|||
|
n = PATTERN (insn);
|
|||
|
if (GET_CODE (n) == PARALLEL)
|
|||
|
n = XVECEXP (n, 0, 0);
|
|||
|
n = XEXP (n, 0);
|
|||
|
}
|
|||
|
dwarf2out_frame_debug_cfa_restore (n, label);
|
|||
|
handled_one = true;
|
|||
|
break;
|
|||
|
|
|||
|
case REG_CFA_SET_VDRAP:
|
|||
|
n = XEXP (note, 0);
|
|||
|
if (REG_P (n))
|
|||
|
{
|
|||
|
dw_fde_ref fde = current_fde ();
|
|||
|
if (fde)
|
|||
|
{
|
|||
|
gcc_assert (fde->vdrap_reg == INVALID_REGNUM);
|
|||
|
if (REG_P (n))
|
|||
|
fde->vdrap_reg = REGNO (n);
|
|||
|
}
|
|||
|
}
|
|||
|
handled_one = true;
|
|||
|
break;
|
|||
|
|
|||
|
case REG_CFA_WINDOW_SAVE:
|
|||
|
dwarf2out_frame_debug_cfa_window_save (label);
|
|||
|
handled_one = true;
|
|||
|
break;
|
|||
|
|
|||
|
case REG_CFA_FLUSH_QUEUE:
|
|||
|
/* The actual flush happens below. */
|
|||
|
need_flush = true;
|
|||
|
handled_one = true;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (handled_one)
|
|||
|
{
|
|||
|
/* Minimize the number of advances by emitting the entire queue
|
|||
|
once anything is emitted. */
|
|||
|
need_flush |= any_cfis_emitted;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
insn = PATTERN (insn);
|
|||
|
do_frame_expr:
|
|||
|
dwarf2out_frame_debug_expr (insn, label);
|
|||
|
|
|||
|
/* Check again. A parallel can save and update the same register.
|
|||
|
We could probably check just once, here, but this is safer than
|
|||
|
removing the check at the start of the function. */
|
|||
|
if (any_cfis_emitted || clobbers_queued_reg_save (insn))
|
|||
|
need_flush = true;
|
|||
|
}
|
|||
|
|
|||
|
if (need_flush)
|
|||
|
dwarf2out_flush_queued_reg_saves ();
|
|||
|
}
|
|||
|
|
|||
|
/* Called once at the start of final to initialize some data for the
|
|||
|
current function. */
|
|||
|
void
|
|||
|
dwarf2out_frame_debug_init (void)
|
|||
|
{
|
|||
|
/* Flush any queued register saves. */
|
|||
|
dwarf2out_flush_queued_reg_saves ();
|
|||
|
|
|||
|
/* Set up state for generating call frame debug info. */
|
|||
|
lookup_cfa (&cfa);
|
|||
|
gcc_assert (cfa.reg
|
|||
|
== (unsigned long)DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM));
|
|||
|
|
|||
|
cfa.reg = STACK_POINTER_REGNUM;
|
|||
|
cfa_store = cfa;
|
|||
|
cfa_temp.reg = -1;
|
|||
|
cfa_temp.offset = 0;
|
|||
|
|
|||
|
regs_saved_in_regs = NULL;
|
|||
|
|
|||
|
if (barrier_args_size)
|
|||
|
{
|
|||
|
XDELETEVEC (barrier_args_size);
|
|||
|
barrier_args_size = 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. */
|
|||
|
|
|||
|
void
|
|||
|
dwarf2out_cfi_begin_epilogue (rtx insn)
|
|||
|
{
|
|||
|
bool saw_frp = false;
|
|||
|
rtx i;
|
|||
|
|
|||
|
/* Scan forward to the return insn, noticing if there are possible
|
|||
|
frame related insns. */
|
|||
|
for (i = NEXT_INSN (insn); i ; i = NEXT_INSN (i))
|
|||
|
{
|
|||
|
if (!INSN_P (i))
|
|||
|
continue;
|
|||
|
|
|||
|
/* Look for both regular and sibcalls to end the block. */
|
|||
|
if (returnjump_p (i))
|
|||
|
break;
|
|||
|
if (CALL_P (i) && SIBLING_CALL_P (i))
|
|||
|
break;
|
|||
|
|
|||
|
if (GET_CODE (PATTERN (i)) == SEQUENCE)
|
|||
|
{
|
|||
|
int idx;
|
|||
|
rtx seq = PATTERN (i);
|
|||
|
|
|||
|
if (returnjump_p (XVECEXP (seq, 0, 0)))
|
|||
|
break;
|
|||
|
if (CALL_P (XVECEXP (seq, 0, 0))
|
|||
|
&& SIBLING_CALL_P (XVECEXP (seq, 0, 0)))
|
|||
|
break;
|
|||
|
|
|||
|
for (idx = 0; idx < XVECLEN (seq, 0); idx++)
|
|||
|
if (RTX_FRAME_RELATED_P (XVECEXP (seq, 0, idx)))
|
|||
|
saw_frp = true;
|
|||
|
}
|
|||
|
|
|||
|
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 (!cfa_remember.in_use);
|
|||
|
cfa_remember = cfa;
|
|||
|
cfa_remember.in_use = 1;
|
|||
|
}
|
|||
|
|
|||
|
/* A "subroutine" of dwarf2out_cfi_begin_epilogue. Emit the restore
|
|||
|
required. */
|
|||
|
|
|||
|
void
|
|||
|
dwarf2out_frame_debug_restore_state (void)
|
|||
|
{
|
|||
|
dw_cfi_ref cfi = new_cfi ();
|
|||
|
const char *label = dwarf2out_cfi_label (false);
|
|||
|
|
|||
|
cfi->dw_cfi_opc = DW_CFA_restore_state;
|
|||
|
add_fde_cfi (label, cfi);
|
|||
|
|
|||
|
gcc_assert (cfa_remember.in_use);
|
|||
|
cfa = cfa_remember;
|
|||
|
cfa_remember.in_use = 0;
|
|||
|
}
|
|||
|
|
|||
|
/* Run once per function. */
|
|||
|
|
|||
|
void
|
|||
|
dwarf2cfi_function_init (void)
|
|||
|
{
|
|||
|
args_size = old_args_size = 0;
|
|||
|
}
|
|||
|
|
|||
|
/* Run once. */
|
|||
|
|
|||
|
void
|
|||
|
dwarf2cfi_frame_init (void)
|
|||
|
{
|
|||
|
dw_cfa_location loc;
|
|||
|
|
|||
|
/* Generate the CFA instructions common to all FDE's. Do it now for the
|
|||
|
sake of lookup_cfa. */
|
|||
|
|
|||
|
/* On entry, the Canonical Frame Address is at SP. */
|
|||
|
memset(&loc, 0, sizeof (loc));
|
|||
|
loc.reg = STACK_POINTER_REGNUM;
|
|||
|
loc.offset = INCOMING_FRAME_SP_OFFSET;
|
|||
|
def_cfa_1 (NULL, &loc);
|
|||
|
|
|||
|
if (targetm.debug_unwind_info () == UI_DWARF2
|
|||
|
|| targetm_common.except_unwind_info (&global_options) == UI_DWARF2)
|
|||
|
initial_return_save (INCOMING_RETURN_ADDR_RTX);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* 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. */
|
|||
|
|
|||
|
int
|
|||
|
dwarf2out_do_frame (void)
|
|||
|
{
|
|||
|
/* We want to emit correct CFA location expressions or lists, so we
|
|||
|
have to return true if we're going to output debug info, even if
|
|||
|
we're not going to output frame or unwind info. */
|
|||
|
if (write_symbols == DWARF2_DEBUG || write_symbols == VMS_AND_DWARF2_DEBUG)
|
|||
|
return true;
|
|||
|
|
|||
|
if (saved_do_cfi_asm)
|
|||
|
return true;
|
|||
|
|
|||
|
if (targetm.debug_unwind_info () == UI_DWARF2)
|
|||
|
return true;
|
|||
|
|
|||
|
if ((flag_unwind_tables || flag_exceptions)
|
|||
|
&& targetm_common.except_unwind_info (&global_options) == UI_DWARF2)
|
|||
|
return true;
|
|||
|
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* Decide whether to emit frame unwind via assembler directives. */
|
|||
|
|
|||
|
int
|
|||
|
dwarf2out_do_cfi_asm (void)
|
|||
|
{
|
|||
|
int enc;
|
|||
|
|
|||
|
#ifdef MIPS_DEBUGGING_INFO
|
|||
|
return false;
|
|||
|
#endif
|
|||
|
if (saved_do_cfi_asm)
|
|||
|
return true;
|
|||
|
if (!flag_dwarf2_cfi_asm || !dwarf2out_do_frame ())
|
|||
|
return false;
|
|||
|
if (!HAVE_GAS_CFI_PERSONALITY_DIRECTIVE)
|
|||
|
return false;
|
|||
|
|
|||
|
/* Make sure the personality encoding is one the assembler can support.
|
|||
|
In particular, aligned addresses can't be handled. */
|
|||
|
enc = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/2,/*global=*/1);
|
|||
|
if ((enc & 0x70) != 0 && (enc & 0x70) != DW_EH_PE_pcrel)
|
|||
|
return false;
|
|||
|
enc = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0,/*global=*/0);
|
|||
|
if ((enc & 0x70) != 0 && (enc & 0x70) != DW_EH_PE_pcrel)
|
|||
|
return false;
|
|||
|
|
|||
|
/* If we can't get the assembler to emit only .debug_frame, and we don't need
|
|||
|
dwarf2 unwind info for exceptions, then emit .debug_frame by hand. */
|
|||
|
if (!HAVE_GAS_CFI_SECTIONS_DIRECTIVE
|
|||
|
&& !flag_unwind_tables && !flag_exceptions
|
|||
|
&& targetm_common.except_unwind_info (&global_options) != UI_DWARF2)
|
|||
|
return false;
|
|||
|
|
|||
|
saved_do_cfi_asm = true;
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
#include "gt-dwarf2cfi.h"
|