| /* Output Dwarf2 format symbol table information from GCC. |
| Copyright (C) 1992, 1993, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, |
| 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. |
| Contributed by Gary Funck (gary@intrepid.com). |
| Derived from DWARF 1 implementation of Ron Guilmette (rfg@monkeys.com). |
| Extensively modified by Jason Merrill (jason@cygnus.com). |
| |
| 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/>. */ |
| |
| /* TODO: Emit .debug_line header even when there are no functions, since |
| the file numbers are used by .debug_info. Alternately, leave |
| out locations for types and decls. |
| Avoid talking about ctors and op= for PODs. |
| Factor out common prologue sequences into multiple CIEs. */ |
| |
| /* The first part of this file deals with the DWARF 2 frame unwind |
| information, which is also used by the GCC efficient exception handling |
| mechanism. The second part, controlled only by an #ifdef |
| DWARF2_DEBUGGING_INFO, deals with the other DWARF 2 debugging |
| information. */ |
| |
| /* DWARF2 Abbreviation Glossary: |
| |
| CFA = Canonical Frame Address |
| a fixed address on the stack which identifies a call frame. |
| We define it to be the value of SP just before the call insn. |
| The CFA register and offset, which may change during the course |
| of the function, are used to calculate its value at runtime. |
| |
| CFI = Call Frame Instruction |
| an instruction for the DWARF2 abstract machine |
| |
| CIE = Common Information Entry |
| information describing information common to one or more FDEs |
| |
| DIE = Debugging Information Entry |
| |
| FDE = Frame Description Entry |
| information describing the stack call frame, in particular, |
| how to restore registers |
| |
| DW_CFA_... = DWARF2 CFA call frame instruction |
| DW_TAG_... = DWARF2 DIE tag */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tm.h" |
| #include "tree.h" |
| #include "version.h" |
| #include "flags.h" |
| #include "real.h" |
| #include "rtl.h" |
| #include "hard-reg-set.h" |
| #include "regs.h" |
| #include "insn-config.h" |
| #include "reload.h" |
| #include "function.h" |
| #include "output.h" |
| #include "expr.h" |
| #include "libfuncs.h" |
| #include "except.h" |
| #include "dwarf2.h" |
| #include "dwarf2out.h" |
| #include "dwarf2asm.h" |
| #include "toplev.h" |
| #include "varray.h" |
| #include "ggc.h" |
| #include "md5.h" |
| #include "tm_p.h" |
| #include "diagnostic.h" |
| #include "debug.h" |
| #include "target.h" |
| #include "langhooks.h" |
| #include "hashtab.h" |
| #include "cgraph.h" |
| #include "input.h" |
| |
| #ifdef DWARF2_DEBUGGING_INFO |
| static void dwarf2out_source_line (unsigned int, const char *); |
| #endif |
| |
| #ifndef DWARF2_FRAME_INFO |
| # ifdef DWARF2_DEBUGGING_INFO |
| # define DWARF2_FRAME_INFO \ |
| (write_symbols == DWARF2_DEBUG || write_symbols == VMS_AND_DWARF2_DEBUG) |
| # else |
| # define DWARF2_FRAME_INFO 0 |
| # endif |
| #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 |
| |
| /* 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. */ |
| return (write_symbols == DWARF2_DEBUG |
| || write_symbols == VMS_AND_DWARF2_DEBUG |
| || DWARF2_FRAME_INFO || saved_do_cfi_asm |
| #ifdef DWARF2_UNWIND_INFO |
| || (DWARF2_UNWIND_INFO |
| && (flag_unwind_tables |
| || (flag_exceptions && ! USING_SJLJ_EXCEPTIONS))) |
| #endif |
| ); |
| } |
| |
| /* 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 (!flag_dwarf2_cfi_asm || !dwarf2out_do_frame ()) |
| return false; |
| if (saved_do_cfi_asm || !eh_personality_libfunc) |
| return true; |
| 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; |
| |
| saved_do_cfi_asm = true; |
| return true; |
| } |
| |
| /* The size of the target's pointer type. */ |
| #ifndef PTR_SIZE |
| #define PTR_SIZE (POINTER_SIZE / BITS_PER_UNIT) |
| #endif |
| |
| /* Array of RTXes referenced by the debugging information, which therefore |
| must be kept around forever. */ |
| static GTY(()) VEC(rtx,gc) *used_rtx_array; |
| |
| /* A pointer to the base of a list of incomplete types which might be |
| completed at some later time. incomplete_types_list needs to be a |
| VEC(tree,gc) because we want to tell the garbage collector about |
| it. */ |
| static GTY(()) VEC(tree,gc) *incomplete_types; |
| |
| /* A pointer to the base of a table of references to declaration |
| scopes. This table is a display which tracks the nesting |
| of declaration scopes at the current scope and containing |
| scopes. This table is used to find the proper place to |
| define type declaration DIE's. */ |
| static GTY(()) VEC(tree,gc) *decl_scope_table; |
| |
| /* Pointers to various DWARF2 sections. */ |
| static GTY(()) section *debug_info_section; |
| static GTY(()) section *debug_abbrev_section; |
| static GTY(()) section *debug_aranges_section; |
| static GTY(()) section *debug_macinfo_section; |
| static GTY(()) section *debug_line_section; |
| static GTY(()) section *debug_loc_section; |
| static GTY(()) section *debug_pubnames_section; |
| static GTY(()) section *debug_pubtypes_section; |
| static GTY(()) section *debug_str_section; |
| static GTY(()) section *debug_ranges_section; |
| static GTY(()) section *debug_frame_section; |
| |
| /* How to start an assembler comment. */ |
| #ifndef ASM_COMMENT_START |
| #define ASM_COMMENT_START ";#" |
| #endif |
| |
| typedef struct dw_cfi_struct *dw_cfi_ref; |
| typedef struct dw_fde_struct *dw_fde_ref; |
| typedef union dw_cfi_oprnd_struct *dw_cfi_oprnd_ref; |
| |
| /* Call frames are described using a sequence of Call Frame |
| Information instructions. The register number, offset |
| and address fields are provided as possible operands; |
| their use is selected by the opcode field. */ |
| |
| enum dw_cfi_oprnd_type { |
| dw_cfi_oprnd_unused, |
| dw_cfi_oprnd_reg_num, |
| dw_cfi_oprnd_offset, |
| dw_cfi_oprnd_addr, |
| dw_cfi_oprnd_loc |
| }; |
| |
| typedef union dw_cfi_oprnd_struct GTY(()) |
| { |
| unsigned int GTY ((tag ("dw_cfi_oprnd_reg_num"))) dw_cfi_reg_num; |
| HOST_WIDE_INT GTY ((tag ("dw_cfi_oprnd_offset"))) dw_cfi_offset; |
| const char * GTY ((tag ("dw_cfi_oprnd_addr"))) dw_cfi_addr; |
| struct dw_loc_descr_struct * GTY ((tag ("dw_cfi_oprnd_loc"))) dw_cfi_loc; |
| } |
| dw_cfi_oprnd; |
| |
| typedef struct dw_cfi_struct GTY(()) |
| { |
| dw_cfi_ref dw_cfi_next; |
| enum dwarf_call_frame_info dw_cfi_opc; |
| dw_cfi_oprnd GTY ((desc ("dw_cfi_oprnd1_desc (%1.dw_cfi_opc)"))) |
| dw_cfi_oprnd1; |
| dw_cfi_oprnd GTY ((desc ("dw_cfi_oprnd2_desc (%1.dw_cfi_opc)"))) |
| dw_cfi_oprnd2; |
| } |
| dw_cfi_node; |
| |
| /* This is how we define the location of the CFA. We use to handle it |
| as REG + OFFSET all the time, but now it can be more complex. |
| It can now be either REG + CFA_OFFSET or *(REG + BASE_OFFSET) + CFA_OFFSET. |
| Instead of passing around REG and OFFSET, we pass a copy |
| of this structure. */ |
| typedef struct cfa_loc GTY(()) |
| { |
| HOST_WIDE_INT offset; |
| HOST_WIDE_INT base_offset; |
| unsigned int reg; |
| int indirect; /* 1 if CFA is accessed via a dereference. */ |
| } dw_cfa_location; |
| |
| /* All call frame descriptions (FDE's) in the GCC generated DWARF |
| refer to a single Common Information Entry (CIE), defined at |
| the beginning of the .debug_frame section. This use of a single |
| CIE obviates the need to keep track of multiple CIE's |
| in the DWARF generation routines below. */ |
| |
| typedef struct dw_fde_struct GTY(()) |
| { |
| tree decl; |
| const char *dw_fde_begin; |
| const char *dw_fde_current_label; |
| const char *dw_fde_end; |
| const char *dw_fde_hot_section_label; |
| const char *dw_fde_hot_section_end_label; |
| const char *dw_fde_unlikely_section_label; |
| const char *dw_fde_unlikely_section_end_label; |
| bool dw_fde_switched_sections; |
| dw_cfi_ref dw_fde_cfi; |
| unsigned funcdef_number; |
| HOST_WIDE_INT stack_realignment; |
| /* Dynamic realign argument pointer register. */ |
| unsigned int drap_reg; |
| /* Virtual dynamic realign argument pointer register. */ |
| unsigned int vdrap_reg; |
| unsigned all_throwers_are_sibcalls : 1; |
| unsigned nothrow : 1; |
| unsigned uses_eh_lsda : 1; |
| /* Whether we did stack realign in this call frame. */ |
| unsigned stack_realign : 1; |
| /* Whether dynamic realign argument pointer register has been saved. */ |
| unsigned drap_reg_saved: 1; |
| } |
| dw_fde_node; |
| |
| /* 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 |
| |
| #define DWARF_VERSION 2 |
| |
| /* 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 |
| |
| /* A pointer to the base of a table that contains frame description |
| information for each routine. */ |
| static GTY((length ("fde_table_allocated"))) dw_fde_ref fde_table; |
| |
| /* Number of elements currently allocated for fde_table. */ |
| static GTY(()) unsigned fde_table_allocated; |
| |
| /* Number of elements in fde_table currently in use. */ |
| static GTY(()) unsigned fde_table_in_use; |
| |
| /* Size (in elements) of increments by which we may expand the |
| fde_table. */ |
| #define FDE_TABLE_INCREMENT 256 |
| |
| /* Get the current fde_table entry we should use. */ |
| |
| static inline dw_fde_ref |
| current_fde (void) |
| { |
| return fde_table_in_use ? &fde_table[fde_table_in_use - 1] : NULL; |
| } |
| |
| /* A list of call frame insns for the CIE. */ |
| static GTY(()) dw_cfi_ref cie_cfi_head; |
| |
| #if defined (DWARF2_DEBUGGING_INFO) || defined (DWARF2_UNWIND_INFO) |
| /* Some DWARF extensions (e.g., MIPS/SGI) implement a subprogram |
| attribute that accelerates the lookup of the FDE associated |
| with the subprogram. This variable holds the table index of the FDE |
| associated with the current function (body) definition. */ |
| static unsigned current_funcdef_fde; |
| #endif |
| |
| struct indirect_string_node GTY(()) |
| { |
| const char *str; |
| unsigned int refcount; |
| unsigned int form; |
| char *label; |
| }; |
| |
| static GTY ((param_is (struct indirect_string_node))) htab_t debug_str_hash; |
| |
| static GTY(()) int dw2_string_counter; |
| static GTY(()) unsigned long dwarf2out_cfi_label_num; |
| |
| /* True if the compilation unit places functions in more than one section. */ |
| static GTY(()) bool have_multiple_function_sections = false; |
| |
| /* Whether the default text and cold text sections have been used at all. */ |
| |
| static GTY(()) bool text_section_used = false; |
| static GTY(()) bool cold_text_section_used = false; |
| |
| /* The default cold text section. */ |
| static GTY(()) section *cold_text_section; |
| |
| #if defined (DWARF2_DEBUGGING_INFO) || defined (DWARF2_UNWIND_INFO) |
| |
| /* Forward declarations for functions defined in this file. */ |
| |
| static char *stripattributes (const char *); |
| static const char *dwarf_cfi_name (unsigned); |
| static dw_cfi_ref new_cfi (void); |
| static void add_cfi (dw_cfi_ref *, dw_cfi_ref); |
| static void add_fde_cfi (const char *, dw_cfi_ref); |
| static void lookup_cfa_1 (dw_cfi_ref, dw_cfa_location *); |
| static void lookup_cfa (dw_cfa_location *); |
| static void reg_save (const char *, unsigned, unsigned, HOST_WIDE_INT); |
| #ifdef DWARF2_UNWIND_INFO |
| static void initial_return_save (rtx); |
| #endif |
| 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); |
| static void dwarf2out_note_section_used (void); |
| static void dwarf2out_stack_adjust (rtx, bool); |
| static void dwarf2out_args_size_adjust (HOST_WIDE_INT, const char *); |
| static void flush_queued_reg_saves (void); |
| static bool clobbers_queued_reg_save (const_rtx); |
| static void dwarf2out_frame_debug_expr (rtx, const char *); |
| |
| /* Support for complex CFA locations. */ |
| static void output_cfa_loc (dw_cfi_ref); |
| static void output_cfa_loc_raw (dw_cfi_ref); |
| static void get_cfa_from_loc_descr (dw_cfa_location *, |
| struct dw_loc_descr_struct *); |
| static struct dw_loc_descr_struct *build_cfa_loc |
| (dw_cfa_location *, HOST_WIDE_INT); |
| static struct dw_loc_descr_struct *build_cfa_aligned_loc |
| (HOST_WIDE_INT, HOST_WIDE_INT); |
| static void def_cfa_1 (const char *, dw_cfa_location *); |
| |
| /* How to start an assembler comment. */ |
| #ifndef ASM_COMMENT_START |
| #define ASM_COMMENT_START ";#" |
| #endif |
| |
| /* Data and reference forms for relocatable data. */ |
| #define DW_FORM_data (DWARF_OFFSET_SIZE == 8 ? DW_FORM_data8 : DW_FORM_data4) |
| #define DW_FORM_ref (DWARF_OFFSET_SIZE == 8 ? DW_FORM_ref8 : DW_FORM_ref4) |
| |
| #ifndef DEBUG_FRAME_SECTION |
| #define DEBUG_FRAME_SECTION ".debug_frame" |
| #endif |
| |
| #ifndef FUNC_BEGIN_LABEL |
| #define FUNC_BEGIN_LABEL "LFB" |
| #endif |
| |
| #ifndef FUNC_END_LABEL |
| #define FUNC_END_LABEL "LFE" |
| #endif |
| |
| #ifndef FRAME_BEGIN_LABEL |
| #define FRAME_BEGIN_LABEL "Lframe" |
| #endif |
| #define CIE_AFTER_SIZE_LABEL "LSCIE" |
| #define CIE_END_LABEL "LECIE" |
| #define FDE_LABEL "LSFDE" |
| #define FDE_AFTER_SIZE_LABEL "LASFDE" |
| #define FDE_END_LABEL "LEFDE" |
| #define LINE_NUMBER_BEGIN_LABEL "LSLT" |
| #define LINE_NUMBER_END_LABEL "LELT" |
| #define LN_PROLOG_AS_LABEL "LASLTP" |
| #define LN_PROLOG_END_LABEL "LELTP" |
| #define DIE_LABEL_PREFIX "DW" |
| |
| /* 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 |
| |
| /* 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)); |
| } |
| |
| /* Return a pointer to a copy of the section string name S with all |
| attributes stripped off, and an asterisk prepended (for assemble_name). */ |
| |
| static inline char * |
| stripattributes (const char *s) |
| { |
| char *stripped = XNEWVEC (char, strlen (s) + 2); |
| char *p = stripped; |
| |
| *p++ = '*'; |
| |
| while (*s && *s != ',') |
| *p++ = *s++; |
| |
| *p = '\0'; |
| return stripped; |
| } |
| |
| /* 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); |
| } |
| |
| /* Convert a DWARF call frame info. operation to its string name */ |
| |
| static const char * |
| dwarf_cfi_name (unsigned int cfi_opc) |
| { |
| switch (cfi_opc) |
| { |
| case DW_CFA_advance_loc: |
| return "DW_CFA_advance_loc"; |
| case DW_CFA_offset: |
| return "DW_CFA_offset"; |
| case DW_CFA_restore: |
| return "DW_CFA_restore"; |
| case DW_CFA_nop: |
| return "DW_CFA_nop"; |
| case DW_CFA_set_loc: |
| return "DW_CFA_set_loc"; |
| case DW_CFA_advance_loc1: |
| return "DW_CFA_advance_loc1"; |
| case DW_CFA_advance_loc2: |
| return "DW_CFA_advance_loc2"; |
| case DW_CFA_advance_loc4: |
| return "DW_CFA_advance_loc4"; |
| case DW_CFA_offset_extended: |
| return "DW_CFA_offset_extended"; |
| case DW_CFA_restore_extended: |
| return "DW_CFA_restore_extended"; |
| case DW_CFA_undefined: |
| return "DW_CFA_undefined"; |
| case DW_CFA_same_value: |
| return "DW_CFA_same_value"; |
| case DW_CFA_register: |
| return "DW_CFA_register"; |
| case DW_CFA_remember_state: |
| return "DW_CFA_remember_state"; |
| case DW_CFA_restore_state: |
| return "DW_CFA_restore_state"; |
| case DW_CFA_def_cfa: |
| return "DW_CFA_def_cfa"; |
| case DW_CFA_def_cfa_register: |
| return "DW_CFA_def_cfa_register"; |
| case DW_CFA_def_cfa_offset: |
| return "DW_CFA_def_cfa_offset"; |
| |
| /* DWARF 3 */ |
| case DW_CFA_def_cfa_expression: |
| return "DW_CFA_def_cfa_expression"; |
| case DW_CFA_expression: |
| return "DW_CFA_expression"; |
| case DW_CFA_offset_extended_sf: |
| return "DW_CFA_offset_extended_sf"; |
| case DW_CFA_def_cfa_sf: |
| return "DW_CFA_def_cfa_sf"; |
| case DW_CFA_def_cfa_offset_sf: |
| return "DW_CFA_def_cfa_offset_sf"; |
| |
| /* SGI/MIPS specific */ |
| case DW_CFA_MIPS_advance_loc8: |
| return "DW_CFA_MIPS_advance_loc8"; |
| |
| /* GNU extensions */ |
| case DW_CFA_GNU_window_save: |
| return "DW_CFA_GNU_window_save"; |
| case DW_CFA_GNU_args_size: |
| return "DW_CFA_GNU_args_size"; |
| case DW_CFA_GNU_negative_offset_extended: |
| return "DW_CFA_GNU_negative_offset_extended"; |
| |
| default: |
| return "DW_CFA_<unknown>"; |
| } |
| } |
| |
| /* Return a pointer to a newly allocated Call Frame Instruction. */ |
| |
| static inline dw_cfi_ref |
| new_cfi (void) |
| { |
| dw_cfi_ref cfi = GGC_NEW (dw_cfi_node); |
| |
| cfi->dw_cfi_next = NULL; |
| 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 (dw_cfi_ref *list_head, dw_cfi_ref cfi) |
| { |
| dw_cfi_ref *p; |
| 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. */ |
| if (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; |
| } |
| |
| /* Find the end of the chain. */ |
| for (p = list_head; (*p) != NULL; p = &(*p)->dw_cfi_next) |
| ; |
| |
| *p = 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. */ |
| |
| 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 |
| { |
| ASM_GENERATE_INTERNAL_LABEL (label, "LCFI", dwarf2out_cfi_label_num++); |
| ASM_OUTPUT_LABEL (asm_out_file, label); |
| } |
| |
| return label; |
| } |
| |
| /* 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) |
| { |
| dw_cfi_ref *list_head = &cie_cfi_head; |
| |
| 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. */ |
| 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 (write_symbols != DWARF2_DEBUG |
| && write_symbols != VMS_AND_DWARF2_DEBUG) |
| break; |
| if (debug_info_level <= DINFO_LEVEL_TERSE) |
| break; |
| |
| 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); |
| |
| list_head = &fde->dw_fde_cfi; |
| } |
| /* ??? 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; |
| } |
| |
| list_head = &fde->dw_fde_cfi; |
| } |
| |
| add_cfi (list_head, cfi); |
| } |
| |
| /* Subroutine of lookup_cfa. */ |
| |
| static void |
| lookup_cfa_1 (dw_cfi_ref cfi, dw_cfa_location *loc) |
| { |
| 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; |
| default: |
| break; |
| } |
| } |
| |
| /* Find the previous value for the CFA. */ |
| |
| static void |
| lookup_cfa (dw_cfa_location *loc) |
| { |
| dw_cfi_ref cfi; |
| dw_fde_ref fde; |
| |
| loc->reg = INVALID_REGNUM; |
| loc->offset = 0; |
| loc->indirect = 0; |
| loc->base_offset = 0; |
| |
| for (cfi = cie_cfi_head; cfi; cfi = cfi->dw_cfi_next) |
| lookup_cfa_1 (cfi, loc); |
| |
| fde = current_fde (); |
| if (fde) |
| for (cfi = fde->dw_fde_cfi; cfi; cfi = cfi->dw_cfi_next) |
| lookup_cfa_1 (cfi, loc); |
| } |
| |
| /* 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 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; |
| |
| /* Entry point to update the canonical frame address (CFA). |
| LABEL is passed to add_fde_cfi. The value of CFA is now to be |
| calculated from REG+OFFSET. */ |
| |
| void |
| dwarf2out_def_cfa (const char *label, unsigned int reg, HOST_WIDE_INT offset) |
| { |
| dw_cfa_location loc; |
| loc.indirect = 0; |
| loc.base_offset = 0; |
| loc.reg = reg; |
| loc.offset = offset; |
| def_cfa_1 (label, &loc); |
| } |
| |
| /* Determine if two dw_cfa_location structures define the same data. */ |
| |
| static 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) |
| { |
| /* 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) |
| { |
| /* 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_oprnd2.dw_cfi_reg_num = reg; |
| cfi->dw_cfi_oprnd1.dw_cfi_loc |
| = build_cfa_aligned_loc (offset, fde->stack_realignment); |
| } |
| else if (sreg == INVALID_REGNUM) |
| { |
| if (offset < 0) |
| 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); |
| } |
| |
| /* Add the CFI for saving a register window. LABEL is passed to reg_save. |
| This CFI tells the unwinder that it needs to restore the window registers |
| from the previous frame's window save area. |
| |
| ??? Perhaps we should note in the CIE where windows are saved (instead of |
| assuming 0(cfa)) and what registers are in the window. */ |
| |
| void |
| dwarf2out_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); |
| } |
| |
| /* Add a CFI to update the running total of the size of arguments |
| pushed onto the stack. */ |
| |
| 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); |
| } |
| |
| /* Entry point for saving a register to the stack. REG is the GCC register |
| number. LABEL and OFFSET are passed to reg_save. */ |
| |
| void |
| dwarf2out_reg_save (const char *label, unsigned int reg, HOST_WIDE_INT offset) |
| { |
| reg_save (label, DWARF_FRAME_REGNUM (reg), INVALID_REGNUM, offset); |
| } |
| |
| /* Entry point for saving the return address in the stack. |
| LABEL and OFFSET are passed to reg_save. */ |
| |
| void |
| dwarf2out_return_save (const char *label, HOST_WIDE_INT offset) |
| { |
| reg_save (label, DWARF_FRAME_RETURN_COLUMN, INVALID_REGNUM, offset); |
| } |
| |
| /* Entry point for saving the return address in a register. |
| LABEL and SREG are passed to reg_save. */ |
| |
| void |
| dwarf2out_return_reg (const char *label, unsigned int sreg) |
| { |
| reg_save (label, DWARF_FRAME_RETURN_COLUMN, DWARF_FRAME_REGNUM (sreg), 0); |
| } |
| |
| #ifdef DWARF2_UNWIND_INFO |
| /* 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 (GET_CODE (XEXP (rtl, 1)) == CONST_INT); |
| 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); |
| } |
| #endif |
| |
| /* 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 |
| || 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; |
| } |
| |
| 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 |
| && GET_CODE (val) == CONST_INT); |
| 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) |
| || sibcall_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); |
| } |
| |
| |
| /* 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_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) || 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) |
| { |
| 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_args_size_adjust (offset, label); |
| } |
| |
| /* Adjust args_size based on stack adjustment OFFSET. */ |
| |
| static void |
| dwarf2out_args_size_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; |
| |
| #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); |
| } |
| |
| #endif |
| |
| /* 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 queued_reg_save GTY(()) |
| { |
| 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. */ |
| struct reg_saved_in_data GTY(()) { |
| rtx orig_reg; |
| rtx saved_in_reg; |
| }; |
| |
| /* A list of registers saved in other registers. |
| The list intentionally has a small maximum capacity of 4; if your |
| port needs more than that, you might consider implementing a |
| more efficient data structure. */ |
| static GTY(()) struct reg_saved_in_data regs_saved_in_regs[4]; |
| static GTY(()) size_t num_regs_saved_in_regs; |
| |
| #if defined (DWARF2_DEBUGGING_INFO) || defined (DWARF2_UNWIND_INFO) |
| 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_NEW (struct 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 |
| flush_queued_reg_saves (void) |
| { |
| struct queued_reg_save *q; |
| |
| for (q = queued_reg_saves; q; q = q->next) |
| { |
| size_t i; |
| unsigned int reg, sreg; |
| |
| for (i = 0; i < num_regs_saved_in_regs; i++) |
| if (REGNO (regs_saved_in_regs[i].orig_reg) == REGNO (q->reg)) |
| break; |
| if (q->saved_reg && i == num_regs_saved_in_regs) |
| { |
| gcc_assert (i != ARRAY_SIZE (regs_saved_in_regs)); |
| num_regs_saved_in_regs++; |
| } |
| if (i != num_regs_saved_in_regs) |
| { |
| regs_saved_in_regs[i].orig_reg = q->reg; |
| regs_saved_in_regs[i].saved_in_reg = q->saved_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; |
| if (modified_in_p (q->reg, insn)) |
| return true; |
| for (i = 0; i < num_regs_saved_in_regs; i++) |
| if (REGNO (q->reg) == REGNO (regs_saved_in_regs[i].orig_reg) |
| && modified_in_p (regs_saved_in_regs[i].saved_in_reg, insn)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* Entry point for saving the first register into the second. */ |
| |
| void |
| dwarf2out_reg_save_reg (const char *label, rtx reg, rtx sreg) |
| { |
| size_t i; |
| unsigned int regno, sregno; |
| |
| for (i = 0; i < num_regs_saved_in_regs; i++) |
| if (REGNO (regs_saved_in_regs[i].orig_reg) == REGNO (reg)) |
| break; |
| if (i == num_regs_saved_in_regs) |
| { |
| gcc_assert (i != ARRAY_SIZE (regs_saved_in_regs)); |
| num_regs_saved_in_regs++; |
| } |
| regs_saved_in_regs[i].orig_reg = reg; |
| regs_saved_in_regs[i].saved_in_reg = sreg; |
| |
| regno = DWARF_FRAME_REGNUM (REGNO (reg)); |
| sregno = DWARF_FRAME_REGNUM (REGNO (sreg)); |
| reg_save (label, regno, sregno, 0); |
| } |
| |
| /* What register, if any, is currently saved in REG? */ |
| |
| static rtx |
| reg_saved_in (rtx reg) |
| { |
| unsigned int regn = REGNO (reg); |
| size_t i; |
| struct queued_reg_save *q; |
| |
| for (q = queued_reg_saves; q; q = q->next) |
| if (q->saved_reg && regn == REGNO (q->saved_reg)) |
| return q->reg; |
| |
| for (i = 0; i < num_regs_saved_in_regs; i++) |
| if (regs_saved_in_regs[i].saved_in_reg |
| && regn == REGNO (regs_saved_in_regs[i].saved_in_reg)) |
| return regs_saved_in_regs[i].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; |
| |
| /* 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_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} 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 (postinc <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 |
| |
| Rule 20: |
| (set reg fde->drap_reg) |
| constraints: fde->vdrap_reg == INVALID_REGNUM |
| effects: fde->vdrap_reg = reg. |
| (set mem fde->drap_reg) |
| constraints: fde->drap_reg_saved == 1 |
| effects: none. */ |
| |
| 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_args_size_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 (); |
| |
| if (GET_CODE (src) == REG |
| && fde |
| && fde->drap_reg == REGNO (src) |
| && (fde->drap_reg_saved |
| || GET_CODE (dest) == REG)) |
| { |
| /* Rule 20 */ |
| /* If we are saving dynamic realign argument pointer to a |
| register, the destination is virtual dynamic realign |
| argument pointer. It may be used to access argument. */ |
| if (GET_CODE (dest) == REG) |
| { |
| gcc_assert (fde->vdrap_reg == INVALID_REGNUM); |
| fde->vdrap_reg = REGNO (dest); |
| } |
| return; |
| } |
| |
| 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 |
| && GET_CODE (XEXP (src, 1)) == CONST_INT); |
| 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 |
| && GET_CODE (XEXP (src, 1)) == CONST_INT) |
| { |
| /* 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 |
| && GET_CODE (XEXP (src, 1)) == CONST_INT) |
| { |
| 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 |
| && GET_CODE (XEXP (src, 1)) == CONST_INT); |
| |
| 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) |
| { |
| 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: |
| /* 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; |
| |
| offset = -cfa_store.offset; |
| break; |
| |
| /* Rule 11 */ |
| case PRE_INC: |
| case PRE_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; |
| |
| offset = -cfa_store.offset; |
| break; |
| |
| /* Rule 12 */ |
| /* With an offset. */ |
| case PLUS: |
| case MINUS: |
| case LO_SUM: |
| { |
| int regno; |
| |
| gcc_assert (GET_CODE (XEXP (XEXP (dest, 0), 1)) == CONST_INT |
| && 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_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_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 src; |
| |
| if (insn == NULL_RTX) |
| { |
| size_t i; |
| |
| /* Flush any queued register saves. */ |
| 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; |
| |
| for (i = 0; i < num_regs_saved_in_regs; i++) |
| { |
| regs_saved_in_regs[i].orig_reg = NULL_RTX; |
| regs_saved_in_regs[i].saved_in_reg = NULL_RTX; |
| } |
| num_regs_saved_in_regs = 0; |
| |
| if (barrier_args_size) |
| { |
| XDELETEVEC (barrier_args_size); |
| barrier_args_size = NULL; |
| } |
| return; |
| } |
| |
| if (!NONJUMP_INSN_P (insn) || clobbers_queued_reg_save (insn)) |
| flush_queued_reg_saves (); |
| |
| if (! RTX_FRAME_RELATED_P (insn)) |
| { |
| if (!ACCUMULATE_OUTGOING_ARGS) |
| dwarf2out_stack_adjust (insn, after_p); |
| return; |
| } |
| |
| label = dwarf2out_cfi_label (false); |
| src = find_reg_note (insn, REG_FRAME_RELATED_EXPR, NULL_RTX); |
| if (src) |
| insn = XEXP (src, 0); |
| else |
| insn = PATTERN (insn); |
| |
| dwarf2out_frame_debug_expr (insn, label); |
| } |
| |
| #endif |
| |
| /* Describe for the GTY machinery what parts of dw_cfi_oprnd1 are used. */ |
| static enum dw_cfi_oprnd_type dw_cfi_oprnd1_desc |
| (enum dwarf_call_frame_info cfi); |
| |
| static enum dw_cfi_oprnd_type |
| dw_cfi_oprnd1_desc (enum dwarf_call_frame_info cfi) |
| { |
| switch (cfi) |
| { |
| case DW_CFA_nop: |
| case DW_CFA_GNU_window_save: |
| return dw_cfi_oprnd_unused; |
| |
| case DW_CFA_set_loc: |
| case DW_CFA_advance_loc1: |
| case DW_CFA_advance_loc2: |
| case DW_CFA_advance_loc4: |
| case DW_CFA_MIPS_advance_loc8: |
| return dw_cfi_oprnd_addr; |
| |
| case DW_CFA_offset: |
| case DW_CFA_offset_extended: |
| case DW_CFA_def_cfa: |
| case DW_CFA_offset_extended_sf: |
| case DW_CFA_def_cfa_sf: |
| case DW_CFA_restore_extended: |
| case DW_CFA_undefined: |
| case DW_CFA_same_value: |
| case DW_CFA_def_cfa_register: |
| case DW_CFA_register: |
| return dw_cfi_oprnd_reg_num; |
| |
| case DW_CFA_def_cfa_offset: |
| case DW_CFA_GNU_args_size: |
| case DW_CFA_def_cfa_offset_sf: |
| return dw_cfi_oprnd_offset; |
| |
| case DW_CFA_def_cfa_expression: |
| case DW_CFA_expression: |
| return dw_cfi_oprnd_loc; |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| /* Describe for the GTY machinery what parts of dw_cfi_oprnd2 are used. */ |
| static enum dw_cfi_oprnd_type dw_cfi_oprnd2_desc |
| (enum dwarf_call_frame_info cfi); |
| |
| static enum dw_cfi_oprnd_type |
| dw_cfi_oprnd2_desc (enum dwarf_call_frame_info cfi) |
| { |
| switch (cfi) |
| { |
| case DW_CFA_def_cfa: |
| case DW_CFA_def_cfa_sf: |
| case DW_CFA_offset: |
| case DW_CFA_offset_extended_sf: |
| case DW_CFA_offset_extended: |
| return dw_cfi_oprnd_offset; |
| |
| case DW_CFA_register: |
| return dw_cfi_oprnd_reg_num; |
| |
| default: |
| return dw_cfi_oprnd_unused; |
| } |
| } |
| |
| #if defined (DWARF2_DEBUGGING_INFO) || defined (DWARF2_UNWIND_INFO) |
| |
| /* Switch to eh_frame_section. If we don't have an eh_frame_section, |
| switch to the data section instead, and write out a synthetic label |
| for collect2. */ |
| |
| static void |
| switch_to_eh_frame_section (void) |
| { |
| tree label; |
| |
| #ifdef EH_FRAME_SECTION_NAME |
| if (eh_frame_section == 0) |
| { |
| int flags; |
| |
| if (EH_TABLES_CAN_BE_READ_ONLY) |
| { |
| int fde_encoding; |
| int per_encoding; |
| int lsda_encoding; |
| |
| fde_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/1, |
| /*global=*/0); |
| per_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/2, |
| /*global=*/1); |
| lsda_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0, |
| /*global=*/0); |
| flags = ((! flag_pic |
| || ((fde_encoding & 0x70) != DW_EH_PE_absptr |
| && (fde_encoding & 0x70) != DW_EH_PE_aligned |
| && (per_encoding & 0x70) != DW_EH_PE_absptr |
| && (per_encoding & 0x70) != DW_EH_PE_aligned |
| && (lsda_encoding & 0x70) != DW_EH_PE_absptr |
| && (lsda_encoding & 0x70) != DW_EH_PE_aligned)) |
| ? 0 : SECTION_WRITE); |
| } |
| else |
| flags = SECTION_WRITE; |
| eh_frame_section = get_section (EH_FRAME_SECTION_NAME, flags, NULL); |
| } |
| #endif |
| |
| if (eh_frame_section) |
| switch_to_section (eh_frame_section); |
| else |
| { |
| /* We have no special eh_frame section. Put the information in |
| the data section and emit special labels to guide collect2. */ |
| switch_to_section (data_section); |
| label = get_file_function_name ("F"); |
| ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (PTR_SIZE)); |
| targetm.asm_out.globalize_label (asm_out_file, |
| IDENTIFIER_POINTER (label)); |
| ASM_OUTPUT_LABEL (asm_out_file, IDENTIFIER_POINTER (label)); |
| } |
| } |
| |
| /* Divide OFF by DWARF_CIE_DATA_ALIGNMENT, asserting no remainder. */ |
| |
| static 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; |
| } |
| |
| /* Output a Call Frame Information opcode and its operand(s). */ |
| |
| static void |
| output_cfi (dw_cfi_ref cfi, dw_fde_ref fde, int for_eh) |
| { |
| unsigned long r; |
| HOST_WIDE_INT off; |
| |
| if (cfi->dw_cfi_opc == DW_CFA_advance_loc) |
| dw2_asm_output_data (1, (cfi->dw_cfi_opc |
| | (cfi->dw_cfi_oprnd1.dw_cfi_offset & 0x3f)), |
| "DW_CFA_advance_loc " HOST_WIDE_INT_PRINT_HEX, |
| ((unsigned HOST_WIDE_INT) |
| cfi->dw_cfi_oprnd1.dw_cfi_offset)); |
| else if (cfi->dw_cfi_opc == DW_CFA_offset) |
| { |
| r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh); |
| dw2_asm_output_data (1, (cfi->dw_cfi_opc | (r & 0x3f)), |
| "DW_CFA_offset, column 0x%lx", r); |
| off = div_data_align (cfi->dw_cfi_oprnd2.dw_cfi_offset); |
| dw2_asm_output_data_uleb128 (off, NULL); |
| } |
| else if (cfi->dw_cfi_opc == DW_CFA_restore) |
| { |
| r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh); |
| dw2_asm_output_data (1, (cfi->dw_cfi_opc | (r & 0x3f)), |
| "DW_CFA_restore, column 0x%lx", r); |
| } |
| else |
| { |
| dw2_asm_output_data (1, cfi->dw_cfi_opc, |
| "%s", dwarf_cfi_name (cfi->dw_cfi_opc)); |
| |
| switch (cfi->dw_cfi_opc) |
| { |
| case DW_CFA_set_loc: |
| if (for_eh) |
| dw2_asm_output_encoded_addr_rtx ( |
| ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/1, /*global=*/0), |
| gen_rtx_SYMBOL_REF (Pmode, cfi->dw_cfi_oprnd1.dw_cfi_addr), |
| false, NULL); |
| else |
| dw2_asm_output_addr (DWARF2_ADDR_SIZE, |
| cfi->dw_cfi_oprnd1.dw_cfi_addr, NULL); |
| fde->dw_fde_current_label = cfi->dw_cfi_oprnd1.dw_cfi_addr; |
| break; |
| |
| case DW_CFA_advance_loc1: |
| dw2_asm_output_delta (1, cfi->dw_cfi_oprnd1.dw_cfi_addr, |
| fde->dw_fde_current_label, NULL); |
| fde->dw_fde_current_label = cfi->dw_cfi_oprnd1.dw_cfi_addr; |
| break; |
| |
| case DW_CFA_advance_loc2: |
| dw2_asm_output_delta (2, cfi->dw_cfi_oprnd1.dw_cfi_addr, |
| fde->dw_fde_current_label, NULL); |
| fde->dw_fde_current_label = cfi->dw_cfi_oprnd1.dw_cfi_addr; |
| break; |
| |
| case DW_CFA_advance_loc4: |
| dw2_asm_output_delta (4, cfi->dw_cfi_oprnd1.dw_cfi_addr, |
| fde->dw_fde_current_label, NULL); |
| fde->dw_fde_current_label = cfi->dw_cfi_oprnd1.dw_cfi_addr; |
| break; |
| |
| case DW_CFA_MIPS_advance_loc8: |
| dw2_asm_output_delta (8, cfi->dw_cfi_oprnd1.dw_cfi_addr, |
| fde->dw_fde_current_label, NULL); |
| fde->dw_fde_current_label = cfi->dw_cfi_oprnd1.dw_cfi_addr; |
| break; |
| |
| case DW_CFA_offset_extended: |
| r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh); |
| dw2_asm_output_data_uleb128 (r, NULL); |
| off = div_data_align (cfi->dw_cfi_oprnd2.dw_cfi_offset); |
| dw2_asm_output_data_uleb128 (off, NULL); |
| break; |
| |
| case DW_CFA_def_cfa: |
| r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh); |
| dw2_asm_output_data_uleb128 (r, NULL); |
| dw2_asm_output_data_uleb128 (cfi->dw_cfi_oprnd2.dw_cfi_offset, NULL); |
| break; |
| |
| case DW_CFA_offset_extended_sf: |
| r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh); |
| dw2_asm_output_data_uleb128 (r, NULL); |
| off = div_data_align (cfi->dw_cfi_oprnd2.dw_cfi_offset); |
| dw2_asm_output_data_sleb128 (off, NULL); |
| break; |
| |
| case DW_CFA_def_cfa_sf: |
| r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh); |
| dw2_asm_output_data_uleb128 (r, NULL); |
| off = div_data_align (cfi->dw_cfi_oprnd2.dw_cfi_offset); |
| dw2_asm_output_data_sleb128 (off, NULL); |
| break; |
| |
| case DW_CFA_restore_extended: |
| case DW_CFA_undefined: |
| case DW_CFA_same_value: |
| case DW_CFA_def_cfa_register: |
| r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh); |
| dw2_asm_output_data_uleb128 (r, NULL); |
| break; |
| |
| case DW_CFA_register: |
| r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh); |
| dw2_asm_output_data_uleb128 (r, NULL); |
| r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd2.dw_cfi_reg_num, for_eh); |
| dw2_asm_output_data_uleb128 (r, NULL); |
| break; |
| |
| case DW_CFA_def_cfa_offset: |
| case DW_CFA_GNU_args_size: |
| dw2_asm_output_data_uleb128 (cfi->dw_cfi_oprnd1.dw_cfi_offset, NULL); |
| break; |
| |
| case DW_CFA_def_cfa_offset_sf: |
| off = div_data_align (cfi->dw_cfi_oprnd1.dw_cfi_offset); |
| dw2_asm_output_data_sleb128 (off, NULL); |
| break; |
| |
| case DW_CFA_GNU_window_save: |
| break; |
| |
| case DW_CFA_def_cfa_expression: |
| case DW_CFA_expression: |
| output_cfa_loc (cfi); |
| break; |
| |
| case DW_CFA_GNU_negative_offset_extended: |
| /* Obsoleted by DW_CFA_offset_extended_sf. */ |
| gcc_unreachable (); |
| |
| default: |
| break; |
| } |
| } |
| } |
| |
| /* Similar, but do it via assembler directives instead. */ |
| |
| static void |
| output_cfi_directive (dw_cfi_ref cfi) |
| { |
| unsigned long r, r2; |
| |
| switch (cfi->dw_cfi_opc) |
| { |
| case DW_CFA_advance_loc: |
| case DW_CFA_advance_loc1: |
| case DW_CFA_advance_loc2: |
| case DW_CFA_advance_loc4: |
| case DW_CFA_MIPS_advance_loc8: |
| case DW_CFA_set_loc: |
| /* Should only be created by add_fde_cfi in a code path not |
| followed when emitting via directives. The assembler is |
| going to take care of this for us. */ |
| gcc_unreachable (); |
| |
| case DW_CFA_offset: |
| case DW_CFA_offset_extended: |
| case DW_CFA_offset_extended_sf: |
| r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1); |
| fprintf (asm_out_file, "\t.cfi_offset %lu, "HOST_WIDE_INT_PRINT_DEC"\n", |
| r, cfi->dw_cfi_oprnd2.dw_cfi_offset); |
| break; |
| |
| case DW_CFA_restore: |
| case DW_CFA_restore_extended: |
| r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1); |
| fprintf (asm_out_file, "\t.cfi_restore %lu\n", r); |
| break; |
| |
| case DW_CFA_undefined: |
| r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1); |
| fprintf (asm_out_file, "\t.cfi_undefined %lu\n", r); |
| break; |
| |
| case DW_CFA_same_value: |
| r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1); |
| fprintf (asm_out_file, "\t.cfi_same_value %lu\n", r); |
| break; |
| |
| case DW_CFA_def_cfa: |
| case DW_CFA_def_cfa_sf: |
| r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1); |
| fprintf (asm_out_file, "\t.cfi_def_cfa %lu, "HOST_WIDE_INT_PRINT_DEC"\n", |
| r, cfi->dw_cfi_oprnd2.dw_cfi_offset); |
| break; |
| |
| case DW_CFA_def_cfa_register: |
| r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1); |
| fprintf (asm_out_file, "\t.cfi_def_cfa_register %lu\n", r); |
| break; |
| |
| case DW_CFA_register: |
| r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1); |
| r2 = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd2.dw_cfi_reg_num, 1); |
| fprintf (asm_out_file, "\t.cfi_register %lu, %lu\n", r, r2); |
| break; |
| |
| case DW_CFA_def_cfa_offset: |
| case DW_CFA_def_cfa_offset_sf: |
| fprintf (asm_out_file, "\t.cfi_def_cfa_offset " |
| HOST_WIDE_INT_PRINT_DEC"\n", |
| cfi->dw_cfi_oprnd1.dw_cfi_offset); |
| break; |
| |
| case DW_CFA_GNU_args_size: |
| fprintf (asm_out_file, "\t.cfi_escape 0x%x,", DW_CFA_GNU_args_size); |
| dw2_asm_output_data_uleb128_raw (cfi->dw_cfi_oprnd1.dw_cfi_offset); |
| if (flag_debug_asm) |
| fprintf (asm_out_file, "\t%s args_size "HOST_WIDE_INT_PRINT_DEC, |
| ASM_COMMENT_START, cfi->dw_cfi_oprnd1.dw_cfi_offset); |
| fputc ('\n', asm_out_file); |
| break; |
| |
| case DW_CFA_GNU_window_save: |
| fprintf (asm_out_file, "\t.cfi_window_save\n"); |
| break; |
| |
| case DW_CFA_def_cfa_expression: |
| case DW_CFA_expression: |
| fprintf (asm_out_file, "\t.cfi_escape 0x%x,", cfi->dw_cfi_opc); |
| output_cfa_loc_raw (cfi); |
| fputc ('\n', asm_out_file); |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| /* Output the call frame information used to record information |
| that relates to calculating the frame pointer, and records the |
| location of saved registers. */ |
| |
| static void |
| output_call_frame_info (int for_eh) |
| { |
| unsigned int i; |
| dw_fde_ref fde; |
| dw_cfi_ref cfi; |
| char l1[20], l2[20], section_start_label[20]; |
| bool any_lsda_needed = false; |
| char augmentation[6]; |
| int augmentation_size; |
| int fde_encoding = DW_EH_PE_absptr; |
| int per_encoding = DW_EH_PE_absptr; |
| int lsda_encoding = DW_EH_PE_absptr; |
| int return_reg; |
| |
| /* Don't emit a CIE if there won't be any FDEs. */ |
| if (fde_table_in_use == 0) |
| return; |
| |
| /* Nothing to do if the assembler's doing it all. */ |
| if (dwarf2out_do_cfi_asm ()) |
| return; |
| |
| /* If we make FDEs linkonce, we may have to emit an empty label for |
| an FDE that wouldn't otherwise be emitted. We want to avoid |
| having an FDE kept around when the function it refers to is |
| discarded. Example where this matters: a primary function |
| template in C++ requires EH information, but an explicit |
| specialization doesn't. */ |
| if (TARGET_USES_WEAK_UNWIND_INFO |
| && ! flag_asynchronous_unwind_tables |
| && flag_exceptions |
| && for_eh) |
| for (i = 0; i < fde_table_in_use; i++) |
| if ((fde_table[i].nothrow || fde_table[i].all_throwers_are_sibcalls) |
| && !fde_table[i].uses_eh_lsda |
| && ! DECL_WEAK (fde_table[i].decl)) |
| targetm.asm_out.unwind_label (asm_out_file, fde_table[i].decl, |
| for_eh, /* empty */ 1); |
| |
| /* If we don't have any functions we'll want to unwind out of, don't |
| emit any EH unwind information. Note that if exceptions aren't |
| enabled, we won't have collected nothrow information, and if we |
| asked for asynchronous tables, we always want this info. */ |
| if (for_eh) |
| { |
| bool any_eh_needed = !flag_exceptions || flag_asynchronous_unwind_tables; |
| |
| for (i = 0; i < fde_table_in_use; i++) |
| if (fde_table[i].uses_eh_lsda) |
| any_eh_needed = any_lsda_needed = true; |
| else if (TARGET_USES_WEAK_UNWIND_INFO && DECL_WEAK (fde_table[i].decl)) |
| any_eh_needed = true; |
| else if (! fde_table[i].nothrow |
| && ! fde_table[i].all_throwers_are_sibcalls) |
| any_eh_needed = true; |
| |
| if (! any_eh_needed) |
| return; |
| } |
| |
| /* We're going to be generating comments, so turn on app. */ |
| if (flag_debug_asm) |
| app_enable (); |
| |
| if (for_eh) |
| switch_to_eh_frame_section (); |
| else |
| { |
| if (!debug_frame_section) |
| debug_frame_section = get_section (DEBUG_FRAME_SECTION, |
| SECTION_DEBUG, NULL); |
| switch_to_section (debug_frame_section); |
| } |
| |
| ASM_GENERATE_INTERNAL_LABEL (section_start_label, FRAME_BEGIN_LABEL, for_eh); |
| ASM_OUTPUT_LABEL (asm_out_file, section_start_label); |
| |
| /* Output the CIE. */ |
| ASM_GENERATE_INTERNAL_LABEL (l1, CIE_AFTER_SIZE_LABEL, for_eh); |
| ASM_GENERATE_INTERNAL_LABEL (l2, CIE_END_LABEL, for_eh); |
| if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4 && !for_eh) |
| dw2_asm_output_data (4, 0xffffffff, |
| "Initial length escape value indicating 64-bit DWARF extension"); |
| dw2_asm_output_delta (for_eh ? 4 : DWARF_OFFSET_SIZE, l2, l1, |
| "Length of Common Information Entry"); |
| ASM_OUTPUT_LABEL (asm_out_file, l1); |
| |
| /* Now that the CIE pointer is PC-relative for EH, |
| use 0 to identify the CIE. */ |
| dw2_asm_output_data ((for_eh ? 4 : DWARF_OFFSET_SIZE), |
| (for_eh ? 0 : DWARF_CIE_ID), |
| "CIE Identifier Tag"); |
| |
| dw2_asm_output_data (1, DW_CIE_VERSION, "CIE Version"); |
| |
| augmentation[0] = 0; |
| augmentation_size = 0; |
| if (for_eh) |
| { |
| char *p; |
| |
| /* Augmentation: |
| z Indicates that a uleb128 is present to size the |
| augmentation section. |
| L Indicates the encoding (and thus presence) of |
| an LSDA pointer in the FDE augmentation. |
| R Indicates a non-default pointer encoding for |
| FDE code pointers. |
| P Indicates the presence of an encoding + language |
| personality routine in the CIE augmentation. */ |
| |
| fde_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/1, /*global=*/0); |
| per_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/2, /*global=*/1); |
| lsda_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0, /*global=*/0); |
| |
| p = augmentation + 1; |
| if (eh_personality_libfunc) |
| { |
| *p++ = 'P'; |
| augmentation_size += 1 + size_of_encoded_value (per_encoding); |
| assemble_external_libcall (eh_personality_libfunc); |
| } |
| if (any_lsda_needed) |
| { |
| *p++ = 'L'; |
| augmentation_size += 1; |
| } |
| if (fde_encoding != DW_EH_PE_absptr) |
| { |
| *p++ = 'R'; |
| augmentation_size += 1; |
| } |
| if (p > augmentation + 1) |
| { |
| augmentation[0] = 'z'; |
| *p = '\0'; |
| } |
| |
| /* Ug. Some platforms can't do unaligned dynamic relocations at all. */ |
| if (eh_personality_libfunc && per_encoding == DW_EH_PE_aligned) |
| { |
| int offset = ( 4 /* Length */ |
| + 4 /* CIE Id */ |
| + 1 /* CIE version */ |
| + strlen (augmentation) + 1 /* Augmentation */ |
| + size_of_uleb128 (1) /* Code alignment */ |
| + size_of_sleb128 (DWARF_CIE_DATA_ALIGNMENT) |
| + 1 /* RA column */ |
| + 1 /* Augmentation size */ |
| + 1 /* Personality encoding */ ); |
| int pad = -offset & (PTR_SIZE - 1); |
| |
| augmentation_size += pad; |
| |
| /* Augmentations should be small, so there's scarce need to |
| iterate for a solution. Die if we exceed one uleb128 byte. */ |
| gcc_assert (size_of_uleb128 (augmentation_size) == 1); |
| } |
| } |
| |
| dw2_asm_output_nstring (augmentation, -1, "CIE Augmentation"); |
| dw2_asm_output_data_uleb128 (1, "CIE Code Alignment Factor"); |
| dw2_asm_output_data_sleb128 (DWARF_CIE_DATA_ALIGNMENT, |
| "CIE Data Alignment Factor"); |
| |
| return_reg = DWARF2_FRAME_REG_OUT (DWARF_FRAME_RETURN_COLUMN, for_eh); |
| if (DW_CIE_VERSION == 1) |
| dw2_asm_output_data (1, return_reg, "CIE RA Column"); |
| else |
| dw2_asm_output_data_uleb128 (return_reg, "CIE RA Column"); |
| |
| if (augmentation[0]) |
| { |
| dw2_asm_output_data_uleb128 (augmentation_size, "Augmentation size"); |
| if (eh_personality_libfunc) |
| { |
| dw2_asm_output_data (1, per_encoding, "Personality (%s)", |
| eh_data_format_name (per_encoding)); |
| dw2_asm_output_encoded_addr_rtx (per_encoding, |
| eh_personality_libfunc, |
| true, NULL); |
| } |
| |
| if (any_lsda_needed) |
| dw2_asm_output_data (1, lsda_encoding, "LSDA Encoding (%s)", |
| eh_data_format_name (lsda_encoding)); |
| |
| if (fde_encoding != DW_EH_PE_absptr) |
| dw2_asm_output_data (1, fde_encoding, "FDE Encoding (%s)", |
| eh_data_format_name (fde_encoding)); |
| } |
| |
| for (cfi = cie_cfi_head; cfi != NULL; cfi = cfi->dw_cfi_next) |
| output_cfi (cfi, NULL, for_eh); |
| |
| /* Pad the CIE out to an address sized boundary. */ |
| ASM_OUTPUT_ALIGN (asm_out_file, |
| floor_log2 (for_eh ? PTR_SIZE : DWARF2_ADDR_SIZE)); |
| ASM_OUTPUT_LABEL (asm_out_file, l2); |
| |
| /* Loop through all of the FDE's. */ |
| for (i = 0; i < fde_table_in_use; i++) |
| { |
| fde = &fde_table[i]; |
| |
| /* Don't emit EH unwind info for leaf functions that don't need it. */ |
| if (for_eh && !flag_asynchronous_unwind_tables && flag_exceptions |
| && (fde->nothrow || fde->all_throwers_are_sibcalls) |
| && ! (TARGET_USES_WEAK_UNWIND_INFO && DECL_WEAK (fde_table[i].decl)) |
| && !fde->uses_eh_lsda) |
| continue; |
| |
| targetm.asm_out.unwind_label (asm_out_file, fde->decl, for_eh, /* empty */ 0); |
| targetm.asm_out.internal_label (asm_out_file, FDE_LABEL, for_eh + i * 2); |
| ASM_GENERATE_INTERNAL_LABEL (l1, FDE_AFTER_SIZE_LABEL, for_eh + i * 2); |
| ASM_GENERATE_INTERNAL_LABEL (l2, FDE_END_LABEL, for_eh + i * 2); |
| if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4 && !for_eh) |
| dw2_asm_output_data (4, 0xffffffff, |
| "Initial length escape value indicating 64-bit DWARF extension"); |
| dw2_asm_output_delta (for_eh ? 4 : DWARF_OFFSET_SIZE, l2, l1, |
| "FDE Length"); |
| ASM_OUTPUT_LABEL (asm_out_file, l1); |
| |
| if (for_eh) |
| dw2_asm_output_delta (4, l1, section_start_label, "FDE CIE offset"); |
| else |
| dw2_asm_output_offset (DWARF_OFFSET_SIZE, section_start_label, |
| debug_frame_section, "FDE CIE offset"); |
| |
| if (for_eh) |
| { |
| if (fde->dw_fde_switched_sections) |
| { |
| rtx sym_ref2 = gen_rtx_SYMBOL_REF (Pmode, |
| fde->dw_fde_unlikely_section_label); |
| rtx sym_ref3= gen_rtx_SYMBOL_REF (Pmode, |
| fde->dw_fde_hot_section_label); |
| SYMBOL_REF_FLAGS (sym_ref2) |= SYMBOL_FLAG_LOCAL; |
| SYMBOL_REF_FLAGS (sym_ref3) |= SYMBOL_FLAG_LOCAL; |
| dw2_asm_output_encoded_addr_rtx (fde_encoding, sym_ref3, false, |
| "FDE initial location"); |
| dw2_asm_output_delta (size_of_encoded_value (fde_encoding), |
| fde->dw_fde_hot_section_end_label, |
| fde->dw_fde_hot_section_label, |
| "FDE address range"); |
| dw2_asm_output_encoded_addr_rtx (fde_encoding, sym_ref2, false, |
| "FDE initial location"); |
| dw2_asm_output_delta (size_of_encoded_value (fde_encoding), |
| fde->dw_fde_unlikely_section_end_label, |
| fde->dw_fde_unlikely_section_label, |
| "FDE address range"); |
| } |
| else |
| { |
| rtx sym_ref = gen_rtx_SYMBOL_REF (Pmode, fde->dw_fde_begin); |
| SYMBOL_REF_FLAGS (sym_ref) |= SYMBOL_FLAG_LOCAL; |
| dw2_asm_output_encoded_addr_rtx (fde_encoding, |
| sym_ref, |
| false, |
| "FDE initial location"); |
| dw2_asm_output_delta (size_of_encoded_value (fde_encoding), |
| fde->dw_fde_end, fde->dw_fde_begin, |
| "FDE address range"); |
| } |
| } |
| else |
| { |
| if (fde->dw_fde_switched_sections) |
| { |
| dw2_asm_output_addr (DWARF2_ADDR_SIZE, |
| fde->dw_fde_hot_section_label, |
| "FDE initial location"); |
| dw2_asm_output_delta (DWARF2_ADDR_SIZE, |
| fde->dw_fde_hot_section_end_label, |
| fde->dw_fde_hot_section_label, |
| "FDE address range"); |
| dw2_asm_output_addr (DWARF2_ADDR_SIZE, |
| fde->dw_fde_unlikely_section_label, |
| "FDE initial location"); |
| dw2_asm_output_delta (DWARF2_ADDR_SIZE, |
| fde->dw_fde_unlikely_section_end_label, |
| fde->dw_fde_unlikely_section_label, |
| "FDE address range"); |
| } |
| else |
| { |
| dw2_asm_output_addr (DWARF2_ADDR_SIZE, fde->dw_fde_begin, |
| "FDE initial location"); |
| dw2_asm_output_delta (DWARF2_ADDR_SIZE, |
| fde->dw_fde_end, fde->dw_fde_begin, |
| "FDE address range"); |
| } |
| } |
| |
| if (augmentation[0]) |
| { |
| if (any_lsda_needed) |
| { |
| int size = size_of_encoded_value (lsda_encoding); |
| |
| if (lsda_encoding == DW_EH_PE_aligned) |
| { |
| int offset = ( 4 /* Length */ |
| + 4 /* CIE offset */ |
| + 2 * size_of_encoded_value (fde_encoding) |
| + 1 /* Augmentation size */ ); |
| int pad = -offset & (PTR_SIZE - 1); |
| |
| size += pad; |
| gcc_assert (size_of_uleb128 (size) == 1); |
| } |
| |
| dw2_asm_output_data_uleb128 (size, "Augmentation size"); |
| |
| if (fde->uses_eh_lsda) |
| { |
| ASM_GENERATE_INTERNAL_LABEL (l1, "LLSDA", |
| fde->funcdef_number); |
| dw2_asm_output_encoded_addr_rtx ( |
| lsda_encoding, gen_rtx_SYMBOL_REF (Pmode, l1), |
| false, "Language Specific Data Area"); |
| } |
| else |
| { |
| if (lsda_encoding == DW_EH_PE_aligned) |
| ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (PTR_SIZE)); |
| dw2_asm_output_data |
| (size_of_encoded_value (lsda_encoding), 0, |
| "Language Specific Data Area (none)"); |
| } |
| } |
| else |
| dw2_asm_output_data_uleb128 (0, "Augmentation size"); |
| } |
| |
| /* Loop through the Call Frame Instructions associated with |
| this FDE. */ |
| fde->dw_fde_current_label = fde->dw_fde_begin; |
| for (cfi = fde->dw_fde_cfi; cfi != NULL; cfi = cfi->dw_cfi_next) |
| output_cfi (cfi, fde, for_eh); |
| |
| /* Pad the FDE out to an address sized boundary. */ |
| ASM_OUTPUT_ALIGN (asm_out_file, |
| floor_log2 ((for_eh ? PTR_SIZE : DWARF2_ADDR_SIZE))); |
| ASM_OUTPUT_LABEL (asm_out_file, l2); |
| } |
| |
| if (for_eh && targetm.terminate_dw2_eh_frame_info) |
| dw2_asm_output_data (4, 0, "End of Table"); |
| #ifdef MIPS_DEBUGGING_INFO |
| /* Work around Irix 6 assembler bug whereby labels at the end of a section |
| get a value of 0. Putting .align 0 after the label fixes it. */ |
| ASM_OUTPUT_ALIGN (asm_out_file, 0); |
| #endif |
| |
| /* Turn off app to make assembly quicker. */ |
| if (flag_debug_asm) |
| app_disable (); |
| } |
| |
| /* Output a marker (i.e. a label) for the beginning of a function, before |
| the prologue. */ |
| |
| void |
| dwarf2out_begin_prologue (unsigned int line ATTRIBUTE_UNUSED, |
| const char *file ATTRIBUTE_UNUSED) |
| { |
| char label[MAX_ARTIFICIAL_LABEL_BYTES]; |
| char * dup_label; |
| dw_fde_ref fde; |
| |
| current_function_func_begin_label = NULL; |
| |
| #ifdef TARGET_UNWIND_INFO |
| /* ??? current_function_func_begin_label is also used by except.c |
| for call-site information. We must emit this label if it might |
| be used. */ |
| if ((! flag_exceptions || USING_SJLJ_EXCEPTIONS) |
| && ! dwarf2out_do_frame ()) |
| return; |
| #else |
| if (! dwarf2out_do_frame ()) |
| return; |
| #endif |
| |
| switch_to_section (function_section (current_function_decl)); |
| ASM_GENERATE_INTERNAL_LABEL (label, FUNC_BEGIN_LABEL, |
| current_function_funcdef_no); |
| ASM_OUTPUT_DEBUG_LABEL (asm_out_file, FUNC_BEGIN_LABEL, |
| current_function_funcdef_no); |
| dup_label = xstrdup (label); |
| current_function_func_begin_label = dup_label; |
| |
| #ifdef TARGET_UNWIND_INFO |
| /* We can elide the fde allocation if we're not emitting debug info. */ |
| if (! dwarf2out_do_frame ()) |
| return; |
| #endif |
| |
| /* Expand the fde table if necessary. */ |
| if (fde_table_in_use == fde_table_allocated) |
| { |
| fde_table_allocated += FDE_TABLE_INCREMENT; |
| fde_table = GGC_RESIZEVEC (dw_fde_node, fde_table, fde_table_allocated); |
| memset (fde_table + fde_table_in_use, 0, |
| FDE_TABLE_INCREMENT * sizeof (dw_fde_node)); |
| } |
| |
| /* Record the FDE associated with this function. */ |
| current_funcdef_fde = fde_table_in_use; |
| |
| /* Add the new FDE at the end of the fde_table. */ |
| fde = &fde_table[fde_table_in_use |