diff options
author | YamaArashi <shadow962@live.com> | 2016-02-14 17:20:50 -0800 |
---|---|---|
committer | YamaArashi <shadow962@live.com> | 2016-02-14 17:20:50 -0800 |
commit | 6b7d4fc68d647b8bfdf40e1c0b83438495352327 (patch) | |
tree | 4eb461c9fb1e9cfcceac1f5f1f20729addf80699 /gcc | |
parent | ee6378488b6ef2654dcbffaaf1319c415c552f27 (diff) |
remove unused files
Diffstat (limited to 'gcc')
-rwxr-xr-x | gcc/dwarf2out_020422.c | 9925 | ||||
-rwxr-xr-x | gcc/function.BAK | 6650 | ||||
-rwxr-xr-x | gcc/function_990206.c | 6578 | ||||
-rwxr-xr-x | gcc/rtl_020422.c | 935 | ||||
-rwxr-xr-x | gcc/rtl_020422.h | 1569 | ||||
-rwxr-xr-x | gcc/unroll_991002.c | 4045 |
6 files changed, 0 insertions, 29702 deletions
diff --git a/gcc/dwarf2out_020422.c b/gcc/dwarf2out_020422.c deleted file mode 100755 index 64a4968..0000000 --- a/gcc/dwarf2out_020422.c +++ /dev/null @@ -1,9925 +0,0 @@ -/* Output Dwarf2 format symbol table information from the GNU C compiler. - Copyright (C) 1992, 1993, 1995, 1996, 1997, 1998, 2001, 2002 - 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 GNU CC. - -GNU CC 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 2, or (at your option) -any later version. - -GNU CC 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 GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ - -/* 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. */ - -#include "config.h" -#include "system.h" -#include "defaults.h" -#include "tree.h" -#include "flags.h" -#include "rtl.h" -#include "hard-reg-set.h" -#include "regs.h" -#include "insn-config.h" -#include "reload.h" -#include "output.h" -#include "expr.h" -#include "except.h" -#include "dwarf2.h" -#include "dwarf2out.h" -#include "toplev.h" -#include "dyn-string.h" - -/* We cannot use <assert.h> in GCC source, since that would include - GCC's assert.h, which may not be compatible with the host compiler. */ -#undef assert -#ifdef NDEBUG -# define assert(e) -#else -# define assert(e) do { if (! (e)) abort (); } while (0) -#endif - -/* Decide whether we want to emit frame unwind information for the current - translation unit. */ - -int -dwarf2out_do_frame () -{ - return (write_symbols == DWARF2_DEBUG -#ifdef DWARF2_FRAME_INFO - || DWARF2_FRAME_INFO -#endif -#ifdef DWARF2_UNWIND_INFO - || (flag_exceptions && ! exceptions_via_longjmp) -#endif - ); -} - -#if defined (DWARF2_DEBUGGING_INFO) || defined (DWARF2_UNWIND_INFO) - -#ifndef __GNUC__ -#define inline -#endif - -/* 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. */ - -typedef union dw_cfi_oprnd_struct -{ - unsigned long dw_cfi_reg_num; - long int dw_cfi_offset; - char *dw_cfi_addr; -} -dw_cfi_oprnd; - -typedef struct dw_cfi_struct -{ - dw_cfi_ref dw_cfi_next; - enum dwarf_call_frame_info dw_cfi_opc; - dw_cfi_oprnd dw_cfi_oprnd1; - dw_cfi_oprnd dw_cfi_oprnd2; -} -dw_cfi_node; - -/* 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 used 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 -{ - char *dw_fde_begin; - char *dw_fde_current_label; - char *dw_fde_end; - dw_cfi_ref dw_fde_cfi; -} -dw_fde_node; - -/* Maximum size (in bytes) of an artificially generated label. */ -#define MAX_ARTIFICIAL_LABEL_BYTES 30 - -/* Make sure we know the sizes of the various types dwarf can describe. These - are only defaults. If the sizes are different for your target, you should - override these values by defining the appropriate symbols in your tm.h - file. */ - -#ifndef CHAR_TYPE_SIZE -#define CHAR_TYPE_SIZE BITS_PER_UNIT -#endif -#ifndef PTR_SIZE -#define PTR_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 - -#define DWARF_VERSION 2 - -/* Round SIZE up to the nearest BOUNDARY. */ -#define DWARF_ROUND(SIZE,BOUNDARY) \ - (((SIZE) + (BOUNDARY) - 1) & ~((BOUNDARY) - 1)) - -/* Offsets recorded in opcodes are a multiple of this alignment factor. */ -#ifdef STACK_GROWS_DOWNWARD -#define DWARF_CIE_DATA_ALIGNMENT (-UNITS_PER_WORD) -#else -#define DWARF_CIE_DATA_ALIGNMENT UNITS_PER_WORD -#endif - -/* A pointer to the base of a table that contains frame description - information for each routine. */ -static dw_fde_ref fde_table; - -/* Number of elements currently allocated for fde_table. */ -static unsigned fde_table_allocated; - -/* Number of elements in fde_table currently in use. */ -static unsigned fde_table_in_use; - -/* Size (in elements) of increments by which we may expand the - fde_table. */ -#define FDE_TABLE_INCREMENT 256 - -/* A list of call frame insns for the CIE. */ -static dw_cfi_ref cie_cfi_head; - -/* The number of the current function definition for which debugging - information is being generated. These numbers range from 1 up to the - maximum number of function definitions contained within the current - compilation unit. These numbers are used to create unique label id's - unique to each function definition. */ -static unsigned current_funcdef_number = 0; - -/* 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; - -/* Forward declarations for functions defined in this file. */ - -static char *stripattributes PROTO((char *)); -static char *dwarf_cfi_name PROTO((unsigned)); -static dw_cfi_ref new_cfi PROTO((void)); -static void add_cfi PROTO((dw_cfi_ref *, dw_cfi_ref)); -static unsigned long size_of_uleb128 PROTO((unsigned long)); -static unsigned long size_of_sleb128 PROTO((long)); -static void output_uleb128 PROTO((unsigned long)); -static void output_sleb128 PROTO((long)); -static void add_fde_cfi PROTO((char *, dw_cfi_ref)); -static void lookup_cfa_1 PROTO((dw_cfi_ref, unsigned long *, - long *)); -static void lookup_cfa PROTO((unsigned long *, long *)); -static void reg_save PROTO((char *, unsigned, unsigned, - long)); -static void initial_return_save PROTO((rtx)); -static void output_cfi PROTO((dw_cfi_ref, dw_fde_ref)); -static void output_call_frame_info PROTO((int)); -static unsigned reg_number PROTO((rtx)); -static void dwarf2out_stack_adjust PROTO((rtx)); - -/* Definitions of defaults for assembler-dependent names of various - pseudo-ops and section names. - Theses may be overridden in the tm.h file (if necessary) for a particular - assembler. */ - -#ifdef OBJECT_FORMAT_ELF -#ifndef UNALIGNED_SHORT_ASM_OP -#define UNALIGNED_SHORT_ASM_OP ".2byte" -#endif -#ifndef UNALIGNED_INT_ASM_OP -#define UNALIGNED_INT_ASM_OP ".4byte" -#endif -#ifndef UNALIGNED_DOUBLE_INT_ASM_OP -#define UNALIGNED_DOUBLE_INT_ASM_OP ".8byte" -#endif -#endif /* OBJECT_FORMAT_ELF */ - -#ifndef ASM_BYTE_OP -#define ASM_BYTE_OP ".byte" -#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) - -/* Pseudo-op for defining a new section. */ -#ifndef SECTION_ASM_OP -#define SECTION_ASM_OP ".section" -#endif - -/* The default format used by the ASM_OUTPUT_SECTION macro (see below) to - print the SECTION_ASM_OP and the section name. The default here works for - almost all svr4 assemblers, except for the sparc, where the section name - must be enclosed in double quotes. (See sparcv4.h). */ -#ifndef SECTION_FORMAT -#ifdef PUSHSECTION_FORMAT -#define SECTION_FORMAT PUSHSECTION_FORMAT -#else -#define SECTION_FORMAT "\t%s\t%s\n" -#endif -#endif - -#ifndef FRAME_SECTION -#define 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 -#define CIE_AFTER_SIZE_LABEL "LSCIE" -#define CIE_END_LABEL "LECIE" -#define CIE_LENGTH_LABEL "LLCIE" -#define FDE_AFTER_SIZE_LABEL "LSFDE" -#define FDE_END_LABEL "LEFDE" -#define FDE_LENGTH_LABEL "LLFDE" - -/* Definitions of defaults for various types of primitive assembly language - output operations. These may be overridden from within the tm.h file, - but typically, that is unnecessary. */ - -#ifndef ASM_OUTPUT_SECTION -#define ASM_OUTPUT_SECTION(FILE, SECTION) \ - fprintf ((FILE), SECTION_FORMAT, SECTION_ASM_OP, SECTION) -#endif - -#ifndef ASM_OUTPUT_DWARF_DATA1 -#define ASM_OUTPUT_DWARF_DATA1(FILE,VALUE) \ - fprintf ((FILE), "\t%s\t0x%x", ASM_BYTE_OP, (unsigned) (VALUE)) -#endif - -#ifndef ASM_OUTPUT_DWARF_DELTA1 -#define ASM_OUTPUT_DWARF_DELTA1(FILE,LABEL1,LABEL2) \ - do { fprintf ((FILE), "\t%s\t", ASM_BYTE_OP); \ - assemble_name (FILE, LABEL1); \ - fprintf (FILE, "-"); \ - assemble_name (FILE, LABEL2); \ - } while (0) -#endif - -#ifdef UNALIGNED_INT_ASM_OP - -#ifndef UNALIGNED_OFFSET_ASM_OP -#define UNALIGNED_OFFSET_ASM_OP \ - (DWARF_OFFSET_SIZE == 8 ? UNALIGNED_DOUBLE_INT_ASM_OP : UNALIGNED_INT_ASM_OP) -#endif - -#ifndef UNALIGNED_WORD_ASM_OP -#define UNALIGNED_WORD_ASM_OP \ - (PTR_SIZE == 8 ? UNALIGNED_DOUBLE_INT_ASM_OP : UNALIGNED_INT_ASM_OP) -#endif - -#ifndef ASM_OUTPUT_DWARF_DELTA2 -#define ASM_OUTPUT_DWARF_DELTA2(FILE,LABEL1,LABEL2) \ - do { fprintf ((FILE), "\t%s\t", UNALIGNED_SHORT_ASM_OP); \ - assemble_name (FILE, LABEL1); \ - fprintf (FILE, "-"); \ - assemble_name (FILE, LABEL2); \ - } while (0) -#endif - -#ifndef ASM_OUTPUT_DWARF_DELTA4 -#define ASM_OUTPUT_DWARF_DELTA4(FILE,LABEL1,LABEL2) \ - do { fprintf ((FILE), "\t%s\t", UNALIGNED_INT_ASM_OP); \ - assemble_name (FILE, LABEL1); \ - fprintf (FILE, "-"); \ - assemble_name (FILE, LABEL2); \ - } while (0) -#endif - -#ifndef ASM_OUTPUT_DWARF_DELTA -#define ASM_OUTPUT_DWARF_DELTA(FILE,LABEL1,LABEL2) \ - do { fprintf ((FILE), "\t%s\t", UNALIGNED_OFFSET_ASM_OP); \ - assemble_name (FILE, LABEL1); \ - fprintf (FILE, "-"); \ - assemble_name (FILE, LABEL2); \ - } while (0) -#endif - -#ifndef ASM_OUTPUT_DWARF_ADDR_DELTA -#define ASM_OUTPUT_DWARF_ADDR_DELTA(FILE,LABEL1,LABEL2) \ - do { fprintf ((FILE), "\t%s\t", UNALIGNED_WORD_ASM_OP); \ - assemble_name (FILE, LABEL1); \ - fprintf (FILE, "-"); \ - assemble_name (FILE, LABEL2); \ - } while (0) -#endif - -#ifndef ASM_OUTPUT_DWARF_ADDR -#define ASM_OUTPUT_DWARF_ADDR(FILE,LABEL) \ - do { fprintf ((FILE), "\t%s\t", UNALIGNED_WORD_ASM_OP); \ - assemble_name (FILE, LABEL); \ - } while (0) -#endif - -#ifndef ASM_OUTPUT_DWARF_ADDR_CONST -#define ASM_OUTPUT_DWARF_ADDR_CONST(FILE,RTX) \ - do { \ - fprintf ((FILE), "\t%s\t", UNALIGNED_INT_ASM_OP); \ - output_addr_const ((FILE), (RTX)); \ - fputc ('\n', (FILE)); \ - } while (0) -#endif - -#ifndef ASM_OUTPUT_DWARF_OFFSET4 -#define ASM_OUTPUT_DWARF_OFFSET4(FILE,LABEL) \ - do { fprintf ((FILE), "\t%s\t", UNALIGNED_INT_ASM_OP); \ - assemble_name (FILE, LABEL); \ - } while (0) -#endif - -#ifndef ASM_OUTPUT_DWARF_OFFSET -#define ASM_OUTPUT_DWARF_OFFSET(FILE,LABEL) \ - do { fprintf ((FILE), "\t%s\t", UNALIGNED_OFFSET_ASM_OP); \ - assemble_name (FILE, LABEL); \ - } while (0) -#endif - -#ifndef ASM_OUTPUT_DWARF_DATA2 -#define ASM_OUTPUT_DWARF_DATA2(FILE,VALUE) \ - fprintf ((FILE), "\t%s\t0x%x", UNALIGNED_SHORT_ASM_OP, (unsigned) (VALUE)) -#endif - -#ifndef ASM_OUTPUT_DWARF_DATA4 -#define ASM_OUTPUT_DWARF_DATA4(FILE,VALUE) \ - fprintf ((FILE), "\t%s\t0x%x", UNALIGNED_INT_ASM_OP, (unsigned) (VALUE)) -#endif - -#ifndef ASM_OUTPUT_DWARF_DATA -#define ASM_OUTPUT_DWARF_DATA(FILE,VALUE) \ - fprintf ((FILE), "\t%s\t0x%lx", UNALIGNED_OFFSET_ASM_OP, \ - (unsigned long) (VALUE)) -#endif - -#ifndef ASM_OUTPUT_DWARF_ADDR_DATA -#define ASM_OUTPUT_DWARF_ADDR_DATA(FILE,VALUE) \ - fprintf ((FILE), "\t%s\t0x%lx", UNALIGNED_WORD_ASM_OP, \ - (unsigned long) (VALUE)) -#endif - -#ifndef ASM_OUTPUT_DWARF_DATA8 -#define ASM_OUTPUT_DWARF_DATA8(FILE,HIGH_VALUE,LOW_VALUE) \ - do { \ - if (WORDS_BIG_ENDIAN) \ - { \ - fprintf ((FILE), "\t%s\t0x%lx\n", UNALIGNED_INT_ASM_OP, (HIGH_VALUE));\ - fprintf ((FILE), "\t%s\t0x%lx", UNALIGNED_INT_ASM_OP, (LOW_VALUE));\ - } \ - else \ - { \ - fprintf ((FILE), "\t%s\t0x%lx\n", UNALIGNED_INT_ASM_OP, (LOW_VALUE)); \ - fprintf ((FILE), "\t%s\t0x%lx", UNALIGNED_INT_ASM_OP, (HIGH_VALUE)); \ - } \ - } while (0) -#endif - -#else /* UNALIGNED_INT_ASM_OP */ - -/* We don't have unaligned support, let's hope the normal output works for - .debug_frame. */ - -#define ASM_OUTPUT_DWARF_ADDR(FILE,LABEL) \ - assemble_integer (gen_rtx_SYMBOL_REF (Pmode, LABEL), PTR_SIZE, 1) - -#define ASM_OUTPUT_DWARF_OFFSET4(FILE,LABEL) \ - assemble_integer (gen_rtx_SYMBOL_REF (SImode, LABEL), 4, 1) - -#define ASM_OUTPUT_DWARF_OFFSET(FILE,LABEL) \ - assemble_integer (gen_rtx_SYMBOL_REF (SImode, LABEL), 4, 1) - -#define ASM_OUTPUT_DWARF_DELTA2(FILE,LABEL1,LABEL2) \ - assemble_integer (gen_rtx_MINUS (HImode, \ - gen_rtx_SYMBOL_REF (Pmode, LABEL1), \ - gen_rtx_SYMBOL_REF (Pmode, LABEL2)), \ - 2, 1) - -#define ASM_OUTPUT_DWARF_DELTA4(FILE,LABEL1,LABEL2) \ - assemble_integer (gen_rtx_MINUS (SImode, \ - gen_rtx_SYMBOL_REF (Pmode, LABEL1), \ - gen_rtx_SYMBOL_REF (Pmode, LABEL2)), \ - 4, 1) - -#define ASM_OUTPUT_DWARF_ADDR_DELTA(FILE,LABEL1,LABEL2) \ - assemble_integer (gen_rtx_MINUS (Pmode, \ - gen_rtx_SYMBOL_REF (Pmode, LABEL1), \ - gen_rtx_SYMBOL_REF (Pmode, LABEL2)), \ - PTR_SIZE, 1) - -#define ASM_OUTPUT_DWARF_DELTA(FILE,LABEL1,LABEL2) \ - ASM_OUTPUT_DWARF_DELTA4 (FILE,LABEL1,LABEL2) - -#define ASM_OUTPUT_DWARF_DATA4(FILE,VALUE) \ - assemble_integer (GEN_INT (VALUE), 4, 1) - -#endif /* UNALIGNED_INT_ASM_OP */ - -#ifdef SET_ASM_OP -#ifndef ASM_OUTPUT_DEFINE_LABEL_DIFFERENCE_SYMBOL -#define ASM_OUTPUT_DEFINE_LABEL_DIFFERENCE_SYMBOL(FILE, SY, HI, LO) \ - do { \ - fprintf (FILE, "\t%s\t", SET_ASM_OP); \ - assemble_name (FILE, SY); \ - fputc (',', FILE); \ - assemble_name (FILE, HI); \ - fputc ('-', FILE); \ - assemble_name (FILE, LO); \ - } while (0) -#endif -#endif /* SET_ASM_OP */ - -/* This is similar to the default ASM_OUTPUT_ASCII, except that no trailing - newline is produced. When flag_debug_asm is asserted, we add commentary - at the end of the line, so we must avoid output of a newline here. */ -#ifndef ASM_OUTPUT_DWARF_STRING -#define ASM_OUTPUT_DWARF_STRING(FILE,P) \ - do { \ - register int slen = strlen(P); \ - register char *p = (P); \ - register int i; \ - fprintf (FILE, "\t.ascii \""); \ - for (i = 0; i < slen; i++) \ - { \ - register int c = p[i]; \ - if (c == '\"' || c == '\\') \ - putc ('\\', FILE); \ - if (c >= ' ' && c < 0177) \ - putc (c, FILE); \ - else \ - { \ - fprintf (FILE, "\\%o", c); \ - } \ - } \ - fprintf (FILE, "\\0\""); \ - } \ - while (0) -#endif - -/* The DWARF 2 CFA column which tracks the return address. Normally this - is the column for PC, or the first column after all of the hard - registers. */ -#ifndef DWARF_FRAME_RETURN_COLUMN -#ifdef PC_REGNUM -#define DWARF_FRAME_RETURN_COLUMN DWARF_FRAME_REGNUM (PC_REGNUM) -#else -#define DWARF_FRAME_RETURN_COLUMN FIRST_PSEUDO_REGISTER -#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_fp_regnum () -{ - return GEN_INT (DWARF_FRAME_REGNUM (HARD_FRAME_POINTER_REGNUM)); -} - -/* The offset from the incoming value of %sp to the top of the stack frame - for the current function. */ -#ifndef INCOMING_FRAME_SP_OFFSET -#define INCOMING_FRAME_SP_OFFSET 0 -#endif - -/* 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 (s) - char *s; -{ - char *stripped = xmalloc (strlen (s) + 2); - char *p = stripped; - - *p++ = '*'; - - while (*s && *s != ',') - *p++ = *s++; - - *p = '\0'; - return stripped; -} - -/* Return the register number described by a given RTL node. */ - -static unsigned -reg_number (rtl) - register rtx rtl; -{ - register unsigned regno = REGNO (rtl); - - if (regno >= FIRST_PSEUDO_REGISTER) - { - warning ("internal regno botch: regno = %d\n", regno); - regno = 0; - } - - regno = DBX_REGISTER_NUMBER (regno); - return regno; -} - -struct reg_size_range -{ - int beg; - int end; - int size; -}; - -/* Given a register number in REG_TREE, return an rtx for its size in bytes. - We do this in kind of a roundabout way, by building up a list of - register size ranges and seeing where our register falls in one of those - ranges. We need to do it this way because REG_TREE is not a constant, - and the target macros were not designed to make this task easy. */ - -rtx -expand_builtin_dwarf_reg_size (reg_tree, target) - tree reg_tree; - rtx target; -{ - enum machine_mode mode; - int size; - struct reg_size_range ranges[5]; - tree t, t2; - - int i = 0; - int n_ranges = 0; - int last_size = -1; - - for (; i < FIRST_PSEUDO_REGISTER; ++i) - { - /* The return address is out of order on the MIPS, and we don't use - copy_reg for it anyway, so we don't care here how large it is. */ - if (DWARF_FRAME_REGNUM (i) == DWARF_FRAME_RETURN_COLUMN) - continue; - - mode = reg_raw_mode[i]; - - /* CCmode is arbitrarily given a size of 4 bytes. It is more useful - to use the same size as word_mode, since that reduces the number - of ranges we need. It should not matter, since the result should - never be used for a condition code register anyways. */ - if (GET_MODE_CLASS (mode) == MODE_CC) - mode = word_mode; - - size = GET_MODE_SIZE (mode); - - /* If this register is not valid in the specified mode and - we have a previous size, use that for the size of this - register to avoid making junk tiny ranges. */ - if (! HARD_REGNO_MODE_OK (i, mode) && last_size != -1) - size = last_size; - - if (size != last_size) - { - ranges[n_ranges].beg = i; - ranges[n_ranges].size = last_size = size; - ++n_ranges; - if (n_ranges >= 5) - abort (); - } - ranges[n_ranges-1].end = i; - } - - /* The usual case: fp regs surrounded by general regs. */ - if (n_ranges == 3 && ranges[0].size == ranges[2].size) - { - if ((DWARF_FRAME_REGNUM (ranges[1].end) - - DWARF_FRAME_REGNUM (ranges[1].beg)) - != ranges[1].end - ranges[1].beg) - abort (); - t = fold (build (GE_EXPR, integer_type_node, reg_tree, - build_int_2 (DWARF_FRAME_REGNUM (ranges[1].beg), 0))); - t2 = fold (build (LE_EXPR, integer_type_node, reg_tree, - build_int_2 (DWARF_FRAME_REGNUM (ranges[1].end), 0))); - t = fold (build (TRUTH_ANDIF_EXPR, integer_type_node, t, t2)); - t = fold (build (COND_EXPR, integer_type_node, t, - build_int_2 (ranges[1].size, 0), - build_int_2 (ranges[0].size, 0))); - } - else - { - /* Initialize last_end to be larger than any possible - DWARF_FRAME_REGNUM. */ - int last_end = 0x7fffffff; - --n_ranges; - t = build_int_2 (ranges[n_ranges].size, 0); - do - { - int beg = DWARF_FRAME_REGNUM (ranges[n_ranges].beg); - int end = DWARF_FRAME_REGNUM (ranges[n_ranges].end); - if (beg < 0) - continue; - if (end >= last_end) - abort (); - last_end = end; - if (end - beg != ranges[n_ranges].end - ranges[n_ranges].beg) - abort (); - t2 = fold (build (LE_EXPR, integer_type_node, reg_tree, - build_int_2 (end, 0))); - t = fold (build (COND_EXPR, integer_type_node, t2, - build_int_2 (ranges[n_ranges].size, 0), t)); - } - while (--n_ranges >= 0); - } - return expand_expr (t, target, Pmode, 0); -} - -/* Convert a DWARF call frame info. operation to its string name */ - -static char * -dwarf_cfi_name (cfi_opc) - register unsigned 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"; - - /* 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"; - - default: - return "DW_CFA_<unknown>"; - } -} - -/* Return a pointer to a newly allocated Call Frame Instruction. */ - -static inline dw_cfi_ref -new_cfi () -{ - register dw_cfi_ref cfi = (dw_cfi_ref) xmalloc (sizeof (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 (list_head, cfi) - register dw_cfi_ref *list_head; - register dw_cfi_ref cfi; -{ - register dw_cfi_ref *p; - - /* 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. */ - -char * -dwarf2out_cfi_label () -{ - static char label[20]; - static unsigned long label_num = 0; - - ASM_GENERATE_INTERNAL_LABEL (label, "LCFI", 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 (label, cfi) - register char *label; - register dw_cfi_ref cfi; -{ - if (label) - { - register dw_fde_ref fde = &fde_table[fde_table_in_use - 1]; - - if (*label == 0) - label = dwarf2out_cfi_label (); - - if (fde->dw_fde_current_label == NULL - || strcmp (label, fde->dw_fde_current_label) != 0) - { - register dw_cfi_ref xcfi; - - fde->dw_fde_current_label = label = xstrdup (label); - - /* Set the location counter to the new label. */ - xcfi = new_cfi (); - xcfi->dw_cfi_opc = DW_CFA_advance_loc4; - xcfi->dw_cfi_oprnd1.dw_cfi_addr = label; - add_cfi (&fde->dw_fde_cfi, xcfi); - } - - add_cfi (&fde->dw_fde_cfi, cfi); - } - - else - add_cfi (&cie_cfi_head, cfi); -} - -/* Subroutine of lookup_cfa. */ - -static inline void -lookup_cfa_1 (cfi, regp, offsetp) - register dw_cfi_ref cfi; - register unsigned long *regp; - register long *offsetp; -{ - switch (cfi->dw_cfi_opc) - { - case DW_CFA_def_cfa_offset: - *offsetp = cfi->dw_cfi_oprnd1.dw_cfi_offset; - break; - case DW_CFA_def_cfa_register: - *regp = cfi->dw_cfi_oprnd1.dw_cfi_reg_num; - break; - case DW_CFA_def_cfa: - *regp = cfi->dw_cfi_oprnd1.dw_cfi_reg_num; - *offsetp = cfi->dw_cfi_oprnd2.dw_cfi_offset; - break; - default: - break; - } -} - -/* Find the previous value for the CFA. */ - -static void -lookup_cfa (regp, offsetp) - register unsigned long *regp; - register long *offsetp; -{ - register dw_cfi_ref cfi; - - *regp = (unsigned long) -1; - *offsetp = 0; - - for (cfi = cie_cfi_head; cfi; cfi = cfi->dw_cfi_next) - lookup_cfa_1 (cfi, regp, offsetp); - - if (fde_table_in_use) - { - register dw_fde_ref fde = &fde_table[fde_table_in_use - 1]; - for (cfi = fde->dw_fde_cfi; cfi; cfi = cfi->dw_cfi_next) - lookup_cfa_1 (cfi, regp, offsetp); - } -} - -/* The current rule for calculating the DWARF2 canonical frame address. */ -static unsigned long cfa_reg; -static long cfa_offset; - -/* The register used for saving registers to the stack, and its offset - from the CFA. */ -static unsigned cfa_store_reg; -static long cfa_store_offset; - -/* The running total of the size of arguments pushed onto the stack. */ -static long args_size; - -/* The last args_size we actually output. */ -static long 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 (label, reg, offset) - register char *label; - register unsigned reg; - register long offset; -{ - register dw_cfi_ref cfi; - unsigned long old_reg; - long old_offset; - - cfa_reg = reg; - cfa_offset = offset; - if (cfa_store_reg == reg) - cfa_store_offset = offset; - - reg = DWARF_FRAME_REGNUM (reg); - lookup_cfa (&old_reg, &old_offset); - - if (reg == old_reg && offset == old_offset) - return; - - cfi = new_cfi (); - - if (reg == old_reg) - { - cfi->dw_cfi_opc = DW_CFA_def_cfa_offset; - cfi->dw_cfi_oprnd1.dw_cfi_offset = offset; - } - -#ifndef MIPS_DEBUGGING_INFO /* SGI dbx thinks this means no offset. */ - else if (offset == old_offset && old_reg != (unsigned long) -1) - { - cfi->dw_cfi_opc = DW_CFA_def_cfa_register; - cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg; - } -#endif - - else - { - cfi->dw_cfi_opc = DW_CFA_def_cfa; - cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg; - cfi->dw_cfi_oprnd2.dw_cfi_offset = offset; - } - - 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 (label, reg, sreg, offset) - register char * label; - register unsigned reg; - register unsigned sreg; - register long offset; -{ - register dw_cfi_ref cfi = new_cfi (); - - cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg; - - /* The following comparison is correct. -1 is used to indicate that - the value isn't a register number. */ - if (sreg == (unsigned int) -1) - { - if (reg & ~0x3f) - /* The register number won't fit in 6 bits, so we have to use - the long form. */ - cfi->dw_cfi_opc = DW_CFA_offset_extended; - else - cfi->dw_cfi_opc = DW_CFA_offset; - - offset /= DWARF_CIE_DATA_ALIGNMENT; - if (offset < 0) - abort (); - cfi->dw_cfi_oprnd2.dw_cfi_offset = offset; - } - 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 (label) - register char * label; -{ - register 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 (label, size) - char *label; - long size; -{ - register 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 (label, reg, offset) - register char * label; - register unsigned reg; - register long offset; -{ - reg_save (label, DWARF_FRAME_REGNUM (reg), -1, offset); -} - -/* Entry point for saving the return address in the stack. - LABEL and OFFSET are passed to reg_save. */ - -void -dwarf2out_return_save (label, offset) - register char * label; - register long offset; -{ - reg_save (label, DWARF_FRAME_RETURN_COLUMN, -1, offset); -} - -/* Entry point for saving the return address in a register. - LABEL and SREG are passed to reg_save. */ - -void -dwarf2out_return_reg (label, sreg) - register char * label; - register unsigned sreg; -{ - reg_save (label, DWARF_FRAME_RETURN_COLUMN, sreg, 0); -} - -/* Record the initial position of the return address. RTL is - INCOMING_RETURN_ADDR_RTX. */ - -static void -initial_return_save (rtl) - register rtx rtl; -{ - unsigned int reg = (unsigned int) -1; - long offset = 0; - - switch (GET_CODE (rtl)) - { - case REG: - /* RA is in a register. */ - reg = reg_number (rtl); - break; - case MEM: - /* RA is on the stack. */ - rtl = XEXP (rtl, 0); - switch (GET_CODE (rtl)) - { - case REG: - if (REGNO (rtl) != STACK_POINTER_REGNUM) - abort (); - offset = 0; - break; - case PLUS: - if (REGNO (XEXP (rtl, 0)) != STACK_POINTER_REGNUM) - abort (); - offset = INTVAL (XEXP (rtl, 1)); - break; - case MINUS: - if (REGNO (XEXP (rtl, 0)) != STACK_POINTER_REGNUM) - abort (); - offset = -INTVAL (XEXP (rtl, 1)); - break; - default: - abort (); - } - 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. */ - if (GET_CODE (XEXP (rtl, 1)) != CONST_INT) - abort (); - initial_return_save (XEXP (rtl, 0)); - return; - default: - abort (); - } - - reg_save (NULL, DWARF_FRAME_RETURN_COLUMN, reg, offset - cfa_offset); -} - -/* 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 (insn) - rtx insn; -{ - long offset; - char *label; - - if (! asynchronous_exceptions && GET_CODE (insn) == CALL_INSN) - { - /* 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); - assert (GET_CODE (insn) == CALL); - dwarf2out_args_size ("", INTVAL (XEXP (insn, 1))); - return; - } - - /* If only calls can throw, and we have a frame pointer, - save up adjustments until we see the CALL_INSN. */ - else if (! asynchronous_exceptions - && cfa_reg != STACK_POINTER_REGNUM) - return; - - if (GET_CODE (insn) == BARRIER) - { - /* When we see a BARRIER, we know to reset args_size to 0. Usually - the compiler will have already emitted a stack adjustment, but - doesn't bother for calls to noreturn functions. */ -#ifdef STACK_GROWS_DOWNWARD - offset = -args_size; -#else - offset = args_size; -#endif - } - else if (GET_CODE (PATTERN (insn)) == SET) - { - rtx src, dest; - enum rtx_code code; - - insn = PATTERN (insn); - src = SET_SRC (insn); - dest = SET_DEST (insn); - - if (dest == stack_pointer_rtx) - { - /* (set (reg sp) (plus (reg sp) (const_int))) */ - code = GET_CODE (src); - if (! (code == PLUS || code == MINUS) - || XEXP (src, 0) != stack_pointer_rtx - || GET_CODE (XEXP (src, 1)) != CONST_INT) - return; - - offset = INTVAL (XEXP (src, 1)); - } - else if (GET_CODE (dest) == MEM) - { - /* (set (mem (pre_dec (reg sp))) (foo)) */ - src = XEXP (dest, 0); - code = GET_CODE (src); - - if (! (code == PRE_DEC || code == PRE_INC) - || XEXP (src, 0) != stack_pointer_rtx) - return; - - offset = GET_MODE_SIZE (GET_MODE (dest)); - } - else - return; - - if (code == PLUS || code == PRE_INC) - offset = -offset; - } - else - return; - - if (offset == 0) - return; - - if (cfa_reg == STACK_POINTER_REGNUM) - cfa_offset += offset; - -#ifndef STACK_GROWS_DOWNWARD - offset = -offset; -#endif - args_size += offset; - if (args_size < 0) - args_size = 0; - - label = dwarf2out_cfi_label (); - dwarf2out_def_cfa (label, cfa_reg, cfa_offset); - dwarf2out_args_size (label, args_size); -} - -/* 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. */ - -void -dwarf2out_frame_debug (insn) - rtx insn; -{ - char *label; - rtx src, dest; - long offset; - - /* A temporary register used in adjusting SP or setting up the store_reg. */ - static unsigned cfa_temp_reg; - static long cfa_temp_value; - - if (insn == NULL_RTX) - { - /* Set up state for generating call frame debug info. */ - lookup_cfa (&cfa_reg, &cfa_offset); - if (cfa_reg != DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM)) - abort (); - cfa_reg = STACK_POINTER_REGNUM; - cfa_store_reg = cfa_reg; - cfa_store_offset = cfa_offset; - cfa_temp_reg = -1; - cfa_temp_value = 0; - return; - } - - if (! RTX_FRAME_RELATED_P (insn)) - { - dwarf2out_stack_adjust (insn); - return; - } - - label = dwarf2out_cfi_label (); - - src = find_reg_note (insn, REG_FRAME_RELATED_EXPR, NULL_RTX); - if (src) - insn = XEXP (src, 0); - else - insn = PATTERN (insn); - - /* Assume that in a PARALLEL prologue insn, only the first elt is - significant. Currently this is true. */ - if (GET_CODE (insn) == PARALLEL) - insn = XVECEXP (insn, 0, 0); - if (GET_CODE (insn) != SET) - abort (); - - src = SET_SRC (insn); - dest = SET_DEST (insn); - - switch (GET_CODE (dest)) - { - case REG: - /* Update the CFA rule wrt SP or FP. Make sure src is - relative to the current CFA register. */ - switch (GET_CODE (src)) - { - /* Setting FP from SP. */ - case REG: - if (cfa_reg != (unsigned) REGNO (src)) - abort (); - if (REGNO (dest) != STACK_POINTER_REGNUM - && !(frame_pointer_needed - && REGNO (dest) == HARD_FRAME_POINTER_REGNUM)) - abort (); - cfa_reg = REGNO (dest); - break; - - case PLUS: - case MINUS: - if (dest == stack_pointer_rtx) - { - /* Adjusting SP. */ - switch (GET_CODE (XEXP (src, 1))) - { - case CONST_INT: - offset = INTVAL (XEXP (src, 1)); - break; - case REG: - if ((unsigned) REGNO (XEXP (src, 1)) != cfa_temp_reg) - abort (); - offset = cfa_temp_value; - break; - default: - abort (); - } - - if (XEXP (src, 0) == hard_frame_pointer_rtx) - { - /* Restoring SP from FP in the epilogue. */ - if (cfa_reg != (unsigned) HARD_FRAME_POINTER_REGNUM) - abort (); - cfa_reg = STACK_POINTER_REGNUM; - } - else if (XEXP (src, 0) != stack_pointer_rtx) - abort (); - - if (GET_CODE (src) == PLUS) - 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) - { - /* Either setting the FP from an offset of the SP, - or adjusting the FP */ - if (! frame_pointer_needed - || REGNO (dest) != HARD_FRAME_POINTER_REGNUM) - abort (); - - if (XEXP (src, 0) == stack_pointer_rtx - && GET_CODE (XEXP (src, 1)) == CONST_INT) - { - if (cfa_reg != STACK_POINTER_REGNUM) - abort (); - offset = INTVAL (XEXP (src, 1)); - if (GET_CODE (src) == PLUS) - offset = -offset; - cfa_offset += offset; - cfa_reg = HARD_FRAME_POINTER_REGNUM; - } - else if (XEXP (src, 0) == hard_frame_pointer_rtx - && GET_CODE (XEXP (src, 1)) == CONST_INT) - { - if (cfa_reg != (unsigned) HARD_FRAME_POINTER_REGNUM) - abort (); - offset = INTVAL (XEXP (src, 1)); - if (GET_CODE (src) == PLUS) - offset = -offset; - cfa_offset += offset; - } - - else - abort(); - } - else - { - if (GET_CODE (src) != PLUS - || XEXP (src, 1) != stack_pointer_rtx) - abort (); - if (GET_CODE (XEXP (src, 0)) != REG - || (unsigned) REGNO (XEXP (src, 0)) != cfa_temp_reg) - abort (); - if (cfa_reg != STACK_POINTER_REGNUM) - abort (); - cfa_store_reg = REGNO (dest); - cfa_store_offset = cfa_offset - cfa_temp_value; - } - break; - - case CONST_INT: - cfa_temp_reg = REGNO (dest); - cfa_temp_value = INTVAL (src); - break; - - case IOR: - if (GET_CODE (XEXP (src, 0)) != REG - || (unsigned) REGNO (XEXP (src, 0)) != cfa_temp_reg - || (unsigned) REGNO (dest) != cfa_temp_reg - || GET_CODE (XEXP (src, 1)) != CONST_INT) - abort (); - cfa_temp_value |= INTVAL (XEXP (src, 1)); - break; - - default: - abort (); - } - dwarf2out_def_cfa (label, cfa_reg, cfa_offset); - break; - - case MEM: - /* Saving a register to the stack. Make sure dest is relative to the - CFA register. */ - if (GET_CODE (src) != REG) - abort (); - switch (GET_CODE (XEXP (dest, 0))) - { - /* With a push. */ - case PRE_INC: - case PRE_DEC: - offset = GET_MODE_SIZE (GET_MODE (dest)); - if (GET_CODE (XEXP (dest, 0)) == PRE_INC) - offset = -offset; - - if (REGNO (XEXP (XEXP (dest, 0), 0)) != STACK_POINTER_REGNUM - || cfa_store_reg != STACK_POINTER_REGNUM) - abort (); - cfa_store_offset += offset; - if (cfa_reg == STACK_POINTER_REGNUM) - cfa_offset = cfa_store_offset; - - offset = -cfa_store_offset; - break; - - /* With an offset. */ - case PLUS: - case MINUS: - offset = INTVAL (XEXP (XEXP (dest, 0), 1)); - if (GET_CODE (src) == MINUS) - offset = -offset; - - if (cfa_store_reg != (unsigned) REGNO (XEXP (XEXP (dest, 0), 0))) - abort (); - offset -= cfa_store_offset; - break; - - /* Without an offset. */ - case REG: - if (cfa_store_reg != (unsigned) REGNO (XEXP (dest, 0))) - abort(); - offset = -cfa_store_offset; - break; - - default: - abort (); - } - dwarf2out_def_cfa (label, cfa_reg, cfa_offset); - dwarf2out_reg_save (label, REGNO (src), offset); - break; - - default: - abort (); - } -} - -/* Return the size of an unsigned LEB128 quantity. */ - -static inline unsigned long -size_of_uleb128 (value) - register unsigned long value; -{ - register unsigned long size = 0; - register unsigned byte; - - do - { - byte = (value & 0x7f); - value >>= 7; - size += 1; - } - while (value != 0); - - return size; -} - -/* Return the size of a signed LEB128 quantity. */ - -static inline unsigned long -size_of_sleb128 (value) - register long value; -{ - register unsigned long size = 0; - register unsigned byte; - - do - { - byte = (value & 0x7f); - value >>= 7; - size += 1; - } - while (!(((value == 0) && ((byte & 0x40) == 0)) - || ((value == -1) && ((byte & 0x40) != 0)))); - - return size; -} - -/* Output an unsigned LEB128 quantity. */ - -static void -output_uleb128 (value) - register unsigned long value; -{ - unsigned long save_value = value; - - fprintf (asm_out_file, "\t%s\t", ASM_BYTE_OP); - do - { - register unsigned byte = (value & 0x7f); - value >>= 7; - if (value != 0) - /* More bytes to follow. */ - byte |= 0x80; - - fprintf (asm_out_file, "0x%x", byte); - if (value != 0) - fprintf (asm_out_file, ","); - } - while (value != 0); - - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s ULEB128 0x%lx", ASM_COMMENT_START, save_value); -} - -/* Output an signed LEB128 quantity. */ - -static void -output_sleb128 (value) - register long value; -{ - register int more; - register unsigned byte; - long save_value = value; - - fprintf (asm_out_file, "\t%s\t", ASM_BYTE_OP); - do - { - byte = (value & 0x7f); - /* arithmetic shift */ - value >>= 7; - more = !((((value == 0) && ((byte & 0x40) == 0)) - || ((value == -1) && ((byte & 0x40) != 0)))); - if (more) - byte |= 0x80; - - fprintf (asm_out_file, "0x%x", byte); - if (more) - fprintf (asm_out_file, ","); - } - - while (more); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s SLEB128 %ld", ASM_COMMENT_START, save_value); -} - -/* Output a Call Frame Information opcode and its operand(s). */ - -static void -output_cfi (cfi, fde) - register dw_cfi_ref cfi; - register dw_fde_ref fde; -{ - if (cfi->dw_cfi_opc == DW_CFA_advance_loc) - { - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, - cfi->dw_cfi_opc - | (cfi->dw_cfi_oprnd1.dw_cfi_offset & 0x3f)); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s DW_CFA_advance_loc 0x%lx", - ASM_COMMENT_START, cfi->dw_cfi_oprnd1.dw_cfi_offset); - fputc ('\n', asm_out_file); - } - - else if (cfi->dw_cfi_opc == DW_CFA_offset) - { - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, - cfi->dw_cfi_opc - | (cfi->dw_cfi_oprnd1.dw_cfi_reg_num & 0x3f)); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s DW_CFA_offset, column 0x%lx", - ASM_COMMENT_START, cfi->dw_cfi_oprnd1.dw_cfi_reg_num); - - fputc ('\n', asm_out_file); - output_uleb128 (cfi->dw_cfi_oprnd2.dw_cfi_offset); - fputc ('\n', asm_out_file); - } - else if (cfi->dw_cfi_opc == DW_CFA_restore) - { - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, - cfi->dw_cfi_opc - | (cfi->dw_cfi_oprnd1.dw_cfi_reg_num & 0x3f)); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s DW_CFA_restore, column 0x%lx", - ASM_COMMENT_START, cfi->dw_cfi_oprnd1.dw_cfi_reg_num); - - fputc ('\n', asm_out_file); - } - else - { - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, cfi->dw_cfi_opc); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s %s", ASM_COMMENT_START, - dwarf_cfi_name (cfi->dw_cfi_opc)); - - fputc ('\n', asm_out_file); - switch (cfi->dw_cfi_opc) - { - case DW_CFA_set_loc: - ASM_OUTPUT_DWARF_ADDR (asm_out_file, cfi->dw_cfi_oprnd1.dw_cfi_addr); - fputc ('\n', asm_out_file); - break; - case DW_CFA_advance_loc1: - ASM_OUTPUT_DWARF_DELTA1 (asm_out_file, - cfi->dw_cfi_oprnd1.dw_cfi_addr, - fde->dw_fde_current_label); - fputc ('\n', asm_out_file); - fde->dw_fde_current_label = cfi->dw_cfi_oprnd1.dw_cfi_addr; - break; - case DW_CFA_advance_loc2: - ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, - cfi->dw_cfi_oprnd1.dw_cfi_addr, - fde->dw_fde_current_label); - fputc ('\n', asm_out_file); - fde->dw_fde_current_label = cfi->dw_cfi_oprnd1.dw_cfi_addr; - break; - case DW_CFA_advance_loc4: - ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, - cfi->dw_cfi_oprnd1.dw_cfi_addr, - fde->dw_fde_current_label); - fputc ('\n', asm_out_file); - fde->dw_fde_current_label = cfi->dw_cfi_oprnd1.dw_cfi_addr; - break; -#ifdef MIPS_DEBUGGING_INFO - case DW_CFA_MIPS_advance_loc8: - /* TODO: not currently implemented. */ - abort (); - break; -#endif - case DW_CFA_offset_extended: - case DW_CFA_def_cfa: - output_uleb128 (cfi->dw_cfi_oprnd1.dw_cfi_reg_num); - fputc ('\n', asm_out_file); - output_uleb128 (cfi->dw_cfi_oprnd2.dw_cfi_offset); - fputc ('\n', asm_out_file); - break; - case DW_CFA_restore_extended: - case DW_CFA_undefined: - output_uleb128 (cfi->dw_cfi_oprnd1.dw_cfi_reg_num); - fputc ('\n', asm_out_file); - break; - case DW_CFA_same_value: - case DW_CFA_def_cfa_register: - output_uleb128 (cfi->dw_cfi_oprnd1.dw_cfi_reg_num); - fputc ('\n', asm_out_file); - break; - case DW_CFA_register: - output_uleb128 (cfi->dw_cfi_oprnd1.dw_cfi_reg_num); - fputc ('\n', asm_out_file); - output_uleb128 (cfi->dw_cfi_oprnd2.dw_cfi_reg_num); - fputc ('\n', asm_out_file); - break; - case DW_CFA_def_cfa_offset: - output_uleb128 (cfi->dw_cfi_oprnd1.dw_cfi_offset); - fputc ('\n', asm_out_file); - break; - case DW_CFA_GNU_window_save: - break; - case DW_CFA_GNU_args_size: - output_uleb128 (cfi->dw_cfi_oprnd1.dw_cfi_offset); - fputc ('\n', asm_out_file); - break; - default: - break; - } - } -} - -#if !defined (EH_FRAME_SECTION) -#if defined (EH_FRAME_SECTION_ASM_OP) -#define EH_FRAME_SECTION() eh_frame_section(); -#else -#if defined (ASM_OUTPUT_SECTION_NAME) -#define EH_FRAME_SECTION() \ - do { \ - named_section (NULL_TREE, ".eh_frame", 0); \ - } while (0) -#endif -#endif -#endif - -/* If we aren't using crtstuff to run ctors, don't use it for EH. */ -#if !defined (HAS_INIT_SECTION) && !defined (INIT_SECTION_ASM_OP) -#undef EH_FRAME_SECTION -#endif - -/* Output the call frame information used to used to record information - that relates to calculating the frame pointer, and records the - location of saved registers. */ - -static void -output_call_frame_info (for_eh) - int for_eh; -{ - register unsigned long i; - register dw_fde_ref fde; - register dw_cfi_ref cfi; - char l1[20], l2[20]; -#ifdef ASM_OUTPUT_DEFINE_LABEL_DIFFERENCE_SYMBOL - char ld[20]; -#endif - - /* Do we want to include a pointer to the exception table? */ - int eh_ptr = for_eh && exception_table_p (); - - fputc ('\n', asm_out_file); - - /* We're going to be generating comments, so turn on app. */ - if (flag_debug_asm) - app_enable (); - - if (for_eh) - { -#ifdef EH_FRAME_SECTION - EH_FRAME_SECTION (); -#else - tree label = get_file_function_name ('F'); - - force_data_section (); - ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (PTR_SIZE)); - ASM_GLOBALIZE_LABEL (asm_out_file, IDENTIFIER_POINTER (label)); - ASM_OUTPUT_LABEL (asm_out_file, IDENTIFIER_POINTER (label)); -#endif - assemble_label ("__FRAME_BEGIN__"); - } - else - ASM_OUTPUT_SECTION (asm_out_file, FRAME_SECTION); - - /* Output the CIE. */ - ASM_GENERATE_INTERNAL_LABEL (l1, CIE_AFTER_SIZE_LABEL, for_eh); - ASM_GENERATE_INTERNAL_LABEL (l2, CIE_END_LABEL, for_eh); -#ifdef ASM_OUTPUT_DEFINE_LABEL_DIFFERENCE_SYMBOL - ASM_GENERATE_INTERNAL_LABEL (ld, CIE_LENGTH_LABEL, for_eh); - if (for_eh) - ASM_OUTPUT_DWARF_OFFSET4 (asm_out_file, ld); - else - ASM_OUTPUT_DWARF_OFFSET (asm_out_file, ld); -#else - if (for_eh) - ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, l2, l1); - else - ASM_OUTPUT_DWARF_DELTA (asm_out_file, l2, l1); -#endif - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s Length of Common Information Entry", - ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_LABEL (asm_out_file, l1); - - if (for_eh) - /* Now that the CIE pointer is PC-relative for EH, - use 0 to identify the CIE. */ - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 0); - else - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, DW_CIE_ID); - - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s CIE Identifier Tag", ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - if (! for_eh && DWARF_OFFSET_SIZE == 8) - { - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, DW_CIE_ID); - fputc ('\n', asm_out_file); - } - - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_CIE_VERSION); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s CIE Version", ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - if (eh_ptr) - { - /* The CIE contains a pointer to the exception region info for the - frame. Make the augmentation string three bytes (including the - trailing null) so the pointer is 4-byte aligned. The Solaris ld - can't handle unaligned relocs. */ - if (flag_debug_asm) - { - ASM_OUTPUT_DWARF_STRING (asm_out_file, "eh"); - fprintf (asm_out_file, "\t%s CIE Augmentation", ASM_COMMENT_START); - } - else - { - ASM_OUTPUT_ASCII (asm_out_file, "eh", 3); - } - fputc ('\n', asm_out_file); - - ASM_OUTPUT_DWARF_ADDR (asm_out_file, "__EXCEPTION_TABLE__"); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s pointer to exception region info", - ASM_COMMENT_START); - } - else - { - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s CIE Augmentation (none)", - ASM_COMMENT_START); - } - - fputc ('\n', asm_out_file); - output_uleb128 (1); - if (flag_debug_asm) - fprintf (asm_out_file, " (CIE Code Alignment Factor)"); - - fputc ('\n', asm_out_file); - output_sleb128 (DWARF_CIE_DATA_ALIGNMENT); - if (flag_debug_asm) - fprintf (asm_out_file, " (CIE Data Alignment Factor)"); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DWARF_FRAME_RETURN_COLUMN); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s CIE RA Column", ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - - for (cfi = cie_cfi_head; cfi != NULL; cfi = cfi->dw_cfi_next) - output_cfi (cfi, NULL); - - /* Pad the CIE out to an address sized boundary. */ - ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (PTR_SIZE)); - ASM_OUTPUT_LABEL (asm_out_file, l2); -#ifdef ASM_OUTPUT_DEFINE_LABEL_DIFFERENCE_SYMBOL - ASM_OUTPUT_DEFINE_LABEL_DIFFERENCE_SYMBOL (asm_out_file, ld, l2, l1); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s CIE Length Symbol", ASM_COMMENT_START); - fputc ('\n', asm_out_file); -#endif - - /* Loop through all of the FDE's. */ - for (i = 0; i < fde_table_in_use; ++i) - { - fde = &fde_table[i]; - - 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); -#ifdef ASM_OUTPUT_DEFINE_LABEL_DIFFERENCE_SYMBOL - ASM_GENERATE_INTERNAL_LABEL (ld, FDE_LENGTH_LABEL, for_eh + i*2); - if (for_eh) - ASM_OUTPUT_DWARF_OFFSET4 (asm_out_file, ld); - else - ASM_OUTPUT_DWARF_OFFSET (asm_out_file, ld); -#else - if (for_eh) - ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, l2, l1); - else - ASM_OUTPUT_DWARF_DELTA (asm_out_file, l2, l1); -#endif - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s FDE Length", ASM_COMMENT_START); - fputc ('\n', asm_out_file); - ASM_OUTPUT_LABEL (asm_out_file, l1); - - /* ??? This always emits a 4 byte offset when for_eh is true, but it - emits a target dependent sized offset when for_eh is not true. - This inconsistency may confuse gdb. The only case where we need a - non-4 byte offset is for the Irix6 N64 ABI, so we may lose SGI - compatibility if we emit a 4 byte offset. We need a 4 byte offset - though in order to be compatible with the dwarf_fde struct in frame.c. - If the for_eh case is changed, then the struct in frame.c has - to be adjusted appropriately. */ - if (for_eh) - ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, l1, "__FRAME_BEGIN__"); - else - ASM_OUTPUT_DWARF_OFFSET (asm_out_file, stripattributes (FRAME_SECTION)); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s FDE CIE offset", ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_ADDR (asm_out_file, fde->dw_fde_begin); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s FDE initial location", ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_ADDR_DELTA (asm_out_file, - fde->dw_fde_end, fde->dw_fde_begin); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s FDE address range", ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - - /* 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); - - /* Pad the FDE out to an address sized boundary. */ - ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (PTR_SIZE)); - ASM_OUTPUT_LABEL (asm_out_file, l2); -#ifdef ASM_OUTPUT_DEFINE_LABEL_DIFFERENCE_SYMBOL - ASM_OUTPUT_DEFINE_LABEL_DIFFERENCE_SYMBOL (asm_out_file, ld, l2, l1); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s FDE Length Symbol", ASM_COMMENT_START); - fputc ('\n', asm_out_file); -#endif - } -#ifndef EH_FRAME_SECTION - if (for_eh) - { - /* Emit terminating zero for table. */ - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 0); - fputc ('\n', asm_out_file); - } -#endif -#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 () -{ - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - register dw_fde_ref fde; - - ++current_funcdef_number; - - function_section (current_function_decl); - ASM_GENERATE_INTERNAL_LABEL (label, FUNC_BEGIN_LABEL, - current_funcdef_number); - ASM_OUTPUT_LABEL (asm_out_file, label); - - /* Expand the fde table if necessary. */ - if (fde_table_in_use == fde_table_allocated) - { - fde_table_allocated += FDE_TABLE_INCREMENT; - fde_table - = (dw_fde_ref) xrealloc (fde_table, - fde_table_allocated * 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++]; - fde->dw_fde_begin = xstrdup (label); - fde->dw_fde_current_label = NULL; - fde->dw_fde_end = NULL; - fde->dw_fde_cfi = NULL; - - args_size = old_args_size = 0; -} - -/* Output a marker (i.e. a label) for the absolute end of the generated code - for a function definition. This gets called *after* the epilogue code has - been generated. */ - -void -dwarf2out_end_epilogue () -{ - dw_fde_ref fde; - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - /* Output a label to mark the endpoint of the code generated for this - function. */ - ASM_GENERATE_INTERNAL_LABEL (label, FUNC_END_LABEL, current_funcdef_number); - ASM_OUTPUT_LABEL (asm_out_file, label); - fde = &fde_table[fde_table_in_use - 1]; - fde->dw_fde_end = xstrdup (label); -} - -void -dwarf2out_frame_init () -{ - /* Allocate the initial hunk of the fde_table. */ - fde_table - = (dw_fde_ref) xmalloc (FDE_TABLE_INCREMENT * sizeof (dw_fde_node)); - zero_memory ((char *) fde_table, FDE_TABLE_INCREMENT * sizeof (dw_fde_node)); - fde_table_allocated = FDE_TABLE_INCREMENT; - fde_table_in_use = 0; - - /* Generate the CFA instructions common to all FDE's. Do it now for the - sake of lookup_cfa. */ - -#ifdef DWARF2_UNWIND_INFO - /* On entry, the Canonical Frame Address is at SP. */ - dwarf2out_def_cfa (NULL, STACK_POINTER_REGNUM, INCOMING_FRAME_SP_OFFSET); - initial_return_save (INCOMING_RETURN_ADDR_RTX); -#endif -} - -void -dwarf2out_frame_finish () -{ - /* Output call frame information. */ -#ifdef MIPS_DEBUGGING_INFO - if (write_symbols == DWARF2_DEBUG) - output_call_frame_info (0); - if (flag_exceptions && ! exceptions_via_longjmp) - output_call_frame_info (1); -#else - if (write_symbols == DWARF2_DEBUG - || (flag_exceptions && ! exceptions_via_longjmp)) - output_call_frame_info (1); -#endif -} - -#endif /* .debug_frame support */ - -/* And now, the support for symbolic debugging information. */ -#ifdef DWARF2_DEBUGGING_INFO - -extern char *getpwd PROTO((void)); - -/* NOTE: In the comments in this file, many references are made to - "Debugging Information Entries". This term is abbreviated as `DIE' - throughout the remainder of this file. */ - -/* An internal representation of the DWARF output is built, and then - walked to generate the DWARF debugging info. The walk of the internal - representation is done after the entire program has been compiled. - The types below are used to describe the internal representation. */ - -/* Each DIE may have a series of attribute/value pairs. Values - can take on several forms. The forms that are used in this - implementation are listed below. */ - -typedef enum -{ - dw_val_class_addr, - dw_val_class_loc, - dw_val_class_const, - dw_val_class_unsigned_const, - dw_val_class_long_long, - dw_val_class_float, - dw_val_class_flag, - dw_val_class_die_ref, - dw_val_class_fde_ref, - dw_val_class_lbl_id, - dw_val_class_section_offset, - dw_val_class_str -} -dw_val_class; - -/* Various DIE's use offsets relative to the beginning of the - .debug_info section to refer to each other. */ - -typedef long int dw_offset; - -/* Define typedefs here to avoid circular dependencies. */ - -typedef struct die_struct *dw_die_ref; -typedef struct dw_attr_struct *dw_attr_ref; -typedef struct dw_val_struct *dw_val_ref; -typedef struct dw_line_info_struct *dw_line_info_ref; -typedef struct dw_separate_line_info_struct *dw_separate_line_info_ref; -typedef struct dw_loc_descr_struct *dw_loc_descr_ref; -typedef struct pubname_struct *pubname_ref; -typedef dw_die_ref *arange_ref; - -/* Describe a double word constant value. */ - -typedef struct dw_long_long_struct -{ - unsigned long hi; - unsigned long low; -} -dw_long_long_const; - -/* Describe a floating point constant value. */ - -typedef struct dw_fp_struct -{ - long *array; - unsigned length; -} -dw_float_const; - -/* Each entry in the line_info_table maintains the file and - line number associated with the label generated for that - entry. The label gives the PC value associated with - the line number entry. */ - -typedef struct dw_line_info_struct -{ - unsigned long dw_file_num; - unsigned long dw_line_num; -} -dw_line_info_entry; - -/* Line information for functions in separate sections; each one gets its - own sequence. */ -typedef struct dw_separate_line_info_struct -{ - unsigned long dw_file_num; - unsigned long dw_line_num; - unsigned long function; -} -dw_separate_line_info_entry; - -/* The dw_val_node describes an attribute's value, as it is - represented internally. */ - -typedef struct dw_val_struct -{ - dw_val_class val_class; - union - { - rtx val_addr; - dw_loc_descr_ref val_loc; - long int val_int; - long unsigned val_unsigned; - dw_long_long_const val_long_long; - dw_float_const val_float; - dw_die_ref val_die_ref; - unsigned val_fde_index; - char *val_str; - char *val_lbl_id; - char *val_section; - unsigned char val_flag; - } - v; -} -dw_val_node; - -/* Locations in memory are described using a sequence of stack machine - operations. */ - -typedef struct dw_loc_descr_struct -{ - dw_loc_descr_ref dw_loc_next; - enum dwarf_location_atom dw_loc_opc; - dw_val_node dw_loc_oprnd1; - dw_val_node dw_loc_oprnd2; -} -dw_loc_descr_node; - -/* Each DIE attribute has a field specifying the attribute kind, - a link to the next attribute in the chain, and an attribute value. - Attributes are typically linked below the DIE they modify. */ - -typedef struct dw_attr_struct -{ - enum dwarf_attribute dw_attr; - dw_attr_ref dw_attr_next; - dw_val_node dw_attr_val; -} -dw_attr_node; - -/* The Debugging Information Entry (DIE) structure */ - -typedef struct die_struct -{ - enum dwarf_tag die_tag; - dw_attr_ref die_attr; - dw_attr_ref die_attr_last; - dw_die_ref die_parent; - dw_die_ref die_child; - dw_die_ref die_child_last; - dw_die_ref die_sib; - dw_offset die_offset; - unsigned long die_abbrev; -} -die_node; - -/* The pubname structure */ - -typedef struct pubname_struct -{ - dw_die_ref die; - char * name; -} -pubname_entry; - -/* The limbo die list structure. */ -typedef struct limbo_die_struct -{ - dw_die_ref die; - struct limbo_die_struct *next; -} -limbo_die_node; - -/* How to start an assembler comment. */ -#ifndef ASM_COMMENT_START -#define ASM_COMMENT_START ";#" -#endif - -/* Define a macro which returns non-zero for a TYPE_DECL which was - implicitly generated for a tagged type. - - Note that unlike the gcc front end (which generates a NULL named - TYPE_DECL node for each complete tagged type, each array type, and - each function type node created) the g++ front end generates a - _named_ TYPE_DECL node for each tagged type node created. - These TYPE_DECLs have DECL_ARTIFICIAL set, so we know not to - generate a DW_TAG_typedef DIE for them. */ - -#define TYPE_DECL_IS_STUB(decl) \ - (DECL_NAME (decl) == NULL_TREE \ - || (DECL_ARTIFICIAL (decl) \ - && is_tagged_type (TREE_TYPE (decl)) \ - && ((decl == TYPE_STUB_DECL (TREE_TYPE (decl))) \ - /* This is necessary for stub decls that \ - appear in nested inline functions. */ \ - || (DECL_ABSTRACT_ORIGIN (decl) != NULL_TREE \ - && (decl_ultimate_origin (decl) \ - == TYPE_STUB_DECL (TREE_TYPE (decl))))))) - -/* Information concerning the compilation unit's programming - language, and compiler version. */ - -extern int flag_traditional; -extern char *version_string; -extern char *language_string; - -/* Fixed size portion of the DWARF compilation unit header. */ -#define DWARF_COMPILE_UNIT_HEADER_SIZE (2 * DWARF_OFFSET_SIZE + 3) - -/* Fixed size portion of debugging line information prolog. */ -#define DWARF_LINE_PROLOG_HEADER_SIZE 5 - -/* Fixed size portion of public names info. */ -#define DWARF_PUBNAMES_HEADER_SIZE (2 * DWARF_OFFSET_SIZE + 2) - -/* Fixed size portion of the address range info. */ -#define DWARF_ARANGES_HEADER_SIZE \ - (DWARF_ROUND (2 * DWARF_OFFSET_SIZE + 4, PTR_SIZE * 2) - DWARF_OFFSET_SIZE) - -/* Define the architecture-dependent minimum instruction length (in bytes). - In this implementation of DWARF, this field is used for information - purposes only. Since GCC generates assembly language, we have - no a priori knowledge of how many instruction bytes are generated - for each source line, and therefore can use only the DW_LNE_set_address - and DW_LNS_fixed_advance_pc line information commands. */ - -#ifndef DWARF_LINE_MIN_INSTR_LENGTH -#define DWARF_LINE_MIN_INSTR_LENGTH 4 -#endif - -/* Minimum line offset in a special line info. opcode. - This value was chosen to give a reasonable range of values. */ -#define DWARF_LINE_BASE -10 - -/* First special line opcde - leave room for the standard opcodes. */ -#define DWARF_LINE_OPCODE_BASE 10 - -/* Range of line offsets in a special line info. opcode. */ -#define DWARF_LINE_RANGE (254-DWARF_LINE_OPCODE_BASE+1) - -/* Flag that indicates the initial value of the is_stmt_start flag. - In the present implementation, we do not mark any lines as - the beginning of a source statement, because that information - is not made available by the GCC front-end. */ -#define DWARF_LINE_DEFAULT_IS_STMT_START 1 - -/* This location is used by calc_die_sizes() to keep track - the offset of each DIE within the .debug_info section. */ -static unsigned long next_die_offset; - -/* Record the root of the DIE's built for the current compilation unit. */ -static dw_die_ref comp_unit_die; - -/* A list of DIEs with a NULL parent waiting to be relocated. */ -static limbo_die_node *limbo_die_list = 0; - -/* Pointer to an array of filenames referenced by this compilation unit. */ -static char **file_table; - -/* Total number of entries in the table (i.e. array) pointed to by - `file_table'. This is the *total* and includes both used and unused - slots. */ -static unsigned file_table_allocated; - -/* Number of entries in the file_table which are actually in use. */ -static unsigned file_table_in_use; - -/* Size (in elements) of increments by which we may expand the filename - table. */ -#define FILE_TABLE_INCREMENT 64 - -/* Local pointer to the name of the main input file. Initialized in - dwarf2out_init. */ -static char *primary_filename; - -/* For Dwarf output, we must assign lexical-blocks id numbers in the order in - which their beginnings are encountered. We output Dwarf debugging info - that refers to the beginnings and ends of the ranges of code for each - lexical block. The labels themselves are generated in final.c, which - assigns numbers to the blocks in the same way. */ -static unsigned next_block_number = 2; - -/* A pointer to the base of a table of references to DIE's that describe - declarations. The table is indexed by DECL_UID() which is a unique - number identifying each decl. */ -static dw_die_ref *decl_die_table; - -/* Number of elements currently allocated for the decl_die_table. */ -static unsigned decl_die_table_allocated; - -/* Number of elements in decl_die_table currently in use. */ -static unsigned decl_die_table_in_use; - -/* Size (in elements) of increments by which we may expand the - decl_die_table. */ -#define DECL_DIE_TABLE_INCREMENT 256 - -/* Structure used for the decl_scope table. scope is the current declaration - scope, and previous is the entry that is the parent of this scope. This - is usually but not always the immediately preceeding entry. */ - -typedef struct decl_scope_struct -{ - tree scope; - int previous; -} -decl_scope_node; - -/* 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 decl_scope_node *decl_scope_table; - -/* Number of elements currently allocated for the decl_scope_table. */ -static int decl_scope_table_allocated; - -/* Current level of nesting of declaration scopes. */ -static int decl_scope_depth; - -/* Size (in elements) of increments by which we may expand the - decl_scope_table. */ -#define DECL_SCOPE_TABLE_INCREMENT 64 - -/* A pointer to the base of a list of references to DIE's that - are uniquely identified by their tag, presence/absence of - children DIE's, and list of attribute/value pairs. */ -static dw_die_ref *abbrev_die_table; - -/* Number of elements currently allocated for abbrev_die_table. */ -static unsigned abbrev_die_table_allocated; - -/* Number of elements in type_die_table currently in use. */ -static unsigned abbrev_die_table_in_use; - -/* Size (in elements) of increments by which we may expand the - abbrev_die_table. */ -#define ABBREV_DIE_TABLE_INCREMENT 256 - -/* A pointer to the base of a table that contains line information - for each source code line in .text in the compilation unit. */ -static dw_line_info_ref line_info_table; - -/* Number of elements currently allocated for line_info_table. */ -static unsigned line_info_table_allocated; - -/* Number of elements in separate_line_info_table currently in use. */ -static unsigned separate_line_info_table_in_use; - -/* A pointer to the base of a table that contains line information - for each source code line outside of .text in the compilation unit. */ -static dw_separate_line_info_ref separate_line_info_table; - -/* Number of elements currently allocated for separate_line_info_table. */ -static unsigned separate_line_info_table_allocated; - -/* Number of elements in line_info_table currently in use. */ -static unsigned line_info_table_in_use; - -/* Size (in elements) of increments by which we may expand the - line_info_table. */ -#define LINE_INFO_TABLE_INCREMENT 1024 - -/* A pointer to the base of a table that contains a list of publicly - accessible names. */ -static pubname_ref pubname_table; - -/* Number of elements currently allocated for pubname_table. */ -static unsigned pubname_table_allocated; - -/* Number of elements in pubname_table currently in use. */ -static unsigned pubname_table_in_use; - -/* Size (in elements) of increments by which we may expand the - pubname_table. */ -#define PUBNAME_TABLE_INCREMENT 64 - -/* A pointer to the base of a table that contains a list of publicly - accessible names. */ -static arange_ref arange_table; - -/* Number of elements currently allocated for arange_table. */ -static unsigned arange_table_allocated; - -/* Number of elements in arange_table currently in use. */ -static unsigned arange_table_in_use; - -/* Size (in elements) of increments by which we may expand the - arange_table. */ -#define ARANGE_TABLE_INCREMENT 64 - -/* A pointer to the base of a list of pending types which we haven't - generated DIEs for yet, but which we will have to come back to - later on. */ - -static tree *pending_types_list; - -/* Number of elements currently allocated for the pending_types_list. */ -static unsigned pending_types_allocated; - -/* Number of elements of pending_types_list currently in use. */ -static unsigned pending_types; - -/* Size (in elements) of increments by which we may expand the pending - types list. Actually, a single hunk of space of this size should - be enough for most typical programs. */ -#define PENDING_TYPES_INCREMENT 64 - -/* Record whether the function being analyzed contains inlined functions. */ -static int current_function_has_inlines; -#if 0 && defined (MIPS_DEBUGGING_INFO) -static int comp_unit_has_inlines; -#endif - -/* A pointer to the ..._DECL node which we have most recently been working - on. We keep this around just in case something about it looks screwy and - we want to tell the user what the source coordinates for the actual - declaration are. */ -static tree dwarf_last_decl; - -/* Forward declarations for functions defined in this file. */ - -static void addr_const_to_string PROTO((dyn_string_t, rtx)); -static int is_pseudo_reg PROTO((rtx)); -static tree type_main_variant PROTO((tree)); -static int is_tagged_type PROTO((tree)); -static char *dwarf_tag_name PROTO((unsigned)); -static char *dwarf_attr_name PROTO((unsigned)); -static char *dwarf_form_name PROTO((unsigned)); -static char *dwarf_stack_op_name PROTO((unsigned)); -#if 0 -static char *dwarf_type_encoding_name PROTO((unsigned)); -#endif -static tree decl_ultimate_origin PROTO((tree)); -static tree block_ultimate_origin PROTO((tree)); -static tree decl_class_context PROTO((tree)); -static void add_dwarf_attr PROTO((dw_die_ref, dw_attr_ref)); -static void add_AT_flag PROTO((dw_die_ref, - enum dwarf_attribute, - unsigned)); -static void add_AT_int PROTO((dw_die_ref, - enum dwarf_attribute, long)); -static void add_AT_unsigned PROTO((dw_die_ref, - enum dwarf_attribute, - unsigned long)); -static void add_AT_long_long PROTO((dw_die_ref, - enum dwarf_attribute, - unsigned long, unsigned long)); -static void add_AT_float PROTO((dw_die_ref, - enum dwarf_attribute, - unsigned, long *)); -static void add_AT_string PROTO((dw_die_ref, - enum dwarf_attribute, char *)); -static void add_AT_die_ref PROTO((dw_die_ref, - enum dwarf_attribute, - dw_die_ref)); -static void add_AT_fde_ref PROTO((dw_die_ref, - enum dwarf_attribute, - unsigned)); -static void add_AT_loc PROTO((dw_die_ref, - enum dwarf_attribute, - dw_loc_descr_ref)); -static void add_AT_addr PROTO((dw_die_ref, - enum dwarf_attribute, rtx)); -static void add_AT_lbl_id PROTO((dw_die_ref, - enum dwarf_attribute, char *)); -static void add_AT_section_offset PROTO((dw_die_ref, - enum dwarf_attribute, char *)); -static int is_extern_subr_die PROTO((dw_die_ref)); -static dw_attr_ref get_AT PROTO((dw_die_ref, - enum dwarf_attribute)); -static char *get_AT_low_pc PROTO((dw_die_ref)); -static char *get_AT_hi_pc PROTO((dw_die_ref)); -static char *get_AT_string PROTO((dw_die_ref, - enum dwarf_attribute)); -static int get_AT_flag PROTO((dw_die_ref, - enum dwarf_attribute)); -static unsigned get_AT_unsigned PROTO((dw_die_ref, - enum dwarf_attribute)); -static int is_c_family PROTO((void)); -static int is_fortran PROTO((void)); -static void remove_AT PROTO((dw_die_ref, - enum dwarf_attribute)); -static void remove_children PROTO((dw_die_ref)); -static void add_child_die PROTO((dw_die_ref, dw_die_ref)); -static dw_die_ref new_die PROTO((enum dwarf_tag, dw_die_ref)); -static dw_die_ref lookup_type_die PROTO((tree)); -static void equate_type_number_to_die PROTO((tree, dw_die_ref)); -static dw_die_ref lookup_decl_die PROTO((tree)); -static void equate_decl_number_to_die PROTO((tree, dw_die_ref)); -static dw_loc_descr_ref new_loc_descr PROTO((enum dwarf_location_atom, - unsigned long, unsigned long)); -static void add_loc_descr PROTO((dw_loc_descr_ref *, - dw_loc_descr_ref)); -static void print_spaces PROTO((FILE *)); -static void print_die PROTO((dw_die_ref, FILE *)); -static void print_dwarf_line_table PROTO((FILE *)); -static void add_sibling_attributes PROTO((dw_die_ref)); -static void build_abbrev_table PROTO((dw_die_ref)); -static unsigned long size_of_string PROTO((char *)); -static unsigned long size_of_loc_descr PROTO((dw_loc_descr_ref)); -static unsigned long size_of_locs PROTO((dw_loc_descr_ref)); -static int constant_size PROTO((long unsigned)); -static unsigned long size_of_die PROTO((dw_die_ref)); -static void calc_die_sizes PROTO((dw_die_ref)); -static unsigned long size_of_line_prolog PROTO((void)); -static unsigned long size_of_line_info PROTO((void)); -static unsigned long size_of_pubnames PROTO((void)); -static unsigned long size_of_aranges PROTO((void)); -static enum dwarf_form value_format PROTO((dw_val_ref)); -static void output_value_format PROTO((dw_val_ref)); -static void output_abbrev_section PROTO((void)); -static void output_loc_operands PROTO((dw_loc_descr_ref)); -static unsigned long sibling_offset PROTO((dw_die_ref)); -static void output_die PROTO((dw_die_ref)); -static void output_compilation_unit_header PROTO((void)); -static char *dwarf2_name PROTO((tree, int)); -static void add_pubname PROTO((tree, dw_die_ref)); -static void output_pubnames PROTO((void)); -static void add_arange PROTO((tree, dw_die_ref)); -static void output_aranges PROTO((void)); -static void output_line_info PROTO((void)); -static int is_body_block PROTO((tree)); -static dw_die_ref base_type_die PROTO((tree)); -static tree root_type PROTO((tree)); -static int is_base_type PROTO((tree)); -static dw_die_ref modified_type_die PROTO((tree, int, int, dw_die_ref)); -static int type_is_enum PROTO((tree)); -static dw_loc_descr_ref reg_loc_descriptor PROTO((rtx)); -static dw_loc_descr_ref based_loc_descr PROTO((unsigned, long)); -static int is_based_loc PROTO((rtx)); -static dw_loc_descr_ref mem_loc_descriptor PROTO((rtx)); -static dw_loc_descr_ref concat_loc_descriptor PROTO((rtx, rtx)); -static dw_loc_descr_ref loc_descriptor PROTO((rtx)); -static unsigned ceiling PROTO((unsigned, unsigned)); -static tree field_type PROTO((tree)); -static unsigned simple_type_align_in_bits PROTO((tree)); -static unsigned simple_type_size_in_bits PROTO((tree)); -static unsigned field_byte_offset PROTO((tree)); -static void add_AT_location_description PROTO((dw_die_ref, - enum dwarf_attribute, rtx)); -static void add_data_member_location_attribute PROTO((dw_die_ref, tree)); -static void add_const_value_attribute PROTO((dw_die_ref, rtx)); -static void add_location_or_const_value_attribute PROTO((dw_die_ref, tree)); -static void add_name_attribute PROTO((dw_die_ref, char *)); -static void add_bound_info PROTO((dw_die_ref, - enum dwarf_attribute, tree)); -static void add_subscript_info PROTO((dw_die_ref, tree)); -static void add_byte_size_attribute PROTO((dw_die_ref, tree)); -static void add_bit_offset_attribute PROTO((dw_die_ref, tree)); -static void add_bit_size_attribute PROTO((dw_die_ref, tree)); -static void add_prototyped_attribute PROTO((dw_die_ref, tree)); -static void add_abstract_origin_attribute PROTO((dw_die_ref, tree)); -static void add_pure_or_virtual_attribute PROTO((dw_die_ref, tree)); -static void add_src_coords_attributes PROTO((dw_die_ref, tree)); -static void add_name_and_src_coords_attributes PROTO((dw_die_ref, tree)); -static void push_decl_scope PROTO((tree)); -static dw_die_ref scope_die_for PROTO((tree, dw_die_ref)); -static void pop_decl_scope PROTO((void)); -static void add_type_attribute PROTO((dw_die_ref, tree, int, int, - dw_die_ref)); -static char *type_tag PROTO((tree)); -static tree member_declared_type PROTO((tree)); -#if 0 -static char *decl_start_label PROTO((tree)); -#endif -static void gen_array_type_die PROTO((tree, dw_die_ref)); -static void gen_set_type_die PROTO((tree, dw_die_ref)); -#if 0 -static void gen_entry_point_die PROTO((tree, dw_die_ref)); -#endif -static void pend_type PROTO((tree)); -static void output_pending_types_for_scope PROTO((dw_die_ref)); -static void gen_inlined_enumeration_type_die PROTO((tree, dw_die_ref)); -static void gen_inlined_structure_type_die PROTO((tree, dw_die_ref)); -static void gen_inlined_union_type_die PROTO((tree, dw_die_ref)); -static void gen_enumeration_type_die PROTO((tree, dw_die_ref)); -static dw_die_ref gen_formal_parameter_die PROTO((tree, dw_die_ref)); -static void gen_unspecified_parameters_die PROTO((tree, dw_die_ref)); -static void gen_formal_types_die PROTO((tree, dw_die_ref)); -static void gen_subprogram_die PROTO((tree, dw_die_ref)); -static void gen_variable_die PROTO((tree, dw_die_ref)); -static void gen_label_die PROTO((tree, dw_die_ref)); -static void gen_lexical_block_die PROTO((tree, dw_die_ref, int)); -static void gen_inlined_subroutine_die PROTO((tree, dw_die_ref, int)); -static void gen_field_die PROTO((tree, dw_die_ref)); -static void gen_ptr_to_mbr_type_die PROTO((tree, dw_die_ref)); -static void gen_compile_unit_die PROTO((char *)); -static void gen_string_type_die PROTO((tree, dw_die_ref)); -static void gen_inheritance_die PROTO((tree, dw_die_ref)); -static void gen_member_die PROTO((tree, dw_die_ref)); -static void gen_struct_or_union_type_die PROTO((tree, dw_die_ref)); -static void gen_subroutine_type_die PROTO((tree, dw_die_ref)); -static void gen_typedef_die PROTO((tree, dw_die_ref)); -static void gen_type_die PROTO((tree, dw_die_ref)); -static void gen_tagged_type_instantiation_die PROTO((tree, dw_die_ref)); -static void gen_block_die PROTO((tree, dw_die_ref, int)); -static void decls_for_scope PROTO((tree, dw_die_ref, int)); -static int is_redundant_typedef PROTO((tree)); -static void gen_decl_die PROTO((tree, dw_die_ref)); -static unsigned lookup_filename PROTO((char *)); -static rtx save_rtx PROTO((rtx)); - -/* Section names used to hold DWARF debugging information. */ -#ifndef DEBUG_INFO_SECTION -#define DEBUG_INFO_SECTION ".debug_info" -#endif -#ifndef ABBREV_SECTION -#define ABBREV_SECTION ".debug_abbrev" -#endif -#ifndef ARANGES_SECTION -#define ARANGES_SECTION ".debug_aranges" -#endif -#ifndef DW_MACINFO_SECTION -#define DW_MACINFO_SECTION ".debug_macinfo" -#endif -#ifndef DEBUG_LINE_SECTION -#define DEBUG_LINE_SECTION ".debug_line" -#endif -#ifndef LOC_SECTION -#define LOC_SECTION ".debug_loc" -#endif -#ifndef PUBNAMES_SECTION -#define PUBNAMES_SECTION ".debug_pubnames" -#endif -#ifndef STR_SECTION -#define STR_SECTION ".debug_str" -#endif - -/* Standard ELF section names for compiled code and data. */ -#ifndef TEXT_SECTION -#define TEXT_SECTION ".text" -#endif -#ifndef DATA_SECTION -#define DATA_SECTION ".data" -#endif -#ifndef BSS_SECTION -#define BSS_SECTION ".bss" -#endif - - -/* Definitions of defaults for formats and names of various special - (artificial) labels which may be generated within this file (when the -g - options is used and DWARF_DEBUGGING_INFO is in effect. - If necessary, these may be overridden from within the tm.h file, but - typically, overriding these defaults is unnecessary. */ - -static char text_end_label[MAX_ARTIFICIAL_LABEL_BYTES]; - -#ifndef TEXT_END_LABEL -#define TEXT_END_LABEL "Letext" -#endif -#ifndef DATA_END_LABEL -#define DATA_END_LABEL "Ledata" -#endif -#ifndef BSS_END_LABEL -#define BSS_END_LABEL "Lebss" -#endif -#ifndef INSN_LABEL_FMT -#define INSN_LABEL_FMT "LI%u_" -#endif -#ifndef BLOCK_BEGIN_LABEL -#define BLOCK_BEGIN_LABEL "LBB" -#endif -#ifndef BLOCK_END_LABEL -#define BLOCK_END_LABEL "LBE" -#endif -#ifndef BODY_BEGIN_LABEL -#define BODY_BEGIN_LABEL "Lbb" -#endif -#ifndef BODY_END_LABEL -#define BODY_END_LABEL "Lbe" -#endif -#ifndef LINE_CODE_LABEL -#define LINE_CODE_LABEL "LM" -#endif -#ifndef SEPARATE_LINE_CODE_LABEL -#define SEPARATE_LINE_CODE_LABEL "LSM" -#endif - -/* Convert a reference to the assembler name of a C-level name. This - macro has the same effect as ASM_OUTPUT_LABELREF, but copies to - a string rather than writing to a file. */ -#ifndef ASM_NAME_TO_STRING -#define ASM_NAME_TO_STRING(STR, NAME) \ - do { \ - if ((NAME)[0] == '*') \ - dyn_string_append (STR, NAME + 1); \ - else \ - { \ - dyn_string_append (STR, user_label_prefix); \ - dyn_string_append (STR, NAME); \ - } \ - } \ - while (0) -#endif - -/* Convert an integer constant expression into assembler syntax. Addition - and subtraction are the only arithmetic that may appear in these - expressions. This is an adaptation of output_addr_const in final.c. - Here, the target of the conversion is a string buffer. We can't use - output_addr_const directly, because it writes to a file. */ - -static void -addr_const_to_string (str, x) - dyn_string_t str; - rtx x; -{ - char buf1[256]; - -restart: - switch (GET_CODE (x)) - { - case PC: - if (flag_pic) - dyn_string_append (str, ","); - else - abort (); - break; - - case SYMBOL_REF: - ASM_NAME_TO_STRING (str, XSTR (x, 0)); - break; - - case LABEL_REF: - ASM_GENERATE_INTERNAL_LABEL (buf1, "L", CODE_LABEL_NUMBER (XEXP (x, 0))); - ASM_NAME_TO_STRING (str, buf1); - break; - - case CODE_LABEL: - ASM_GENERATE_INTERNAL_LABEL (buf1, "L", CODE_LABEL_NUMBER (x)); - ASM_NAME_TO_STRING (str, buf1); - break; - - case CONST_INT: - sprintf (buf1, HOST_WIDE_INT_PRINT_DEC, INTVAL (x)); - dyn_string_append (str, buf1); - break; - - case CONST: - /* This used to output parentheses around the expression, but that does - not work on the 386 (either ATT or BSD assembler). */ - addr_const_to_string (str, XEXP (x, 0)); - break; - - case CONST_DOUBLE: - if (GET_MODE (x) == VOIDmode) - { - /* We can use %d if the number is one word and positive. */ - if (CONST_DOUBLE_HIGH (x)) - sprintf (buf1, HOST_WIDE_INT_PRINT_DOUBLE_HEX, - CONST_DOUBLE_HIGH (x), CONST_DOUBLE_LOW (x)); - else if (CONST_DOUBLE_LOW (x) < 0) - sprintf (buf1, HOST_WIDE_INT_PRINT_HEX, CONST_DOUBLE_LOW (x)); - else - sprintf (buf1, HOST_WIDE_INT_PRINT_DEC, - CONST_DOUBLE_LOW (x)); - dyn_string_append (str, buf1); - } - else - /* We can't handle floating point constants; PRINT_OPERAND must - handle them. */ - output_operand_lossage ("floating constant misused"); - break; - - case PLUS: - /* Some assemblers need integer constants to appear last (eg masm). */ - if (GET_CODE (XEXP (x, 0)) == CONST_INT) - { - addr_const_to_string (str, XEXP (x, 1)); - if (INTVAL (XEXP (x, 0)) >= 0) - dyn_string_append (str, "+"); - - addr_const_to_string (str, XEXP (x, 0)); - } - else - { - addr_const_to_string (str, XEXP (x, 0)); - if (INTVAL (XEXP (x, 1)) >= 0) - dyn_string_append (str, "+"); - - addr_const_to_string (str, XEXP (x, 1)); - } - break; - - case MINUS: - /* Avoid outputting things like x-x or x+5-x, since some assemblers - can't handle that. */ - x = simplify_subtraction (x); - if (GET_CODE (x) != MINUS) - goto restart; - - addr_const_to_string (str, XEXP (x, 0)); - dyn_string_append (str, "-"); - if (GET_CODE (XEXP (x, 1)) == CONST_INT - && INTVAL (XEXP (x, 1)) < 0) - { - dyn_string_append (str, ASM_OPEN_PAREN); - addr_const_to_string (str, XEXP (x, 1)); - dyn_string_append (str, ASM_CLOSE_PAREN); - } - else - addr_const_to_string (str, XEXP (x, 1)); - break; - - case ZERO_EXTEND: - case SIGN_EXTEND: - addr_const_to_string (str, XEXP (x, 0)); - break; - - default: - output_operand_lossage ("invalid expression as operand"); - } -} - -/* Return an rtx like ORIG which lives forever. That means making a - copy on the permanent_obstack. */ - -static rtx -save_rtx (orig) - register rtx orig; -{ - push_obstacks_nochange (); - end_temporary_allocation (); - orig = really_copy_rtx (orig); - pop_obstacks (); - - return orig; -} - -/* Test if rtl node points to a pseudo register. */ - -static inline int -is_pseudo_reg (rtl) - register rtx rtl; -{ - return (((GET_CODE (rtl) == REG) && (REGNO (rtl) >= FIRST_PSEUDO_REGISTER)) - || ((GET_CODE (rtl) == SUBREG) - && (REGNO (XEXP (rtl, 0)) >= FIRST_PSEUDO_REGISTER))); -} - -/* Return a reference to a type, with its const and volatile qualifiers - removed. */ - -static inline tree -type_main_variant (type) - register tree type; -{ - type = TYPE_MAIN_VARIANT (type); - - /* There really should be only one main variant among any group of variants - of a given type (and all of the MAIN_VARIANT values for all members of - the group should point to that one type) but sometimes the C front-end - messes this up for array types, so we work around that bug here. */ - - if (TREE_CODE (type) == ARRAY_TYPE) - while (type != TYPE_MAIN_VARIANT (type)) - type = TYPE_MAIN_VARIANT (type); - - return type; -} - -/* Return non-zero if the given type node represents a tagged type. */ - -static inline int -is_tagged_type (type) - register tree type; -{ - register enum tree_code code = TREE_CODE (type); - - return (code == RECORD_TYPE || code == UNION_TYPE - || code == QUAL_UNION_TYPE || code == ENUMERAL_TYPE); -} - -/* Convert a DIE tag into its string name. */ - -static char * -dwarf_tag_name (tag) - register unsigned tag; -{ - switch (tag) - { - case DW_TAG_padding: - return "DW_TAG_padding"; - case DW_TAG_array_type: - return "DW_TAG_array_type"; - case DW_TAG_class_type: - return "DW_TAG_class_type"; - case DW_TAG_entry_point: - return "DW_TAG_entry_point"; - case DW_TAG_enumeration_type: - return "DW_TAG_enumeration_type"; - case DW_TAG_formal_parameter: - return "DW_TAG_formal_parameter"; - case DW_TAG_imported_declaration: - return "DW_TAG_imported_declaration"; - case DW_TAG_label: - return "DW_TAG_label"; - case DW_TAG_lexical_block: - return "DW_TAG_lexical_block"; - case DW_TAG_member: - return "DW_TAG_member"; - case DW_TAG_pointer_type: - return "DW_TAG_pointer_type"; - case DW_TAG_reference_type: - return "DW_TAG_reference_type"; - case DW_TAG_compile_unit: - return "DW_TAG_compile_unit"; - case DW_TAG_string_type: - return "DW_TAG_string_type"; - case DW_TAG_structure_type: - return "DW_TAG_structure_type"; - case DW_TAG_subroutine_type: - return "DW_TAG_subroutine_type"; - case DW_TAG_typedef: - return "DW_TAG_typedef"; - case DW_TAG_union_type: - return "DW_TAG_union_type"; - case DW_TAG_unspecified_parameters: - return "DW_TAG_unspecified_parameters"; - case DW_TAG_variant: - return "DW_TAG_variant"; - case DW_TAG_common_block: - return "DW_TAG_common_block"; - case DW_TAG_common_inclusion: - return "DW_TAG_common_inclusion"; - case DW_TAG_inheritance: - return "DW_TAG_inheritance"; - case DW_TAG_inlined_subroutine: - return "DW_TAG_inlined_subroutine"; - case DW_TAG_module: - return "DW_TAG_module"; - case DW_TAG_ptr_to_member_type: - return "DW_TAG_ptr_to_member_type"; - case DW_TAG_set_type: - return "DW_TAG_set_type"; - case DW_TAG_subrange_type: - return "DW_TAG_subrange_type"; - case DW_TAG_with_stmt: - return "DW_TAG_with_stmt"; - case DW_TAG_access_declaration: - return "DW_TAG_access_declaration"; - case DW_TAG_base_type: - return "DW_TAG_base_type"; - case DW_TAG_catch_block: - return "DW_TAG_catch_block"; - case DW_TAG_const_type: - return "DW_TAG_const_type"; - case DW_TAG_constant: - return "DW_TAG_constant"; - case DW_TAG_enumerator: - return "DW_TAG_enumerator"; - case DW_TAG_file_type: - return "DW_TAG_file_type"; - case DW_TAG_friend: - return "DW_TAG_friend"; - case DW_TAG_namelist: - return "DW_TAG_namelist"; - case DW_TAG_namelist_item: - return "DW_TAG_namelist_item"; - case DW_TAG_packed_type: - return "DW_TAG_packed_type"; - case DW_TAG_subprogram: - return "DW_TAG_subprogram"; - case DW_TAG_template_type_param: - return "DW_TAG_template_type_param"; - case DW_TAG_template_value_param: - return "DW_TAG_template_value_param"; - case DW_TAG_thrown_type: - return "DW_TAG_thrown_type"; - case DW_TAG_try_block: - return "DW_TAG_try_block"; - case DW_TAG_variant_part: - return "DW_TAG_variant_part"; - case DW_TAG_variable: - return "DW_TAG_variable"; - case DW_TAG_volatile_type: - return "DW_TAG_volatile_type"; - case DW_TAG_MIPS_loop: - return "DW_TAG_MIPS_loop"; - case DW_TAG_format_label: - return "DW_TAG_format_label"; - case DW_TAG_function_template: - return "DW_TAG_function_template"; - case DW_TAG_class_template: - return "DW_TAG_class_template"; - default: - return "DW_TAG_<unknown>"; - } -} - -/* Convert a DWARF attribute code into its string name. */ - -static char * -dwarf_attr_name (attr) - register unsigned attr; -{ - switch (attr) - { - case DW_AT_sibling: - return "DW_AT_sibling"; - case DW_AT_location: - return "DW_AT_location"; - case DW_AT_name: - return "DW_AT_name"; - case DW_AT_ordering: - return "DW_AT_ordering"; - case DW_AT_subscr_data: - return "DW_AT_subscr_data"; - case DW_AT_byte_size: - return "DW_AT_byte_size"; - case DW_AT_bit_offset: - return "DW_AT_bit_offset"; - case DW_AT_bit_size: - return "DW_AT_bit_size"; - case DW_AT_element_list: - return "DW_AT_element_list"; - case DW_AT_stmt_list: - return "DW_AT_stmt_list"; - case DW_AT_low_pc: - return "DW_AT_low_pc"; - case DW_AT_high_pc: - return "DW_AT_high_pc"; - case DW_AT_language: - return "DW_AT_language"; - case DW_AT_member: - return "DW_AT_member"; - case DW_AT_discr: - return "DW_AT_discr"; - case DW_AT_discr_value: - return "DW_AT_discr_value"; - case DW_AT_visibility: - return "DW_AT_visibility"; - case DW_AT_import: - return "DW_AT_import"; - case DW_AT_string_length: - return "DW_AT_string_length"; - case DW_AT_common_reference: - return "DW_AT_common_reference"; - case DW_AT_comp_dir: - return "DW_AT_comp_dir"; - case DW_AT_const_value: - return "DW_AT_const_value"; - case DW_AT_containing_type: - return "DW_AT_containing_type"; - case DW_AT_default_value: - return "DW_AT_default_value"; - case DW_AT_inline: - return "DW_AT_inline"; - case DW_AT_is_optional: - return "DW_AT_is_optional"; - case DW_AT_lower_bound: - return "DW_AT_lower_bound"; - case DW_AT_producer: - return "DW_AT_producer"; - case DW_AT_prototyped: - return "DW_AT_prototyped"; - case DW_AT_return_addr: - return "DW_AT_return_addr"; - case DW_AT_start_scope: - return "DW_AT_start_scope"; - case DW_AT_stride_size: - return "DW_AT_stride_size"; - case DW_AT_upper_bound: - return "DW_AT_upper_bound"; - case DW_AT_abstract_origin: - return "DW_AT_abstract_origin"; - case DW_AT_accessibility: - return "DW_AT_accessibility"; - case DW_AT_address_class: - return "DW_AT_address_class"; - case DW_AT_artificial: - return "DW_AT_artificial"; - case DW_AT_base_types: - return "DW_AT_base_types"; - case DW_AT_calling_convention: - return "DW_AT_calling_convention"; - case DW_AT_count: - return "DW_AT_count"; - case DW_AT_data_member_location: - return "DW_AT_data_member_location"; - case DW_AT_decl_column: - return "DW_AT_decl_column"; - case DW_AT_decl_file: - return "DW_AT_decl_file"; - case DW_AT_decl_line: - return "DW_AT_decl_line"; - case DW_AT_declaration: - return "DW_AT_declaration"; - case DW_AT_discr_list: - return "DW_AT_discr_list"; - case DW_AT_encoding: - return "DW_AT_encoding"; - case DW_AT_external: - return "DW_AT_external"; - case DW_AT_frame_base: - return "DW_AT_frame_base"; - case DW_AT_friend: - return "DW_AT_friend"; - case DW_AT_identifier_case: - return "DW_AT_identifier_case"; - case DW_AT_macro_info: - return "DW_AT_macro_info"; - case DW_AT_namelist_items: - return "DW_AT_namelist_items"; - case DW_AT_priority: - return "DW_AT_priority"; - case DW_AT_segment: - return "DW_AT_segment"; - case DW_AT_specification: - return "DW_AT_specification"; - case DW_AT_static_link: - return "DW_AT_static_link"; - case DW_AT_type: - return "DW_AT_type"; - case DW_AT_use_location: - return "DW_AT_use_location"; - case DW_AT_variable_parameter: - return "DW_AT_variable_parameter"; - case DW_AT_virtuality: - return "DW_AT_virtuality"; - case DW_AT_vtable_elem_location: - return "DW_AT_vtable_elem_location"; - - case DW_AT_MIPS_fde: - return "DW_AT_MIPS_fde"; - case DW_AT_MIPS_loop_begin: - return "DW_AT_MIPS_loop_begin"; - case DW_AT_MIPS_tail_loop_begin: - return "DW_AT_MIPS_tail_loop_begin"; - case DW_AT_MIPS_epilog_begin: - return "DW_AT_MIPS_epilog_begin"; - case DW_AT_MIPS_loop_unroll_factor: - return "DW_AT_MIPS_loop_unroll_factor"; - case DW_AT_MIPS_software_pipeline_depth: - return "DW_AT_MIPS_software_pipeline_depth"; - case DW_AT_MIPS_linkage_name: - return "DW_AT_MIPS_linkage_name"; - case DW_AT_MIPS_stride: - return "DW_AT_MIPS_stride"; - case DW_AT_MIPS_abstract_name: - return "DW_AT_MIPS_abstract_name"; - case DW_AT_MIPS_clone_origin: - return "DW_AT_MIPS_clone_origin"; - case DW_AT_MIPS_has_inlines: - return "DW_AT_MIPS_has_inlines"; - - case DW_AT_sf_names: - return "DW_AT_sf_names"; - case DW_AT_src_info: - return "DW_AT_src_info"; - case DW_AT_mac_info: - return "DW_AT_mac_info"; - case DW_AT_src_coords: - return "DW_AT_src_coords"; - case DW_AT_body_begin: - return "DW_AT_body_begin"; - case DW_AT_body_end: - return "DW_AT_body_end"; - default: - return "DW_AT_<unknown>"; - } -} - -/* Convert a DWARF value form code into its string name. */ - -static char * -dwarf_form_name (form) - register unsigned form; -{ - switch (form) - { - case DW_FORM_addr: - return "DW_FORM_addr"; - case DW_FORM_block2: - return "DW_FORM_block2"; - case DW_FORM_block4: - return "DW_FORM_block4"; - case DW_FORM_data2: - return "DW_FORM_data2"; - case DW_FORM_data4: - return "DW_FORM_data4"; - case DW_FORM_data8: - return "DW_FORM_data8"; - case DW_FORM_string: - return "DW_FORM_string"; - case DW_FORM_block: - return "DW_FORM_block"; - case DW_FORM_block1: - return "DW_FORM_block1"; - case DW_FORM_data1: - return "DW_FORM_data1"; - case DW_FORM_flag: - return "DW_FORM_flag"; - case DW_FORM_sdata: - return "DW_FORM_sdata"; - case DW_FORM_strp: - return "DW_FORM_strp"; - case DW_FORM_udata: - return "DW_FORM_udata"; - case DW_FORM_ref_addr: - return "DW_FORM_ref_addr"; - case DW_FORM_ref1: - return "DW_FORM_ref1"; - case DW_FORM_ref2: - return "DW_FORM_ref2"; - case DW_FORM_ref4: - return "DW_FORM_ref4"; - case DW_FORM_ref8: - return "DW_FORM_ref8"; - case DW_FORM_ref_udata: - return "DW_FORM_ref_udata"; - case DW_FORM_indirect: - return "DW_FORM_indirect"; - default: - return "DW_FORM_<unknown>"; - } -} - -/* Convert a DWARF stack opcode into its string name. */ - -static char * -dwarf_stack_op_name (op) - register unsigned op; -{ - switch (op) - { - case DW_OP_addr: - return "DW_OP_addr"; - case DW_OP_deref: - return "DW_OP_deref"; - case DW_OP_const1u: - return "DW_OP_const1u"; - case DW_OP_const1s: - return "DW_OP_const1s"; - case DW_OP_const2u: - return "DW_OP_const2u"; - case DW_OP_const2s: - return "DW_OP_const2s"; - case DW_OP_const4u: - return "DW_OP_const4u"; - case DW_OP_const4s: - return "DW_OP_const4s"; - case DW_OP_const8u: - return "DW_OP_const8u"; - case DW_OP_const8s: - return "DW_OP_const8s"; - case DW_OP_constu: - return "DW_OP_constu"; - case DW_OP_consts: - return "DW_OP_consts"; - case DW_OP_dup: - return "DW_OP_dup"; - case DW_OP_drop: - return "DW_OP_drop"; - case DW_OP_over: - return "DW_OP_over"; - case DW_OP_pick: - return "DW_OP_pick"; - case DW_OP_swap: - return "DW_OP_swap"; - case DW_OP_rot: - return "DW_OP_rot"; - case DW_OP_xderef: - return "DW_OP_xderef"; - case DW_OP_abs: - return "DW_OP_abs"; - case DW_OP_and: - return "DW_OP_and"; - case DW_OP_div: - return "DW_OP_div"; - case DW_OP_minus: - return "DW_OP_minus"; - case DW_OP_mod: - return "DW_OP_mod"; - case DW_OP_mul: - return "DW_OP_mul"; - case DW_OP_neg: - return "DW_OP_neg"; - case DW_OP_not: - return "DW_OP_not"; - case DW_OP_or: - return "DW_OP_or"; - case DW_OP_plus: - return "DW_OP_plus"; - case DW_OP_plus_uconst: - return "DW_OP_plus_uconst"; - case DW_OP_shl: - return "DW_OP_shl"; - case DW_OP_shr: - return "DW_OP_shr"; - case DW_OP_shra: - return "DW_OP_shra"; - case DW_OP_xor: - return "DW_OP_xor"; - case DW_OP_bra: - return "DW_OP_bra"; - case DW_OP_eq: - return "DW_OP_eq"; - case DW_OP_ge: - return "DW_OP_ge"; - case DW_OP_gt: - return "DW_OP_gt"; - case DW_OP_le: - return "DW_OP_le"; - case DW_OP_lt: - return "DW_OP_lt"; - case DW_OP_ne: - return "DW_OP_ne"; - case DW_OP_skip: - return "DW_OP_skip"; - case DW_OP_lit0: - return "DW_OP_lit0"; - case DW_OP_lit1: - return "DW_OP_lit1"; - case DW_OP_lit2: - return "DW_OP_lit2"; - case DW_OP_lit3: - return "DW_OP_lit3"; - case DW_OP_lit4: - return "DW_OP_lit4"; - case DW_OP_lit5: - return "DW_OP_lit5"; - case DW_OP_lit6: - return "DW_OP_lit6"; - case DW_OP_lit7: - return "DW_OP_lit7"; - case DW_OP_lit8: - return "DW_OP_lit8"; - case DW_OP_lit9: - return "DW_OP_lit9"; - case DW_OP_lit10: - return "DW_OP_lit10"; - case DW_OP_lit11: - return "DW_OP_lit11"; - case DW_OP_lit12: - return "DW_OP_lit12"; - case DW_OP_lit13: - return "DW_OP_lit13"; - case DW_OP_lit14: - return "DW_OP_lit14"; - case DW_OP_lit15: - return "DW_OP_lit15"; - case DW_OP_lit16: - return "DW_OP_lit16"; - case DW_OP_lit17: - return "DW_OP_lit17"; - case DW_OP_lit18: - return "DW_OP_lit18"; - case DW_OP_lit19: - return "DW_OP_lit19"; - case DW_OP_lit20: - return "DW_OP_lit20"; - case DW_OP_lit21: - return "DW_OP_lit21"; - case DW_OP_lit22: - return "DW_OP_lit22"; - case DW_OP_lit23: - return "DW_OP_lit23"; - case DW_OP_lit24: - return "DW_OP_lit24"; - case DW_OP_lit25: - return "DW_OP_lit25"; - case DW_OP_lit26: - return "DW_OP_lit26"; - case DW_OP_lit27: - return "DW_OP_lit27"; - case DW_OP_lit28: - return "DW_OP_lit28"; - case DW_OP_lit29: - return "DW_OP_lit29"; - case DW_OP_lit30: - return "DW_OP_lit30"; - case DW_OP_lit31: - return "DW_OP_lit31"; - case DW_OP_reg0: - return "DW_OP_reg0"; - case DW_OP_reg1: - return "DW_OP_reg1"; - case DW_OP_reg2: - return "DW_OP_reg2"; - case DW_OP_reg3: - return "DW_OP_reg3"; - case DW_OP_reg4: - return "DW_OP_reg4"; - case DW_OP_reg5: - return "DW_OP_reg5"; - case DW_OP_reg6: - return "DW_OP_reg6"; - case DW_OP_reg7: - return "DW_OP_reg7"; - case DW_OP_reg8: - return "DW_OP_reg8"; - case DW_OP_reg9: - return "DW_OP_reg9"; - case DW_OP_reg10: - return "DW_OP_reg10"; - case DW_OP_reg11: - return "DW_OP_reg11"; - case DW_OP_reg12: - return "DW_OP_reg12"; - case DW_OP_reg13: - return "DW_OP_reg13"; - case DW_OP_reg14: - return "DW_OP_reg14"; - case DW_OP_reg15: - return "DW_OP_reg15"; - case DW_OP_reg16: - return "DW_OP_reg16"; - case DW_OP_reg17: - return "DW_OP_reg17"; - case DW_OP_reg18: - return "DW_OP_reg18"; - case DW_OP_reg19: - return "DW_OP_reg19"; - case DW_OP_reg20: - return "DW_OP_reg20"; - case DW_OP_reg21: - return "DW_OP_reg21"; - case DW_OP_reg22: - return "DW_OP_reg22"; - case DW_OP_reg23: - return "DW_OP_reg23"; - case DW_OP_reg24: - return "DW_OP_reg24"; - case DW_OP_reg25: - return "DW_OP_reg25"; - case DW_OP_reg26: - return "DW_OP_reg26"; - case DW_OP_reg27: - return "DW_OP_reg27"; - case DW_OP_reg28: - return "DW_OP_reg28"; - case DW_OP_reg29: - return "DW_OP_reg29"; - case DW_OP_reg30: - return "DW_OP_reg30"; - case DW_OP_reg31: - return "DW_OP_reg31"; - case DW_OP_breg0: - return "DW_OP_breg0"; - case DW_OP_breg1: - return "DW_OP_breg1"; - case DW_OP_breg2: - return "DW_OP_breg2"; - case DW_OP_breg3: - return "DW_OP_breg3"; - case DW_OP_breg4: - return "DW_OP_breg4"; - case DW_OP_breg5: - return "DW_OP_breg5"; - case DW_OP_breg6: - return "DW_OP_breg6"; - case DW_OP_breg7: - return "DW_OP_breg7"; - case DW_OP_breg8: - return "DW_OP_breg8"; - case DW_OP_breg9: - return "DW_OP_breg9"; - case DW_OP_breg10: - return "DW_OP_breg10"; - case DW_OP_breg11: - return "DW_OP_breg11"; - case DW_OP_breg12: - return "DW_OP_breg12"; - case DW_OP_breg13: - return "DW_OP_breg13"; - case DW_OP_breg14: - return "DW_OP_breg14"; - case DW_OP_breg15: - return "DW_OP_breg15"; - case DW_OP_breg16: - return "DW_OP_breg16"; - case DW_OP_breg17: - return "DW_OP_breg17"; - case DW_OP_breg18: - return "DW_OP_breg18"; - case DW_OP_breg19: - return "DW_OP_breg19"; - case DW_OP_breg20: - return "DW_OP_breg20"; - case DW_OP_breg21: - return "DW_OP_breg21"; - case DW_OP_breg22: - return "DW_OP_breg22"; - case DW_OP_breg23: - return "DW_OP_breg23"; - case DW_OP_breg24: - return "DW_OP_breg24"; - case DW_OP_breg25: - return "DW_OP_breg25"; - case DW_OP_breg26: - return "DW_OP_breg26"; - case DW_OP_breg27: - return "DW_OP_breg27"; - case DW_OP_breg28: - return "DW_OP_breg28"; - case DW_OP_breg29: - return "DW_OP_breg29"; - case DW_OP_breg30: - return "DW_OP_breg30"; - case DW_OP_breg31: - return "DW_OP_breg31"; - case DW_OP_regx: - return "DW_OP_regx"; - case DW_OP_fbreg: - return "DW_OP_fbreg"; - case DW_OP_bregx: - return "DW_OP_bregx"; - case DW_OP_piece: - return "DW_OP_piece"; - case DW_OP_deref_size: - return "DW_OP_deref_size"; - case DW_OP_xderef_size: - return "DW_OP_xderef_size"; - case DW_OP_nop: - return "DW_OP_nop"; - default: - return "OP_<unknown>"; - } -} - -/* Convert a DWARF type code into its string name. */ - -#if 0 -static char * -dwarf_type_encoding_name (enc) - register unsigned enc; -{ - switch (enc) - { - case DW_ATE_address: - return "DW_ATE_address"; - case DW_ATE_boolean: - return "DW_ATE_boolean"; - case DW_ATE_complex_float: - return "DW_ATE_complex_float"; - case DW_ATE_float: - return "DW_ATE_float"; - case DW_ATE_signed: - return "DW_ATE_signed"; - case DW_ATE_signed_char: - return "DW_ATE_signed_char"; - case DW_ATE_unsigned: - return "DW_ATE_unsigned"; - case DW_ATE_unsigned_char: - return "DW_ATE_unsigned_char"; - default: - return "DW_ATE_<unknown>"; - } -} -#endif - -/* Determine the "ultimate origin" of a decl. The decl may be an inlined - instance of an inlined instance of a decl which is local to an inline - function, so we have to trace all of the way back through the origin chain - to find out what sort of node actually served as the original seed for the - given block. */ - -static tree -decl_ultimate_origin (decl) - register tree decl; -{ -#ifdef ENABLE_CHECKING - if (DECL_FROM_INLINE (DECL_ORIGIN (decl))) - /* Since the DECL_ABSTRACT_ORIGIN for a DECL is supposed to be the - most distant ancestor, this should never happen. */ - abort (); -#endif - - return DECL_ABSTRACT_ORIGIN (decl); -} - -/* Determine the "ultimate origin" of a block. The block may be an inlined - instance of an inlined instance of a block which is local to an inline - function, so we have to trace all of the way back through the origin chain - to find out what sort of node actually served as the original seed for the - given block. */ - -static tree -block_ultimate_origin (block) - register tree block; -{ - register tree immediate_origin = BLOCK_ABSTRACT_ORIGIN (block); - - if (immediate_origin == NULL_TREE) - return NULL_TREE; - else - { - register tree ret_val; - register tree lookahead = immediate_origin; - - do - { - ret_val = lookahead; - lookahead = (TREE_CODE (ret_val) == BLOCK) - ? BLOCK_ABSTRACT_ORIGIN (ret_val) - : NULL; - } - while (lookahead != NULL && lookahead != ret_val); - - return ret_val; - } -} - -/* Get the class to which DECL belongs, if any. In g++, the DECL_CONTEXT - of a virtual function may refer to a base class, so we check the 'this' - parameter. */ - -static tree -decl_class_context (decl) - tree decl; -{ - tree context = NULL_TREE; - - if (TREE_CODE (decl) != FUNCTION_DECL || ! DECL_VINDEX (decl)) - context = DECL_CONTEXT (decl); - else - context = TYPE_MAIN_VARIANT - (TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (decl))))); - - if (context && TREE_CODE_CLASS (TREE_CODE (context)) != 't') - context = NULL_TREE; - - return context; -} - -/* Add an attribute/value pair to a DIE */ - -static inline void -add_dwarf_attr (die, attr) - register dw_die_ref die; - register dw_attr_ref attr; -{ - if (die != NULL && attr != NULL) - { - if (die->die_attr == NULL) - { - die->die_attr = attr; - die->die_attr_last = attr; - } - else - { - die->die_attr_last->dw_attr_next = attr; - die->die_attr_last = attr; - } - } -} - -/* Add a flag value attribute to a DIE. */ - -static inline void -add_AT_flag (die, attr_kind, flag) - register dw_die_ref die; - register enum dwarf_attribute attr_kind; - register unsigned flag; -{ - register dw_attr_ref attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); - - attr->dw_attr_next = NULL; - attr->dw_attr = attr_kind; - attr->dw_attr_val.val_class = dw_val_class_flag; - attr->dw_attr_val.v.val_flag = flag; - add_dwarf_attr (die, attr); -} - -/* Add a signed integer attribute value to a DIE. */ - -static inline void -add_AT_int (die, attr_kind, int_val) - register dw_die_ref die; - register enum dwarf_attribute attr_kind; - register long int int_val; -{ - register dw_attr_ref attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); - - attr->dw_attr_next = NULL; - attr->dw_attr = attr_kind; - attr->dw_attr_val.val_class = dw_val_class_const; - attr->dw_attr_val.v.val_int = int_val; - add_dwarf_attr (die, attr); -} - -/* Add an unsigned integer attribute value to a DIE. */ - -static inline void -add_AT_unsigned (die, attr_kind, unsigned_val) - register dw_die_ref die; - register enum dwarf_attribute attr_kind; - register unsigned long unsigned_val; -{ - register dw_attr_ref attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); - - attr->dw_attr_next = NULL; - attr->dw_attr = attr_kind; - attr->dw_attr_val.val_class = dw_val_class_unsigned_const; - attr->dw_attr_val.v.val_unsigned = unsigned_val; - add_dwarf_attr (die, attr); -} - -/* Add an unsigned double integer attribute value to a DIE. */ - -static inline void -add_AT_long_long (die, attr_kind, val_hi, val_low) - register dw_die_ref die; - register enum dwarf_attribute attr_kind; - register unsigned long val_hi; - register unsigned long val_low; -{ - register dw_attr_ref attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); - - attr->dw_attr_next = NULL; - attr->dw_attr = attr_kind; - attr->dw_attr_val.val_class = dw_val_class_long_long; - attr->dw_attr_val.v.val_long_long.hi = val_hi; - attr->dw_attr_val.v.val_long_long.low = val_low; - add_dwarf_attr (die, attr); -} - -/* Add a floating point attribute value to a DIE and return it. */ - -static inline void -add_AT_float (die, attr_kind, length, array) - register dw_die_ref die; - register enum dwarf_attribute attr_kind; - register unsigned length; - register long *array; -{ - register dw_attr_ref attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); - - attr->dw_attr_next = NULL; - attr->dw_attr = attr_kind; - attr->dw_attr_val.val_class = dw_val_class_float; - attr->dw_attr_val.v.val_float.length = length; - attr->dw_attr_val.v.val_float.array = array; - add_dwarf_attr (die, attr); -} - -/* Add a string attribute value to a DIE. */ - -static inline void -add_AT_string (die, attr_kind, str) - register dw_die_ref die; - register enum dwarf_attribute attr_kind; - register char *str; -{ - register dw_attr_ref attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); - - attr->dw_attr_next = NULL; - attr->dw_attr = attr_kind; - attr->dw_attr_val.val_class = dw_val_class_str; - attr->dw_attr_val.v.val_str = xstrdup (str); - add_dwarf_attr (die, attr); -} - -/* Add a DIE reference attribute value to a DIE. */ - -static inline void -add_AT_die_ref (die, attr_kind, targ_die) - register dw_die_ref die; - register enum dwarf_attribute attr_kind; - register dw_die_ref targ_die; -{ - register dw_attr_ref attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); - - attr->dw_attr_next = NULL; - attr->dw_attr = attr_kind; - attr->dw_attr_val.val_class = dw_val_class_die_ref; - attr->dw_attr_val.v.val_die_ref = targ_die; - add_dwarf_attr (die, attr); -} - -/* Add an FDE reference attribute value to a DIE. */ - -static inline void -add_AT_fde_ref (die, attr_kind, targ_fde) - register dw_die_ref die; - register enum dwarf_attribute attr_kind; - register unsigned targ_fde; -{ - register dw_attr_ref attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); - - attr->dw_attr_next = NULL; - attr->dw_attr = attr_kind; - attr->dw_attr_val.val_class = dw_val_class_fde_ref; - attr->dw_attr_val.v.val_fde_index = targ_fde; - add_dwarf_attr (die, attr); -} - -/* Add a location description attribute value to a DIE. */ - -static inline void -add_AT_loc (die, attr_kind, loc) - register dw_die_ref die; - register enum dwarf_attribute attr_kind; - register dw_loc_descr_ref loc; -{ - register dw_attr_ref attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); - - attr->dw_attr_next = NULL; - attr->dw_attr = attr_kind; - attr->dw_attr_val.val_class = dw_val_class_loc; - attr->dw_attr_val.v.val_loc = loc; - add_dwarf_attr (die, attr); -} - -/* Add an address constant attribute value to a DIE. */ - -static inline void -add_AT_addr (die, attr_kind, addr) - register dw_die_ref die; - register enum dwarf_attribute attr_kind; - rtx addr; -{ - register dw_attr_ref attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); - - attr->dw_attr_next = NULL; - attr->dw_attr = attr_kind; - attr->dw_attr_val.val_class = dw_val_class_addr; - attr->dw_attr_val.v.val_addr = addr; - add_dwarf_attr (die, attr); -} - -/* Add a label identifier attribute value to a DIE. */ - -static inline void -add_AT_lbl_id (die, attr_kind, lbl_id) - register dw_die_ref die; - register enum dwarf_attribute attr_kind; - register char *lbl_id; -{ - register dw_attr_ref attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); - - attr->dw_attr_next = NULL; - attr->dw_attr = attr_kind; - attr->dw_attr_val.val_class = dw_val_class_lbl_id; - attr->dw_attr_val.v.val_lbl_id = xstrdup (lbl_id); - add_dwarf_attr (die, attr); -} - -/* Add a section offset attribute value to a DIE. */ - -static inline void -add_AT_section_offset (die, attr_kind, section) - register dw_die_ref die; - register enum dwarf_attribute attr_kind; - register char *section; -{ - register dw_attr_ref attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); - - attr->dw_attr_next = NULL; - attr->dw_attr = attr_kind; - attr->dw_attr_val.val_class = dw_val_class_section_offset; - attr->dw_attr_val.v.val_section = section; - add_dwarf_attr (die, attr); - -} - -/* Test if die refers to an external subroutine. */ - -static inline int -is_extern_subr_die (die) - register dw_die_ref die; -{ - register dw_attr_ref a; - register int is_subr = FALSE; - register int is_extern = FALSE; - - if (die != NULL && die->die_tag == DW_TAG_subprogram) - { - is_subr = TRUE; - for (a = die->die_attr; a != NULL; a = a->dw_attr_next) - { - if (a->dw_attr == DW_AT_external - && a->dw_attr_val.val_class == dw_val_class_flag - && a->dw_attr_val.v.val_flag != 0) - { - is_extern = TRUE; - break; - } - } - } - - return is_subr && is_extern; -} - -/* Get the attribute of type attr_kind. */ - -static inline dw_attr_ref -get_AT (die, attr_kind) - register dw_die_ref die; - register enum dwarf_attribute attr_kind; -{ - register dw_attr_ref a; - register dw_die_ref spec = NULL; - - if (die != NULL) - { - for (a = die->die_attr; a != NULL; a = a->dw_attr_next) - { - if (a->dw_attr == attr_kind) - return a; - - if (a->dw_attr == DW_AT_specification - || a->dw_attr == DW_AT_abstract_origin) - spec = a->dw_attr_val.v.val_die_ref; - } - - if (spec) - return get_AT (spec, attr_kind); - } - - return NULL; -} - -/* Return the "low pc" attribute value, typically associated with - a subprogram DIE. Return null if the "low pc" attribute is - either not prsent, or if it cannot be represented as an - assembler label identifier. */ - -static inline char * -get_AT_low_pc (die) - register dw_die_ref die; -{ - register dw_attr_ref a = get_AT (die, DW_AT_low_pc); - - if (a && a->dw_attr_val.val_class == dw_val_class_lbl_id) - return a->dw_attr_val.v.val_lbl_id; - - return NULL; -} - -/* Return the "high pc" attribute value, typically associated with - a subprogram DIE. Return null if the "high pc" attribute is - either not prsent, or if it cannot be represented as an - assembler label identifier. */ - -static inline char * -get_AT_hi_pc (die) - register dw_die_ref die; -{ - register dw_attr_ref a = get_AT (die, DW_AT_high_pc); - - if (a && a->dw_attr_val.val_class == dw_val_class_lbl_id) - return a->dw_attr_val.v.val_lbl_id; - - return NULL; -} - -/* Return the value of the string attribute designated by ATTR_KIND, or - NULL if it is not present. */ - -static inline char * -get_AT_string (die, attr_kind) - register dw_die_ref die; - register enum dwarf_attribute attr_kind; -{ - register dw_attr_ref a = get_AT (die, attr_kind); - - if (a && a->dw_attr_val.val_class == dw_val_class_str) - return a->dw_attr_val.v.val_str; - - return NULL; -} - -/* Return the value of the flag attribute designated by ATTR_KIND, or -1 - if it is not present. */ - -static inline int -get_AT_flag (die, attr_kind) - register dw_die_ref die; - register enum dwarf_attribute attr_kind; -{ - register dw_attr_ref a = get_AT (die, attr_kind); - - if (a && a->dw_attr_val.val_class == dw_val_class_flag) - return a->dw_attr_val.v.val_flag; - - return -1; -} - -/* Return the value of the unsigned attribute designated by ATTR_KIND, or 0 - if it is not present. */ - -static inline unsigned -get_AT_unsigned (die, attr_kind) - register dw_die_ref die; - register enum dwarf_attribute attr_kind; -{ - register dw_attr_ref a = get_AT (die, attr_kind); - - if (a && a->dw_attr_val.val_class == dw_val_class_unsigned_const) - return a->dw_attr_val.v.val_unsigned; - - return 0; -} - -static inline int -is_c_family () -{ - register unsigned lang = get_AT_unsigned (comp_unit_die, DW_AT_language); - - return (lang == DW_LANG_C || lang == DW_LANG_C89 - || lang == DW_LANG_C_plus_plus); -} - -static inline int -is_fortran () -{ - register unsigned lang = get_AT_unsigned (comp_unit_die, DW_AT_language); - - return (lang == DW_LANG_Fortran77 || lang == DW_LANG_Fortran90); -} - -/* Remove the specified attribute if present. */ - -static inline void -remove_AT (die, attr_kind) - register dw_die_ref die; - register enum dwarf_attribute attr_kind; -{ - register dw_attr_ref a; - register dw_attr_ref removed = NULL;; - - if (die != NULL) - { - if (die->die_attr->dw_attr == attr_kind) - { - removed = die->die_attr; - if (die->die_attr_last == die->die_attr) - die->die_attr_last = NULL; - - die->die_attr = die->die_attr->dw_attr_next; - } - - else - for (a = die->die_attr; a->dw_attr_next != NULL; - a = a->dw_attr_next) - if (a->dw_attr_next->dw_attr == attr_kind) - { - removed = a->dw_attr_next; - if (die->die_attr_last == a->dw_attr_next) - die->die_attr_last = a; - - a->dw_attr_next = a->dw_attr_next->dw_attr_next; - break; - } - - if (removed != 0) - free (removed); - } -} - -/* Discard the children of this DIE. */ - -static inline void -remove_children (die) - register dw_die_ref die; -{ - register dw_die_ref child_die = die->die_child; - - die->die_child = NULL; - die->die_child_last = NULL; - - while (child_die != NULL) - { - register dw_die_ref tmp_die = child_die; - register dw_attr_ref a; - - child_die = child_die->die_sib; - - for (a = tmp_die->die_attr; a != NULL; ) - { - register dw_attr_ref tmp_a = a; - - a = a->dw_attr_next; - free (tmp_a); - } - - free (tmp_die); - } -} - -/* Add a child DIE below its parent. */ - -static inline void -add_child_die (die, child_die) - register dw_die_ref die; - register dw_die_ref child_die; -{ - if (die != NULL && child_die != NULL) - { - if (die == child_die) - abort (); - child_die->die_parent = die; - child_die->die_sib = NULL; - - if (die->die_child == NULL) - { - die->die_child = child_die; - die->die_child_last = child_die; - } - else - { - die->die_child_last->die_sib = child_die; - die->die_child_last = child_die; - } - } -} - -/* Return a pointer to a newly created DIE node. */ - -static inline dw_die_ref -new_die (tag_value, parent_die) - register enum dwarf_tag tag_value; - register dw_die_ref parent_die; -{ - register dw_die_ref die = (dw_die_ref) xmalloc (sizeof (die_node)); - - die->die_tag = tag_value; - die->die_abbrev = 0; - die->die_offset = 0; - die->die_child = NULL; - die->die_parent = NULL; - die->die_sib = NULL; - die->die_child_last = NULL; - die->die_attr = NULL; - die->die_attr_last = NULL; - - if (parent_die != NULL) - add_child_die (parent_die, die); - else - { - limbo_die_node *limbo_node; - - limbo_node = (limbo_die_node *) xmalloc (sizeof (limbo_die_node)); - limbo_node->die = die; - limbo_node->next = limbo_die_list; - limbo_die_list = limbo_node; - } - - return die; -} - -/* Return the DIE associated with the given type specifier. */ - -static inline dw_die_ref -lookup_type_die (type) - register tree type; -{ - return (dw_die_ref) TYPE_SYMTAB_POINTER (type); -} - -/* Equate a DIE to a given type specifier. */ - -static void -equate_type_number_to_die (type, type_die) - register tree type; - register dw_die_ref type_die; -{ - TYPE_SYMTAB_POINTER (type) = (char *) type_die; -} - -/* Return the DIE associated with a given declaration. */ - -static inline dw_die_ref -lookup_decl_die (decl) - register tree decl; -{ - register unsigned decl_id = DECL_UID (decl); - - return (decl_id < decl_die_table_in_use - ? decl_die_table[decl_id] : NULL); -} - -/* Equate a DIE to a particular declaration. */ - -static void -equate_decl_number_to_die (decl, decl_die) - register tree decl; - register dw_die_ref decl_die; -{ - register unsigned decl_id = DECL_UID (decl); - register unsigned num_allocated; - - if (decl_id >= decl_die_table_allocated) - { - num_allocated - = ((decl_id + 1 + DECL_DIE_TABLE_INCREMENT - 1) - / DECL_DIE_TABLE_INCREMENT) - * DECL_DIE_TABLE_INCREMENT; - - decl_die_table - = (dw_die_ref *) xrealloc (decl_die_table, - sizeof (dw_die_ref) * num_allocated); - - zero_memory ((char *) &decl_die_table[decl_die_table_allocated], - (num_allocated - decl_die_table_allocated) * sizeof (dw_die_ref)); - decl_die_table_allocated = num_allocated; - } - - if (decl_id >= decl_die_table_in_use) - decl_die_table_in_use = (decl_id + 1); - - decl_die_table[decl_id] = decl_die; -} - -/* Return a pointer to a newly allocated location description. Location - descriptions are simple expression terms that can be strung - together to form more complicated location (address) descriptions. */ - -static inline dw_loc_descr_ref -new_loc_descr (op, oprnd1, oprnd2) - register enum dwarf_location_atom op; - register unsigned long oprnd1; - register unsigned long oprnd2; -{ - register dw_loc_descr_ref descr - = (dw_loc_descr_ref) xmalloc (sizeof (dw_loc_descr_node)); - - descr->dw_loc_next = NULL; - descr->dw_loc_opc = op; - descr->dw_loc_oprnd1.val_class = dw_val_class_unsigned_const; - descr->dw_loc_oprnd1.v.val_unsigned = oprnd1; - descr->dw_loc_oprnd2.val_class = dw_val_class_unsigned_const; - descr->dw_loc_oprnd2.v.val_unsigned = oprnd2; - - return descr; -} - -/* Add a location description term to a location description expression. */ - -static inline void -add_loc_descr (list_head, descr) - register dw_loc_descr_ref *list_head; - register dw_loc_descr_ref descr; -{ - register dw_loc_descr_ref *d; - - /* Find the end of the chain. */ - for (d = list_head; (*d) != NULL; d = &(*d)->dw_loc_next) - ; - - *d = descr; -} - -/* Keep track of the number of spaces used to indent the - output of the debugging routines that print the structure of - the DIE internal representation. */ -static int print_indent; - -/* Indent the line the number of spaces given by print_indent. */ - -static inline void -print_spaces (outfile) - FILE *outfile; -{ - fprintf (outfile, "%*s", print_indent, ""); -} - -/* Print the information associated with a given DIE, and its children. - This routine is a debugging aid only. */ - -static void -print_die (die, outfile) - dw_die_ref die; - FILE *outfile; -{ - register dw_attr_ref a; - register dw_die_ref c; - - print_spaces (outfile); - fprintf (outfile, "DIE %4lu: %s\n", - die->die_offset, dwarf_tag_name (die->die_tag)); - print_spaces (outfile); - fprintf (outfile, " abbrev id: %lu", die->die_abbrev); - fprintf (outfile, " offset: %lu\n", die->die_offset); - - for (a = die->die_attr; a != NULL; a = a->dw_attr_next) - { - print_spaces (outfile); - fprintf (outfile, " %s: ", dwarf_attr_name (a->dw_attr)); - - switch (a->dw_attr_val.val_class) - { - case dw_val_class_addr: - fprintf (outfile, "address"); - break; - case dw_val_class_loc: - fprintf (outfile, "location descriptor"); - break; - case dw_val_class_const: - fprintf (outfile, "%ld", a->dw_attr_val.v.val_int); - break; - case dw_val_class_unsigned_const: - fprintf (outfile, "%lu", a->dw_attr_val.v.val_unsigned); - break; - case dw_val_class_long_long: - fprintf (outfile, "constant (%lu,%lu)", - a->dw_attr_val.v.val_long_long.hi, - a->dw_attr_val.v.val_long_long.low); - break; - case dw_val_class_float: - fprintf (outfile, "floating-point constant"); - break; - case dw_val_class_flag: - fprintf (outfile, "%u", a->dw_attr_val.v.val_flag); - break; - case dw_val_class_die_ref: - if (a->dw_attr_val.v.val_die_ref != NULL) - fprintf (outfile, "die -> %lu", - a->dw_attr_val.v.val_die_ref->die_offset); - else - fprintf (outfile, "die -> <null>"); - break; - case dw_val_class_lbl_id: - fprintf (outfile, "label: %s", a->dw_attr_val.v.val_lbl_id); - break; - case dw_val_class_section_offset: - fprintf (outfile, "section: %s", a->dw_attr_val.v.val_section); - break; - case dw_val_class_str: - if (a->dw_attr_val.v.val_str != NULL) - fprintf (outfile, "\"%s\"", a->dw_attr_val.v.val_str); - else - fprintf (outfile, "<null>"); - break; - default: - break; - } - - fprintf (outfile, "\n"); - } - - if (die->die_child != NULL) - { - print_indent += 4; - for (c = die->die_child; c != NULL; c = c->die_sib) - print_die (c, outfile); - - print_indent -= 4; - } -} - -/* Print the contents of the source code line number correspondence table. - This routine is a debugging aid only. */ - -static void -print_dwarf_line_table (outfile) - FILE *outfile; -{ - register unsigned i; - register dw_line_info_ref line_info; - - fprintf (outfile, "\n\nDWARF source line information\n"); - for (i = 1; i < line_info_table_in_use; ++i) - { - line_info = &line_info_table[i]; - fprintf (outfile, "%5d: ", i); - fprintf (outfile, "%-20s", file_table[line_info->dw_file_num]); - fprintf (outfile, "%6ld", line_info->dw_line_num); - fprintf (outfile, "\n"); - } - - fprintf (outfile, "\n\n"); -} - -/* Print the information collected for a given DIE. */ - -void -debug_dwarf_die (die) - dw_die_ref die; -{ - print_die (die, stderr); -} - -/* Print all DWARF information collected for the compilation unit. - This routine is a debugging aid only. */ - -void -debug_dwarf () -{ - print_indent = 0; - print_die (comp_unit_die, stderr); - print_dwarf_line_table (stderr); -} - -/* Traverse the DIE, and add a sibling attribute if it may have the - effect of speeding up access to siblings. To save some space, - avoid generating sibling attributes for DIE's without children. */ - -static void -add_sibling_attributes(die) - register dw_die_ref die; -{ - register dw_die_ref c; - register dw_attr_ref attr; - if (die != comp_unit_die && die->die_child != NULL) - { - attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); - attr->dw_attr_next = NULL; - attr->dw_attr = DW_AT_sibling; - attr->dw_attr_val.val_class = dw_val_class_die_ref; - attr->dw_attr_val.v.val_die_ref = die->die_sib; - - /* Add the sibling link to the front of the attribute list. */ - attr->dw_attr_next = die->die_attr; - if (die->die_attr == NULL) - die->die_attr_last = attr; - - die->die_attr = attr; - } - - for (c = die->die_child; c != NULL; c = c->die_sib) - add_sibling_attributes (c); -} - -/* The format of each DIE (and its attribute value pairs) - is encoded in an abbreviation table. This routine builds the - abbreviation table and assigns a unique abbreviation id for - each abbreviation entry. The children of each die are visited - recursively. */ - -static void -build_abbrev_table (die) - register dw_die_ref die; -{ - register unsigned long abbrev_id; - register unsigned long n_alloc; - register dw_die_ref c; - register dw_attr_ref d_attr, a_attr; - for (abbrev_id = 1; abbrev_id < abbrev_die_table_in_use; ++abbrev_id) - { - register dw_die_ref abbrev = abbrev_die_table[abbrev_id]; - - if (abbrev->die_tag == die->die_tag) - { - if ((abbrev->die_child != NULL) == (die->die_child != NULL)) - { - a_attr = abbrev->die_attr; - d_attr = die->die_attr; - - while (a_attr != NULL && d_attr != NULL) - { - if ((a_attr->dw_attr != d_attr->dw_attr) - || (value_format (&a_attr->dw_attr_val) - != value_format (&d_attr->dw_attr_val))) - break; - - a_attr = a_attr->dw_attr_next; - d_attr = d_attr->dw_attr_next; - } - - if (a_attr == NULL && d_attr == NULL) - break; - } - } - } - - if (abbrev_id >= abbrev_die_table_in_use) - { - if (abbrev_die_table_in_use >= abbrev_die_table_allocated) - { - n_alloc = abbrev_die_table_allocated + ABBREV_DIE_TABLE_INCREMENT; - abbrev_die_table - = (dw_die_ref *) xrealloc (abbrev_die_table, - sizeof (dw_die_ref) * n_alloc); - - zero_memory ((char *) &abbrev_die_table[abbrev_die_table_allocated], - (n_alloc - abbrev_die_table_allocated) * sizeof (dw_die_ref)); - abbrev_die_table_allocated = n_alloc; - } - - ++abbrev_die_table_in_use; - abbrev_die_table[abbrev_id] = die; - } - - die->die_abbrev = abbrev_id; - for (c = die->die_child; c != NULL; c = c->die_sib) - build_abbrev_table (c); -} - -/* Return the size of a string, including the null byte. - - This used to treat backslashes as escapes, and hence they were not included - in the count. However, that conflicts with what ASM_OUTPUT_ASCII does, - which treats a backslash as a backslash, escaping it if necessary, and hence - we must include them in the count. */ - -static unsigned long -size_of_string (str) - register char *str; -{ - return strlen (str) + 1; -} - -/* Return the size of a location descriptor. */ - -static unsigned long -size_of_loc_descr (loc) - register dw_loc_descr_ref loc; -{ - register unsigned long size = 1; - - switch (loc->dw_loc_opc) - { - case DW_OP_addr: - size += PTR_SIZE; - break; - case DW_OP_const1u: - case DW_OP_const1s: - size += 1; - break; - case DW_OP_const2u: - case DW_OP_const2s: - size += 2; - break; - case DW_OP_const4u: - case DW_OP_const4s: - size += 4; - break; - case DW_OP_const8u: - case DW_OP_const8s: - size += 8; - break; - case DW_OP_constu: - size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned); - break; - case DW_OP_consts: - size += size_of_sleb128 (loc->dw_loc_oprnd1.v.val_int); - break; - case DW_OP_pick: - size += 1; - break; - case DW_OP_plus_uconst: - size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned); - break; - case DW_OP_skip: - case DW_OP_bra: - size += 2; - break; - case DW_OP_breg0: - case DW_OP_breg1: - case DW_OP_breg2: - case DW_OP_breg3: - case DW_OP_breg4: - case DW_OP_breg5: - case DW_OP_breg6: - case DW_OP_breg7: - case DW_OP_breg8: - case DW_OP_breg9: - case DW_OP_breg10: - case DW_OP_breg11: - case DW_OP_breg12: - case DW_OP_breg13: - case DW_OP_breg14: - case DW_OP_breg15: - case DW_OP_breg16: - case DW_OP_breg17: - case DW_OP_breg18: - case DW_OP_breg19: - case DW_OP_breg20: - case DW_OP_breg21: - case DW_OP_breg22: - case DW_OP_breg23: - case DW_OP_breg24: - case DW_OP_breg25: - case DW_OP_breg26: - case DW_OP_breg27: - case DW_OP_breg28: - case DW_OP_breg29: - case DW_OP_breg30: - case DW_OP_breg31: - size += size_of_sleb128 (loc->dw_loc_oprnd1.v.val_int); - break; - case DW_OP_regx: - size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned); - break; - case DW_OP_fbreg: - size += size_of_sleb128 (loc->dw_loc_oprnd1.v.val_int); - break; - case DW_OP_bregx: - size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned); - size += size_of_sleb128 (loc->dw_loc_oprnd2.v.val_int); - break; - case DW_OP_piece: - size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned); - break; - case DW_OP_deref_size: - case DW_OP_xderef_size: - size += 1; - break; - default: - break; - } - - return size; -} - -/* Return the size of a series of location descriptors. */ - -static unsigned long -size_of_locs (loc) - register dw_loc_descr_ref loc; -{ - register unsigned long size = 0; - - for (; loc != NULL; loc = loc->dw_loc_next) - size += size_of_loc_descr (loc); - - return size; -} - -/* Return the power-of-two number of bytes necessary to represent VALUE. */ - -static int -constant_size (value) - long unsigned value; -{ - int log; - - if (value == 0) - log = 0; - else - log = floor_log2 (value); - - log = log / 8; - log = 1 << (floor_log2 (log) + 1); - - return log; -} - -/* Return the size of a DIE, as it is represented in the - .debug_info section. */ - -static unsigned long -size_of_die (die) - register dw_die_ref die; -{ - register unsigned long size = 0; - register dw_attr_ref a; - - size += size_of_uleb128 (die->die_abbrev); - for (a = die->die_attr; a != NULL; a = a->dw_attr_next) - { - switch (a->dw_attr_val.val_class) - { - case dw_val_class_addr: - size += PTR_SIZE; - break; - case dw_val_class_loc: - { - register unsigned long lsize - = size_of_locs (a->dw_attr_val.v.val_loc); - - /* Block length. */ - size += constant_size (lsize); - size += lsize; - } - break; - case dw_val_class_const: - size += 4; - break; - case dw_val_class_unsigned_const: - size += constant_size (a->dw_attr_val.v.val_unsigned); - break; - case dw_val_class_long_long: - size += 1 + 8; /* block */ - break; - case dw_val_class_float: - size += 1 + a->dw_attr_val.v.val_float.length * 4; /* block */ - break; - case dw_val_class_flag: - size += 1; - break; - case dw_val_class_die_ref: - size += DWARF_OFFSET_SIZE; - break; - case dw_val_class_fde_ref: - size += DWARF_OFFSET_SIZE; - break; - case dw_val_class_lbl_id: - size += PTR_SIZE; - break; - case dw_val_class_section_offset: - size += DWARF_OFFSET_SIZE; - break; - case dw_val_class_str: - size += size_of_string (a->dw_attr_val.v.val_str); - break; - default: - abort (); - } - } - - return size; -} - -/* Size the debugging information associated with a given DIE. - Visits the DIE's children recursively. Updates the global - variable next_die_offset, on each time through. Uses the - current value of next_die_offset to update the die_offset - field in each DIE. */ - -static void -calc_die_sizes (die) - dw_die_ref die; -{ - register dw_die_ref c; - die->die_offset = next_die_offset; - next_die_offset += size_of_die (die); - - for (c = die->die_child; c != NULL; c = c->die_sib) - calc_die_sizes (c); - - if (die->die_child != NULL) - /* Count the null byte used to terminate sibling lists. */ - next_die_offset += 1; -} - -/* Return the size of the line information prolog generated for the - compilation unit. */ - -static unsigned long -size_of_line_prolog () -{ - register unsigned long size; - register unsigned long ft_index; - - size = DWARF_LINE_PROLOG_HEADER_SIZE; - - /* Count the size of the table giving number of args for each - standard opcode. */ - size += DWARF_LINE_OPCODE_BASE - 1; - - /* Include directory table is empty (at present). Count only the - null byte used to terminate the table. */ - size += 1; - - for (ft_index = 1; ft_index < file_table_in_use; ++ft_index) - { - /* File name entry. */ - size += size_of_string (file_table[ft_index]); - - /* Include directory index. */ - size += size_of_uleb128 (0); - - /* Modification time. */ - size += size_of_uleb128 (0); - - /* File length in bytes. */ - size += size_of_uleb128 (0); - } - - /* Count the file table terminator. */ - size += 1; - return size; -} - -/* Return the size of the line information generated for this - compilation unit. */ - -static unsigned long -size_of_line_info () -{ - register unsigned long size; - register unsigned long lt_index; - register unsigned long current_line; - register long line_offset; - register long line_delta; - register unsigned long current_file; - register unsigned long function; - unsigned long size_of_set_address; - - /* Size of a DW_LNE_set_address instruction. */ - size_of_set_address = 1 + size_of_uleb128 (1 + PTR_SIZE) + 1 + PTR_SIZE; - - /* Version number. */ - size = 2; - - /* Prolog length specifier. */ - size += DWARF_OFFSET_SIZE; - - /* Prolog. */ - size += size_of_line_prolog (); - - /* Set address register instruction. */ - size += size_of_set_address; - - current_file = 1; - current_line = 1; - for (lt_index = 1; lt_index < line_info_table_in_use; ++lt_index) - { - register dw_line_info_ref line_info; - - /* Advance pc instruction. */ - /* ??? See the DW_LNS_advance_pc comment in output_line_info. */ - if (0) - size += 1 + 2; - else - size += size_of_set_address; - - line_info = &line_info_table[lt_index]; - if (line_info->dw_file_num != current_file) - { - /* Set file number instruction. */ - size += 1; - current_file = line_info->dw_file_num; - size += size_of_uleb128 (current_file); - } - - if (line_info->dw_line_num != current_line) - { - line_offset = line_info->dw_line_num - current_line; - line_delta = line_offset - DWARF_LINE_BASE; - current_line = line_info->dw_line_num; - if (line_delta >= 0 && line_delta < (DWARF_LINE_RANGE - 1)) - /* 1-byte special line number instruction. */ - size += 1; - else - { - /* Advance line instruction. */ - size += 1; - size += size_of_sleb128 (line_offset); - /* Generate line entry instruction. */ - size += 1; - } - } - } - - /* Advance pc instruction. */ - if (0) - size += 1 + 2; - else - size += size_of_set_address; - - /* End of line number info. marker. */ - size += 1 + size_of_uleb128 (1) + 1; - - function = 0; - current_file = 1; - current_line = 1; - for (lt_index = 0; lt_index < separate_line_info_table_in_use; ) - { - register dw_separate_line_info_ref line_info - = &separate_line_info_table[lt_index]; - if (function != line_info->function) - { - function = line_info->function; - /* Set address register instruction. */ - size += size_of_set_address; - } - else - { - /* Advance pc instruction. */ - if (0) - size += 1 + 2; - else - size += size_of_set_address; - } - - if (line_info->dw_file_num != current_file) - { - /* Set file number instruction. */ - size += 1; - current_file = line_info->dw_file_num; - size += size_of_uleb128 (current_file); - } - - if (line_info->dw_line_num != current_line) - { - line_offset = line_info->dw_line_num - current_line; - line_delta = line_offset - DWARF_LINE_BASE; - current_line = line_info->dw_line_num; - if (line_delta >= 0 && line_delta < (DWARF_LINE_RANGE - 1)) - /* 1-byte special line number instruction. */ - size += 1; - else - { - /* Advance line instruction. */ - size += 1; - size += size_of_sleb128 (line_offset); - - /* Generate line entry instruction. */ - size += 1; - } - } - - ++lt_index; - - /* If we're done with a function, end its sequence. */ - if (lt_index == separate_line_info_table_in_use - || separate_line_info_table[lt_index].function != function) - { - current_file = 1; - current_line = 1; - - /* Advance pc instruction. */ - if (0) - size += 1 + 2; - else - size += size_of_set_address; - - /* End of line number info. marker. */ - size += 1 + size_of_uleb128 (1) + 1; - } - } - - return size; -} - -/* Return the size of the .debug_pubnames table generated for the - compilation unit. */ - -static unsigned long -size_of_pubnames () -{ - register unsigned long size; - register unsigned i; - - size = DWARF_PUBNAMES_HEADER_SIZE; - for (i = 0; i < pubname_table_in_use; ++i) - { - register pubname_ref p = &pubname_table[i]; - size += DWARF_OFFSET_SIZE + size_of_string (p->name); - } - - size += DWARF_OFFSET_SIZE; - return size; -} - -/* Return the size of the information in the .debug_aranges section. */ - -static unsigned long -size_of_aranges () -{ - register unsigned long size; - - size = DWARF_ARANGES_HEADER_SIZE; - - /* Count the address/length pair for this compilation unit. */ - size += 2 * PTR_SIZE; - size += 2 * PTR_SIZE * arange_table_in_use; - - /* Count the two zero words used to terminated the address range table. */ - size += 2 * PTR_SIZE; - return size; -} - -/* Select the encoding of an attribute value. */ - -static enum dwarf_form -value_format (v) - dw_val_ref v; -{ - switch (v->val_class) - { - case dw_val_class_addr: - return DW_FORM_addr; - case dw_val_class_loc: - switch (constant_size (size_of_locs (v->v.val_loc))) - { - case 1: - return DW_FORM_block1; - case 2: - return DW_FORM_block2; - default: - abort (); - } - case dw_val_class_const: - return DW_FORM_data4; - case dw_val_class_unsigned_const: - switch (constant_size (v->v.val_unsigned)) - { - case 1: - return DW_FORM_data1; - case 2: - return DW_FORM_data2; - case 4: - return DW_FORM_data4; - case 8: - return DW_FORM_data8; - default: - abort (); - } - case dw_val_class_long_long: - return DW_FORM_block1; - case dw_val_class_float: - return DW_FORM_block1; - case dw_val_class_flag: - return DW_FORM_flag; - case dw_val_class_die_ref: - return DW_FORM_ref; - case dw_val_class_fde_ref: - return DW_FORM_data; - case dw_val_class_lbl_id: - return DW_FORM_addr; - case dw_val_class_section_offset: - return DW_FORM_data; - case dw_val_class_str: - return DW_FORM_string; - default: - abort (); - } -} - -/* Output the encoding of an attribute value. */ - -static void -output_value_format (v) - dw_val_ref v; -{ - enum dwarf_form form = value_format (v); - - output_uleb128 (form); - if (flag_debug_asm) - fprintf (asm_out_file, " (%s)", dwarf_form_name (form)); - - fputc ('\n', asm_out_file); -} - -/* Output the .debug_abbrev section which defines the DIE abbreviation - table. */ - -static void -output_abbrev_section () -{ - unsigned long abbrev_id; - - dw_attr_ref a_attr; - for (abbrev_id = 1; abbrev_id < abbrev_die_table_in_use; ++abbrev_id) - { - register dw_die_ref abbrev = abbrev_die_table[abbrev_id]; - - output_uleb128 (abbrev_id); - if (flag_debug_asm) - fprintf (asm_out_file, " (abbrev code)"); - - fputc ('\n', asm_out_file); - output_uleb128 (abbrev->die_tag); - if (flag_debug_asm) - fprintf (asm_out_file, " (TAG: %s)", - dwarf_tag_name (abbrev->die_tag)); - - fputc ('\n', asm_out_file); - fprintf (asm_out_file, "\t%s\t0x%x", ASM_BYTE_OP, - abbrev->die_child != NULL ? DW_children_yes : DW_children_no); - - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s %s", - ASM_COMMENT_START, - (abbrev->die_child != NULL - ? "DW_children_yes" : "DW_children_no")); - - fputc ('\n', asm_out_file); - - for (a_attr = abbrev->die_attr; a_attr != NULL; - a_attr = a_attr->dw_attr_next) - { - output_uleb128 (a_attr->dw_attr); - if (flag_debug_asm) - fprintf (asm_out_file, " (%s)", - dwarf_attr_name (a_attr->dw_attr)); - - fputc ('\n', asm_out_file); - output_value_format (&a_attr->dw_attr_val); - } - - fprintf (asm_out_file, "\t%s\t0,0\n", ASM_BYTE_OP); - } -} - -/* Output location description stack opcode's operands (if any). */ - -static void -output_loc_operands (loc) - register dw_loc_descr_ref loc; -{ - register dw_val_ref val1 = &loc->dw_loc_oprnd1; - register dw_val_ref val2 = &loc->dw_loc_oprnd2; - - switch (loc->dw_loc_opc) - { - case DW_OP_addr: - ASM_OUTPUT_DWARF_ADDR_CONST (asm_out_file, val1->v.val_addr); - fputc ('\n', asm_out_file); - break; - case DW_OP_const1u: - case DW_OP_const1s: - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, val1->v.val_flag); - fputc ('\n', asm_out_file); - break; - case DW_OP_const2u: - case DW_OP_const2s: - ASM_OUTPUT_DWARF_DATA2 (asm_out_file, val1->v.val_int); - fputc ('\n', asm_out_file); - break; - case DW_OP_const4u: - case DW_OP_const4s: - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, val1->v.val_int); - fputc ('\n', asm_out_file); - break; - case DW_OP_const8u: - case DW_OP_const8s: - abort (); - fputc ('\n', asm_out_file); - break; - case DW_OP_constu: - output_uleb128 (val1->v.val_unsigned); - fputc ('\n', asm_out_file); - break; - case DW_OP_consts: - output_sleb128 (val1->v.val_int); - fputc ('\n', asm_out_file); - break; - case DW_OP_pick: - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, val1->v.val_int); - fputc ('\n', asm_out_file); - break; - case DW_OP_plus_uconst: - output_uleb128 (val1->v.val_unsigned); - fputc ('\n', asm_out_file); - break; - case DW_OP_skip: - case DW_OP_bra: - ASM_OUTPUT_DWARF_DATA2 (asm_out_file, val1->v.val_int); - fputc ('\n', asm_out_file); - break; - case DW_OP_breg0: - case DW_OP_breg1: - case DW_OP_breg2: - case DW_OP_breg3: - case DW_OP_breg4: - case DW_OP_breg5: - case DW_OP_breg6: - case DW_OP_breg7: - case DW_OP_breg8: - case DW_OP_breg9: - case DW_OP_breg10: - case DW_OP_breg11: - case DW_OP_breg12: - case DW_OP_breg13: - case DW_OP_breg14: - case DW_OP_breg15: - case DW_OP_breg16: - case DW_OP_breg17: - case DW_OP_breg18: - case DW_OP_breg19: - case DW_OP_breg20: - case DW_OP_breg21: - case DW_OP_breg22: - case DW_OP_breg23: - case DW_OP_breg24: - case DW_OP_breg25: - case DW_OP_breg26: - case DW_OP_breg27: - case DW_OP_breg28: - case DW_OP_breg29: - case DW_OP_breg30: - case DW_OP_breg31: - output_sleb128 (val1->v.val_int); - fputc ('\n', asm_out_file); - break; - case DW_OP_regx: - output_uleb128 (val1->v.val_unsigned); - fputc ('\n', asm_out_file); - break; - case DW_OP_fbreg: - output_sleb128 (val1->v.val_int); - fputc ('\n', asm_out_file); - break; - case DW_OP_bregx: - output_uleb128 (val1->v.val_unsigned); - fputc ('\n', asm_out_file); - output_sleb128 (val2->v.val_int); - fputc ('\n', asm_out_file); - break; - case DW_OP_piece: - output_uleb128 (val1->v.val_unsigned); - fputc ('\n', asm_out_file); - break; - case DW_OP_deref_size: - case DW_OP_xderef_size: - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, val1->v.val_flag); - fputc ('\n', asm_out_file); - break; - default: - break; - } -} - -/* Compute the offset of a sibling. */ - -static unsigned long -sibling_offset (die) - dw_die_ref die; -{ - unsigned long offset; - - if (die->die_child_last == NULL) - offset = die->die_offset + size_of_die (die); - else - offset = sibling_offset (die->die_child_last) + 1; - - return offset; -} - -/* Output the DIE and its attributes. Called recursively to generate - the definitions of each child DIE. */ - -static void -output_die (die) - register dw_die_ref die; -{ - register dw_attr_ref a; - register dw_die_ref c; - register unsigned long ref_offset; - register unsigned long size; - register dw_loc_descr_ref loc; - - output_uleb128 (die->die_abbrev); - if (flag_debug_asm) - fprintf (asm_out_file, " (DIE (0x%lx) %s)", - die->die_offset, dwarf_tag_name (die->die_tag)); - - fputc ('\n', asm_out_file); - - for (a = die->die_attr; a != NULL; a = a->dw_attr_next) - { - switch (a->dw_attr_val.val_class) - { - case dw_val_class_addr: - ASM_OUTPUT_DWARF_ADDR_CONST (asm_out_file, - a->dw_attr_val.v.val_addr); - break; - - case dw_val_class_loc: - size = size_of_locs (a->dw_attr_val.v.val_loc); - - /* Output the block length for this list of location operations. */ - switch (constant_size (size)) - { - case 1: - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, size); - break; - case 2: - ASM_OUTPUT_DWARF_DATA2 (asm_out_file, size); - break; - default: - abort (); - } - - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s %s", - ASM_COMMENT_START, dwarf_attr_name (a->dw_attr)); - - fputc ('\n', asm_out_file); - for (loc = a->dw_attr_val.v.val_loc; loc != NULL; - loc = loc->dw_loc_next) - { - /* Output the opcode. */ - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, loc->dw_loc_opc); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s %s", ASM_COMMENT_START, - dwarf_stack_op_name (loc->dw_loc_opc)); - - fputc ('\n', asm_out_file); - - /* Output the operand(s) (if any). */ - output_loc_operands (loc); - } - break; - - case dw_val_class_const: - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, a->dw_attr_val.v.val_int); - break; - - case dw_val_class_unsigned_const: - switch (constant_size (a->dw_attr_val.v.val_unsigned)) - { - case 1: - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, - a->dw_attr_val.v.val_unsigned); - break; - case 2: - ASM_OUTPUT_DWARF_DATA2 (asm_out_file, - a->dw_attr_val.v.val_unsigned); - break; - case 4: - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, - a->dw_attr_val.v.val_unsigned); - break; - case 8: - ASM_OUTPUT_DWARF_DATA8 (asm_out_file, - a->dw_attr_val.v.val_long_long.hi, - a->dw_attr_val.v.val_long_long.low); - break; - default: - abort (); - } - break; - - case dw_val_class_long_long: - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 8); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s %s", - ASM_COMMENT_START, dwarf_attr_name (a->dw_attr)); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DATA8 (asm_out_file, - a->dw_attr_val.v.val_long_long.hi, - a->dw_attr_val.v.val_long_long.low); - - if (flag_debug_asm) - fprintf (asm_out_file, - "\t%s long long constant", ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - break; - - case dw_val_class_float: - { - register unsigned int i; - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, - a->dw_attr_val.v.val_float.length * 4); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s %s", - ASM_COMMENT_START, dwarf_attr_name (a->dw_attr)); - - fputc ('\n', asm_out_file); - for (i = 0; i < a->dw_attr_val.v.val_float.length; ++i) - { - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, - a->dw_attr_val.v.val_float.array[i]); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s fp constant word %u", - ASM_COMMENT_START, i); - - fputc ('\n', asm_out_file); - } - break; - } - - case dw_val_class_flag: - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, a->dw_attr_val.v.val_flag); - break; - - case dw_val_class_die_ref: - if (a->dw_attr_val.v.val_die_ref != NULL) - ref_offset = a->dw_attr_val.v.val_die_ref->die_offset; - else if (a->dw_attr == DW_AT_sibling) - ref_offset = sibling_offset(die); - else - abort (); - - ASM_OUTPUT_DWARF_DATA (asm_out_file, ref_offset); - break; - - case dw_val_class_fde_ref: - { - char l1[20]; - ASM_GENERATE_INTERNAL_LABEL - (l1, FDE_AFTER_SIZE_LABEL, a->dw_attr_val.v.val_fde_index * 2); - ASM_OUTPUT_DWARF_OFFSET (asm_out_file, l1); - fprintf (asm_out_file, " - %d", DWARF_OFFSET_SIZE); - } - break; - - case dw_val_class_lbl_id: - ASM_OUTPUT_DWARF_ADDR (asm_out_file, a->dw_attr_val.v.val_lbl_id); - break; - - case dw_val_class_section_offset: - ASM_OUTPUT_DWARF_OFFSET (asm_out_file, - stripattributes - (a->dw_attr_val.v.val_section)); - break; - - case dw_val_class_str: - if (flag_debug_asm) - ASM_OUTPUT_DWARF_STRING (asm_out_file, a->dw_attr_val.v.val_str); - else - ASM_OUTPUT_ASCII (asm_out_file, - a->dw_attr_val.v.val_str, - (int) strlen (a->dw_attr_val.v.val_str) + 1); - break; - - default: - abort (); - } - - if (a->dw_attr_val.val_class != dw_val_class_loc - && a->dw_attr_val.val_class != dw_val_class_long_long - && a->dw_attr_val.val_class != dw_val_class_float) - { - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s %s", - ASM_COMMENT_START, dwarf_attr_name (a->dw_attr)); - - fputc ('\n', asm_out_file); - } - } - - for (c = die->die_child; c != NULL; c = c->die_sib) - output_die (c); - - if (die->die_child != NULL) - { - /* Add null byte to terminate sibling list. */ - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s end of children of DIE 0x%lx", - ASM_COMMENT_START, die->die_offset); - - fputc ('\n', asm_out_file); - } -} - -/* Output the compilation unit that appears at the beginning of the - .debug_info section, and precedes the DIE descriptions. */ - -static void -output_compilation_unit_header () -{ - ASM_OUTPUT_DWARF_DATA (asm_out_file, next_die_offset - DWARF_OFFSET_SIZE); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s Length of Compilation Unit Info.", - ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DATA2 (asm_out_file, DWARF_VERSION); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s DWARF version number", ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_OFFSET (asm_out_file, stripattributes (ABBREV_SECTION)); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s Offset Into Abbrev. Section", - ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, PTR_SIZE); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s Pointer Size (in bytes)", ASM_COMMENT_START); - - fputc ('\n', asm_out_file); -} - -/* The DWARF2 pubname for a nested thingy looks like "A::f". The output - of decl_printable_name for C++ looks like "A::f(int)". Let's drop the - argument list, and maybe the scope. */ - -static char * -dwarf2_name (decl, scope) - tree decl; - int scope; -{ - return (*decl_printable_name) (decl, scope ? 1 : 0); -} - -/* Add a new entry to .debug_pubnames if appropriate. */ - -static void -add_pubname (decl, die) - tree decl; - dw_die_ref die; -{ - pubname_ref p; - - if (! TREE_PUBLIC (decl)) - return; - - if (pubname_table_in_use == pubname_table_allocated) - { - pubname_table_allocated += PUBNAME_TABLE_INCREMENT; - pubname_table = (pubname_ref) xrealloc - (pubname_table, pubname_table_allocated * sizeof (pubname_entry)); - } - - p = &pubname_table[pubname_table_in_use++]; - p->die = die; - - p->name = xstrdup (dwarf2_name (decl, 1)); -} - -/* Output the public names table used to speed up access to externally - visible names. For now, only generate entries for externally - visible procedures. */ - -static void -output_pubnames () -{ - register unsigned i; - register unsigned long pubnames_length = size_of_pubnames (); - - ASM_OUTPUT_DWARF_DATA (asm_out_file, pubnames_length); - - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s Length of Public Names Info.", - ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DATA2 (asm_out_file, DWARF_VERSION); - - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s DWARF Version", ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_OFFSET (asm_out_file, stripattributes (DEBUG_INFO_SECTION)); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s Offset of Compilation Unit Info.", - ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DATA (asm_out_file, next_die_offset); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s Compilation Unit Length", ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - for (i = 0; i < pubname_table_in_use; ++i) - { - register pubname_ref pub = &pubname_table[i]; - - ASM_OUTPUT_DWARF_DATA (asm_out_file, pub->die->die_offset); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s DIE offset", ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - - if (flag_debug_asm) - { - ASM_OUTPUT_DWARF_STRING (asm_out_file, pub->name); - fprintf (asm_out_file, "%s external name", ASM_COMMENT_START); - } - else - { - ASM_OUTPUT_ASCII (asm_out_file, pub->name, - (int) strlen (pub->name) + 1); - } - - fputc ('\n', asm_out_file); - } - - ASM_OUTPUT_DWARF_DATA (asm_out_file, 0); - fputc ('\n', asm_out_file); -} - -/* Add a new entry to .debug_aranges if appropriate. */ - -static void -add_arange (decl, die) - tree decl; - dw_die_ref die; -{ - if (! DECL_SECTION_NAME (decl)) - return; - - if (arange_table_in_use == arange_table_allocated) - { - arange_table_allocated += ARANGE_TABLE_INCREMENT; - arange_table - = (arange_ref) xrealloc (arange_table, - arange_table_allocated * sizeof (dw_die_ref)); - } - - arange_table[arange_table_in_use++] = die; -} - -/* Output the information that goes into the .debug_aranges table. - Namely, define the beginning and ending address range of the - text section generated for this compilation unit. */ - -static void -output_aranges () -{ - register unsigned i; - register unsigned long aranges_length = size_of_aranges (); - - ASM_OUTPUT_DWARF_DATA (asm_out_file, aranges_length); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s Length of Address Ranges Info.", - ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DATA2 (asm_out_file, DWARF_VERSION); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s DWARF Version", ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_OFFSET (asm_out_file, stripattributes (DEBUG_INFO_SECTION)); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s Offset of Compilation Unit Info.", - ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, PTR_SIZE); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s Size of Address", ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s Size of Segment Descriptor", - ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 4); - if (PTR_SIZE == 8) - fprintf (asm_out_file, ",0,0"); - - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s Pad to %d byte boundary", - ASM_COMMENT_START, 2 * PTR_SIZE); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_ADDR (asm_out_file, stripattributes (TEXT_SECTION)); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s Address", ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_ADDR_DELTA (asm_out_file, text_end_label, - stripattributes (TEXT_SECTION)); - if (flag_debug_asm) - fprintf (asm_out_file, "%s Length", ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - for (i = 0; i < arange_table_in_use; ++i) - { - dw_die_ref a = arange_table[i]; - - if (a->die_tag == DW_TAG_subprogram) - ASM_OUTPUT_DWARF_ADDR (asm_out_file, get_AT_low_pc (a)); - else - { - char *name = get_AT_string (a, DW_AT_MIPS_linkage_name); - if (! name) - name = get_AT_string (a, DW_AT_name); - - ASM_OUTPUT_DWARF_ADDR (asm_out_file, name); - } - - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s Address", ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - if (a->die_tag == DW_TAG_subprogram) - ASM_OUTPUT_DWARF_ADDR_DELTA (asm_out_file, get_AT_hi_pc (a), - get_AT_low_pc (a)); - else - ASM_OUTPUT_DWARF_ADDR_DATA (asm_out_file, - get_AT_unsigned (a, DW_AT_byte_size)); - - if (flag_debug_asm) - fprintf (asm_out_file, "%s Length", ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - } - - /* Output the terminator words. */ - ASM_OUTPUT_DWARF_ADDR_DATA (asm_out_file, 0); - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_ADDR_DATA (asm_out_file, 0); - fputc ('\n', asm_out_file); -} - -/* Output the source line number correspondence information. This - information goes into the .debug_line section. - - If the format of this data changes, then the function size_of_line_info - must also be adjusted the same way. */ - -static void -output_line_info () -{ - char line_label[MAX_ARTIFICIAL_LABEL_BYTES]; - char prev_line_label[MAX_ARTIFICIAL_LABEL_BYTES]; - register unsigned opc; - register unsigned n_op_args; - register unsigned long ft_index; - register unsigned long lt_index; - register unsigned long current_line; - register long line_offset; - register long line_delta; - register unsigned long current_file; - register unsigned long function; - - ASM_OUTPUT_DWARF_DATA (asm_out_file, size_of_line_info ()); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s Length of Source Line Info.", - ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DATA2 (asm_out_file, DWARF_VERSION); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s DWARF Version", ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DATA (asm_out_file, size_of_line_prolog ()); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s Prolog Length", ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DWARF_LINE_MIN_INSTR_LENGTH); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s Minimum Instruction Length", - ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DWARF_LINE_DEFAULT_IS_STMT_START); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s Default is_stmt_start flag", - ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - fprintf (asm_out_file, "\t%s\t%d", ASM_BYTE_OP, DWARF_LINE_BASE); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s Line Base Value (Special Opcodes)", - ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - fprintf (asm_out_file, "\t%s\t%u", ASM_BYTE_OP, DWARF_LINE_RANGE); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s Line Range Value (Special Opcodes)", - ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - fprintf (asm_out_file, "\t%s\t%u", ASM_BYTE_OP, DWARF_LINE_OPCODE_BASE); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s Special Opcode Base", ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - for (opc = 1; opc < DWARF_LINE_OPCODE_BASE; ++opc) - { - switch (opc) - { - case DW_LNS_advance_pc: - case DW_LNS_advance_line: - case DW_LNS_set_file: - case DW_LNS_set_column: - case DW_LNS_fixed_advance_pc: - n_op_args = 1; - break; - default: - n_op_args = 0; - break; - } - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, n_op_args); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s opcode: 0x%x has %d args", - ASM_COMMENT_START, opc, n_op_args); - fputc ('\n', asm_out_file); - } - - if (flag_debug_asm) - fprintf (asm_out_file, "%s Include Directory Table\n", ASM_COMMENT_START); - - /* Include directory table is empty, at present */ - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); - fputc ('\n', asm_out_file); - if (flag_debug_asm) - fprintf (asm_out_file, "%s File Name Table\n", ASM_COMMENT_START); - - for (ft_index = 1; ft_index < file_table_in_use; ++ft_index) - { - if (flag_debug_asm) - { - ASM_OUTPUT_DWARF_STRING (asm_out_file, file_table[ft_index]); - fprintf (asm_out_file, "%s File Entry: 0x%lx", - ASM_COMMENT_START, ft_index); - } - else - { - ASM_OUTPUT_ASCII (asm_out_file, - file_table[ft_index], - (int) strlen (file_table[ft_index]) + 1); - } - - fputc ('\n', asm_out_file); - - /* Include directory index */ - output_uleb128 (0); - fputc ('\n', asm_out_file); - - /* Modification time */ - output_uleb128 (0); - fputc ('\n', asm_out_file); - - /* File length in bytes */ - output_uleb128 (0); - fputc ('\n', asm_out_file); - } - - /* Terminate the file name table */ - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); - fputc ('\n', asm_out_file); - - /* Set the address register to the first location in the text section */ - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s DW_LNE_set_address", ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - output_uleb128 (1 + PTR_SIZE); - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNE_set_address); - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_ADDR (asm_out_file, stripattributes (TEXT_SECTION)); - fputc ('\n', asm_out_file); - - /* Generate the line number to PC correspondence table, encoded as - a series of state machine operations. */ - current_file = 1; - current_line = 1; - strcpy (prev_line_label, stripattributes (TEXT_SECTION)); - for (lt_index = 1; lt_index < line_info_table_in_use; ++lt_index) - { - register dw_line_info_ref line_info; - - /* Emit debug info for the address of the current line, choosing - the encoding that uses the least amount of space. */ - /* ??? Unfortunately, we have little choice here currently, and must - always use the most general form. Gcc does not know the address - delta itself, so we can't use DW_LNS_advance_pc. There are no known - dwarf2 aware assemblers at this time, so we can't use any special - pseudo ops that would allow the assembler to optimally encode this for - us. Many ports do have length attributes which will give an upper - bound on the address range. We could perhaps use length attributes - to determine when it is safe to use DW_LNS_fixed_advance_pc. */ - ASM_GENERATE_INTERNAL_LABEL (line_label, LINE_CODE_LABEL, lt_index); - if (0) - { - /* This can handle deltas up to 0xffff. This takes 3 bytes. */ - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNS_fixed_advance_pc); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s DW_LNS_fixed_advance_pc", - ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, line_label, prev_line_label); - fputc ('\n', asm_out_file); - } - else - { - /* This can handle any delta. This takes 4+PTR_SIZE bytes. */ - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s DW_LNE_set_address", - ASM_COMMENT_START); - fputc ('\n', asm_out_file); - output_uleb128 (1 + PTR_SIZE); - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNE_set_address); - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_ADDR (asm_out_file, line_label); - fputc ('\n', asm_out_file); - } - strcpy (prev_line_label, line_label); - - /* Emit debug info for the source file of the current line, if - different from the previous line. */ - line_info = &line_info_table[lt_index]; - if (line_info->dw_file_num != current_file) - { - current_file = line_info->dw_file_num; - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNS_set_file); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s DW_LNS_set_file", ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - output_uleb128 (current_file); - if (flag_debug_asm) - fprintf (asm_out_file, " (\"%s\")", file_table[current_file]); - - fputc ('\n', asm_out_file); - } - - /* Emit debug info for the current line number, choosing the encoding - that uses the least amount of space. */ - line_offset = line_info->dw_line_num - current_line; - line_delta = line_offset - DWARF_LINE_BASE; - current_line = line_info->dw_line_num; - if (line_delta >= 0 && line_delta < (DWARF_LINE_RANGE - 1)) - { - /* This can handle deltas from -10 to 234, using the current - definitions of DWARF_LINE_BASE and DWARF_LINE_RANGE. This - takes 1 byte. */ - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, - DWARF_LINE_OPCODE_BASE + line_delta); - if (flag_debug_asm) - fprintf (asm_out_file, - "\t%s line %ld", ASM_COMMENT_START, current_line); - - fputc ('\n', asm_out_file); - } - else - { - /* This can handle any delta. This takes at least 4 bytes, depending - on the value being encoded. */ - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNS_advance_line); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s advance to line %ld", - ASM_COMMENT_START, current_line); - - fputc ('\n', asm_out_file); - output_sleb128 (line_offset); - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNS_copy); - fputc ('\n', asm_out_file); - } - } - - /* Emit debug info for the address of the end of the function. */ - if (0) - { - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNS_fixed_advance_pc); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s DW_LNS_fixed_advance_pc", - ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, text_end_label, prev_line_label); - fputc ('\n', asm_out_file); - } - else - { - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s DW_LNE_set_address", ASM_COMMENT_START); - fputc ('\n', asm_out_file); - output_uleb128 (1 + PTR_SIZE); - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNE_set_address); - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_ADDR (asm_out_file, text_end_label); - fputc ('\n', asm_out_file); - } - - /* Output the marker for the end of the line number info. */ - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s DW_LNE_end_sequence", ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - output_uleb128 (1); - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNE_end_sequence); - fputc ('\n', asm_out_file); - - function = 0; - current_file = 1; - current_line = 1; - for (lt_index = 0; lt_index < separate_line_info_table_in_use; ) - { - register dw_separate_line_info_ref line_info - = &separate_line_info_table[lt_index]; - - /* Emit debug info for the address of the current line. If this is - a new function, or the first line of a function, then we need - to handle it differently. */ - ASM_GENERATE_INTERNAL_LABEL (line_label, SEPARATE_LINE_CODE_LABEL, - lt_index); - if (function != line_info->function) - { - function = line_info->function; - - /* Set the address register to the first line in the function */ - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s DW_LNE_set_address", - ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - output_uleb128 (1 + PTR_SIZE); - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNE_set_address); - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_ADDR (asm_out_file, line_label); - fputc ('\n', asm_out_file); - } - else - { - /* ??? See the DW_LNS_advance_pc comment above. */ - if (0) - { - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNS_fixed_advance_pc); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s DW_LNS_fixed_advance_pc", - ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, line_label, - prev_line_label); - fputc ('\n', asm_out_file); - } - else - { - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s DW_LNE_set_address", - ASM_COMMENT_START); - fputc ('\n', asm_out_file); - output_uleb128 (1 + PTR_SIZE); - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNE_set_address); - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_ADDR (asm_out_file, line_label); - fputc ('\n', asm_out_file); - } - } - strcpy (prev_line_label, line_label); - - /* Emit debug info for the source file of the current line, if - different from the previous line. */ - if (line_info->dw_file_num != current_file) - { - current_file = line_info->dw_file_num; - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNS_set_file); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s DW_LNS_set_file", ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - output_uleb128 (current_file); - if (flag_debug_asm) - fprintf (asm_out_file, " (\"%s\")", file_table[current_file]); - - fputc ('\n', asm_out_file); - } - - /* Emit debug info for the current line number, choosing the encoding - that uses the least amount of space. */ - if (line_info->dw_line_num != current_line) - { - line_offset = line_info->dw_line_num - current_line; - line_delta = line_offset - DWARF_LINE_BASE; - current_line = line_info->dw_line_num; - if (line_delta >= 0 && line_delta < (DWARF_LINE_RANGE - 1)) - { - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, - DWARF_LINE_OPCODE_BASE + line_delta); - if (flag_debug_asm) - fprintf (asm_out_file, - "\t%s line %ld", ASM_COMMENT_START, current_line); - - fputc ('\n', asm_out_file); - } - else - { - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNS_advance_line); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s advance to line %ld", - ASM_COMMENT_START, current_line); - - fputc ('\n', asm_out_file); - output_sleb128 (line_offset); - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNS_copy); - fputc ('\n', asm_out_file); - } - } - - ++lt_index; - - /* If we're done with a function, end its sequence. */ - if (lt_index == separate_line_info_table_in_use - || separate_line_info_table[lt_index].function != function) - { - current_file = 1; - current_line = 1; - - /* Emit debug info for the address of the end of the function. */ - ASM_GENERATE_INTERNAL_LABEL (line_label, FUNC_END_LABEL, function); - if (0) - { - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNS_fixed_advance_pc); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s DW_LNS_fixed_advance_pc", - ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, line_label, - prev_line_label); - fputc ('\n', asm_out_file); - } - else - { - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s DW_LNE_set_address", - ASM_COMMENT_START); - fputc ('\n', asm_out_file); - output_uleb128 (1 + PTR_SIZE); - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNE_set_address); - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_ADDR (asm_out_file, line_label); - fputc ('\n', asm_out_file); - } - - /* Output the marker for the end of this sequence. */ - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s DW_LNE_end_sequence", - ASM_COMMENT_START); - - fputc ('\n', asm_out_file); - output_uleb128 (1); - fputc ('\n', asm_out_file); - ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNE_end_sequence); - fputc ('\n', asm_out_file); - } - } -} - -/* Given a pointer to a BLOCK node return non-zero if (and only if) the node - in question represents the outermost pair of curly braces (i.e. the "body - block") of a function or method. - - For any BLOCK node representing a "body block" of a function or method, the - BLOCK_SUPERCONTEXT of the node will point to another BLOCK node which - represents the outermost (function) scope for the function or method (i.e. - the one which includes the formal parameters). The BLOCK_SUPERCONTEXT of - *that* node in turn will point to the relevant FUNCTION_DECL node. */ - -static inline int -is_body_block (stmt) - register tree stmt; -{ - if (TREE_CODE (stmt) == BLOCK) - { - register tree parent = BLOCK_SUPERCONTEXT (stmt); - - if (TREE_CODE (parent) == BLOCK) - { - register tree grandparent = BLOCK_SUPERCONTEXT (parent); - - if (TREE_CODE (grandparent) == FUNCTION_DECL) - return 1; - } - } - - return 0; -} - -/* Given a pointer to a tree node for some base type, return a pointer to - a DIE that describes the given type. - - This routine must only be called for GCC type nodes that correspond to - Dwarf base (fundamental) types. */ - -static dw_die_ref -base_type_die (type) - register tree type; -{ - register dw_die_ref base_type_result; - register char *type_name; - register enum dwarf_type encoding; - register tree name = TYPE_NAME (type); - - if (TREE_CODE (type) == ERROR_MARK - || TREE_CODE (type) == VOID_TYPE) - return 0; - - if (TREE_CODE (name) == TYPE_DECL) - name = DECL_NAME (name); - type_name = IDENTIFIER_POINTER (name); - - switch (TREE_CODE (type)) - { - case INTEGER_TYPE: - /* Carefully distinguish the C character types, without messing - up if the language is not C. Note that we check only for the names - that contain spaces; other names might occur by coincidence in other - languages. */ - if (! (TYPE_PRECISION (type) == CHAR_TYPE_SIZE - && (type == char_type_node - || ! strcmp (type_name, "signed char") - || ! strcmp (type_name, "unsigned char")))) - { - if (TREE_UNSIGNED (type)) - encoding = DW_ATE_unsigned; - else - encoding = DW_ATE_signed; - break; - } - /* else fall through */ - - case CHAR_TYPE: - /* GNU Pascal/Ada CHAR type. Not used in C. */ - if (TREE_UNSIGNED (type)) - encoding = DW_ATE_unsigned_char; - else - encoding = DW_ATE_signed_char; - break; - - case REAL_TYPE: - encoding = DW_ATE_float; - break; - - case COMPLEX_TYPE: - encoding = DW_ATE_complex_float; - break; - - case BOOLEAN_TYPE: - /* GNU FORTRAN/Ada/C++ BOOLEAN type. */ - encoding = DW_ATE_boolean; - break; - - default: - abort (); /* No other TREE_CODEs are Dwarf fundamental types. */ - } - - base_type_result = new_die (DW_TAG_base_type, comp_unit_die); - add_AT_string (base_type_result, DW_AT_name, type_name); - add_AT_unsigned (base_type_result, DW_AT_byte_size, - int_size_in_bytes (type)); - add_AT_unsigned (base_type_result, DW_AT_encoding, encoding); - - return base_type_result; -} - -/* Given a pointer to an arbitrary ..._TYPE tree node, return a pointer to - the Dwarf "root" type for the given input type. The Dwarf "root" type of - a given type is generally the same as the given type, except that if the - given type is a pointer or reference type, then the root type of the given - type is the root type of the "basis" type for the pointer or reference - type. (This definition of the "root" type is recursive.) Also, the root - type of a `const' qualified type or a `volatile' qualified type is the - root type of the given type without the qualifiers. */ - -static tree -root_type (type) - register tree type; -{ - if (TREE_CODE (type) == ERROR_MARK) - return error_mark_node; - - switch (TREE_CODE (type)) - { - case ERROR_MARK: - return error_mark_node; - - case POINTER_TYPE: - case REFERENCE_TYPE: - return type_main_variant (root_type (TREE_TYPE (type))); - - default: - return type_main_variant (type); - } -} - -/* Given a pointer to an arbitrary ..._TYPE tree node, return non-zero if the - given input type is a Dwarf "fundamental" type. Otherwise return null. */ - -static inline int -is_base_type (type) - register tree type; -{ - switch (TREE_CODE (type)) - { - case ERROR_MARK: - case VOID_TYPE: - case INTEGER_TYPE: - case REAL_TYPE: - case COMPLEX_TYPE: - case BOOLEAN_TYPE: - case CHAR_TYPE: - return 1; - - case SET_TYPE: - case ARRAY_TYPE: - case RECORD_TYPE: - case UNION_TYPE: - case QUAL_UNION_TYPE: - case ENUMERAL_TYPE: - case FUNCTION_TYPE: - case METHOD_TYPE: - case POINTER_TYPE: - case REFERENCE_TYPE: - case FILE_TYPE: - case OFFSET_TYPE: - case LANG_TYPE: - return 0; - - default: - abort (); - } - - return 0; -} - -/* Given a pointer to an arbitrary ..._TYPE tree node, return a debugging - entry that chains various modifiers in front of the given type. */ - -static dw_die_ref -modified_type_die (type, is_const_type, is_volatile_type, context_die) - register tree type; - register int is_const_type; - register int is_volatile_type; - register dw_die_ref context_die; -{ - register enum tree_code code = TREE_CODE (type); - register dw_die_ref mod_type_die = NULL; - register dw_die_ref sub_die = NULL; - register tree item_type = NULL; - - if (code != ERROR_MARK) - { - type = build_type_variant (type, is_const_type, is_volatile_type); - - mod_type_die = lookup_type_die (type); - if (mod_type_die) - return mod_type_die; - - /* Handle C typedef types. */ - if (TYPE_NAME (type) && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL - && DECL_ORIGINAL_TYPE (TYPE_NAME (type))) - { - tree dtype = TREE_TYPE (TYPE_NAME (type)); - if (type == dtype) - { - /* For a named type, use the typedef. */ - gen_type_die (type, context_die); - mod_type_die = lookup_type_die (type); - } - - else if (is_const_type < TYPE_READONLY (dtype) - || is_volatile_type < TYPE_VOLATILE (dtype)) - /* cv-unqualified version of named type. Just use the unnamed - type to which it refers. */ - mod_type_die - = modified_type_die (DECL_ORIGINAL_TYPE (TYPE_NAME (type)), - is_const_type, is_volatile_type, - context_die); - /* Else cv-qualified version of named type; fall through. */ - } - - if (mod_type_die) - /* OK */; - else if (is_const_type) - { - mod_type_die = new_die (DW_TAG_const_type, comp_unit_die); - sub_die = modified_type_die (type, 0, is_volatile_type, context_die); - } - else if (is_volatile_type) - { - mod_type_die = new_die (DW_TAG_volatile_type, comp_unit_die); - sub_die = modified_type_die (type, 0, 0, context_die); - } - else if (code == POINTER_TYPE) - { - mod_type_die = new_die (DW_TAG_pointer_type, comp_unit_die); - add_AT_unsigned (mod_type_die, DW_AT_byte_size, PTR_SIZE); -#if 0 - add_AT_unsigned (mod_type_die, DW_AT_address_class, 0); -#endif - item_type = TREE_TYPE (type); - } - else if (code == REFERENCE_TYPE) - { - mod_type_die = new_die (DW_TAG_reference_type, comp_unit_die); - add_AT_unsigned (mod_type_die, DW_AT_byte_size, PTR_SIZE); -#if 0 - add_AT_unsigned (mod_type_die, DW_AT_address_class, 0); -#endif - item_type = TREE_TYPE (type); - } - else if (is_base_type (type)) - mod_type_die = base_type_die (type); - else - { - gen_type_die (type, context_die); - - /* We have to get the type_main_variant here (and pass that to the - `lookup_type_die' routine) because the ..._TYPE node we have - might simply be a *copy* of some original type node (where the - copy was created to help us keep track of typedef names) and - that copy might have a different TYPE_UID from the original - ..._TYPE node. */ - mod_type_die = lookup_type_die (type_main_variant (type)); - if (mod_type_die == NULL) - abort (); - } - } - - equate_type_number_to_die (type, mod_type_die); - if (item_type) - /* We must do this after the equate_type_number_to_die call, in case - this is a recursive type. This ensures that the modified_type_die - recursion will terminate even if the type is recursive. Recursive - types are possible in Ada. */ - sub_die = modified_type_die (item_type, - TYPE_READONLY (item_type), - TYPE_VOLATILE (item_type), - context_die); - - if (sub_die != NULL) - add_AT_die_ref (mod_type_die, DW_AT_type, sub_die); - - return mod_type_die; -} - -/* Given a pointer to an arbitrary ..._TYPE tree node, return true if it is - an enumerated type. */ - -static inline int -type_is_enum (type) - register tree type; -{ - return TREE_CODE (type) == ENUMERAL_TYPE; -} - -/* Return a location descriptor that designates a machine register. */ - -static dw_loc_descr_ref -reg_loc_descriptor (rtl) - register rtx rtl; -{ - register dw_loc_descr_ref loc_result = NULL; - register unsigned reg = reg_number (rtl); - - if (reg <= 31) - loc_result = new_loc_descr (DW_OP_reg0 + reg, 0, 0); - else - loc_result = new_loc_descr (DW_OP_regx, reg, 0); - - return loc_result; -} - -/* Return a location descriptor that designates a base+offset location. */ - -static dw_loc_descr_ref -based_loc_descr (reg, offset) - unsigned reg; - long int offset; -{ - register dw_loc_descr_ref loc_result; - /* For the "frame base", we use the frame pointer or stack pointer - registers, since the RTL for local variables is relative to one of - them. */ - register unsigned fp_reg = DBX_REGISTER_NUMBER (frame_pointer_needed - ? HARD_FRAME_POINTER_REGNUM - : STACK_POINTER_REGNUM); - - if (reg == fp_reg) - loc_result = new_loc_descr (DW_OP_fbreg, offset, 0); - else if (reg <= 31) - loc_result = new_loc_descr (DW_OP_breg0 + reg, offset, 0); - else - loc_result = new_loc_descr (DW_OP_bregx, reg, offset); - - return loc_result; -} - -/* Return true if this RTL expression describes a base+offset calculation. */ - -static inline int -is_based_loc (rtl) - register rtx rtl; -{ - return (GET_CODE (rtl) == PLUS - && ((GET_CODE (XEXP (rtl, 0)) == REG - && GET_CODE (XEXP (rtl, 1)) == CONST_INT))); -} - -/* The following routine converts the RTL for a variable or parameter - (resident in memory) into an equivalent Dwarf representation of a - mechanism for getting the address of that same variable onto the top of a - hypothetical "address evaluation" stack. - - When creating memory location descriptors, we are effectively transforming - the RTL for a memory-resident object into its Dwarf postfix expression - equivalent. This routine recursively descends an RTL tree, turning - it into Dwarf postfix code as it goes. */ - -static dw_loc_descr_ref -mem_loc_descriptor (rtl) - register rtx rtl; -{ - dw_loc_descr_ref mem_loc_result = NULL; - /* Note that for a dynamically sized array, the location we will generate a - description of here will be the lowest numbered location which is - actually within the array. That's *not* necessarily the same as the - zeroth element of the array. */ - - switch (GET_CODE (rtl)) - { - case SUBREG: - /* The case of a subreg may arise when we have a local (register) - variable or a formal (register) parameter which doesn't quite fill - up an entire register. For now, just assume that it is - legitimate to make the Dwarf info refer to the whole register which - contains the given subreg. */ - rtl = XEXP (rtl, 0); - - /* ... fall through ... */ - - case REG: - /* Whenever a register number forms a part of the description of the - method for calculating the (dynamic) address of a memory resident - object, DWARF rules require the register number be referred to as - a "base register". This distinction is not based in any way upon - what category of register the hardware believes the given register - belongs to. This is strictly DWARF terminology we're dealing with - here. Note that in cases where the location of a memory-resident - data object could be expressed as: OP_ADD (OP_BASEREG (basereg), - OP_CONST (0)) the actual DWARF location descriptor that we generate - may just be OP_BASEREG (basereg). This may look deceptively like - the object in question was allocated to a register (rather than in - memory) so DWARF consumers need to be aware of the subtle - distinction between OP_REG and OP_BASEREG. */ - mem_loc_result = based_loc_descr (reg_number (rtl), 0); - break; - - case MEM: - mem_loc_result = mem_loc_descriptor (XEXP (rtl, 0)); - add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_deref, 0, 0)); - break; - - case CONST: - case LABEL_REF: - case SYMBOL_REF: - mem_loc_result = new_loc_descr (DW_OP_addr, 0, 0); - mem_loc_result->dw_loc_oprnd1.val_class = dw_val_class_addr; - mem_loc_result->dw_loc_oprnd1.v.val_addr = save_rtx (rtl); - break; - - case PLUS: - if (is_based_loc (rtl)) - mem_loc_result = based_loc_descr (reg_number (XEXP (rtl, 0)), - INTVAL (XEXP (rtl, 1))); - else - { - add_loc_descr (&mem_loc_result, mem_loc_descriptor (XEXP (rtl, 0))); - add_loc_descr (&mem_loc_result, mem_loc_descriptor (XEXP (rtl, 1))); - add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_plus, 0, 0)); - } - break; - - case MULT: - /* If a pseudo-reg is optimized away, it is possible for it to - be replaced with a MEM containing a multiply. */ - add_loc_descr (&mem_loc_result, mem_loc_descriptor (XEXP (rtl, 0))); - add_loc_descr (&mem_loc_result, mem_loc_descriptor (XEXP (rtl, 1))); - add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_mul, 0, 0)); - break; - - case CONST_INT: - mem_loc_result = new_loc_descr (DW_OP_constu, INTVAL (rtl), 0); - break; - - default: - abort (); - } - - return mem_loc_result; -} - -/* Return a descriptor that describes the concatenation of two locations. - This is typically a complex variable. */ - -static dw_loc_descr_ref -concat_loc_descriptor (x0, x1) - register rtx x0, x1; -{ - dw_loc_descr_ref cc_loc_result = NULL; - - if (!is_pseudo_reg (x0) - && (GET_CODE (x0) != MEM || !is_pseudo_reg (XEXP (x0, 0)))) - add_loc_descr (&cc_loc_result, loc_descriptor (x0)); - add_loc_descr (&cc_loc_result, - new_loc_descr (DW_OP_piece, GET_MODE_SIZE (GET_MODE (x0)), 0)); - - if (!is_pseudo_reg (x1) - && (GET_CODE (x1) != MEM || !is_pseudo_reg (XEXP (x1, 0)))) - add_loc_descr (&cc_loc_result, loc_descriptor (x1)); - add_loc_descr (&cc_loc_result, - new_loc_descr (DW_OP_piece, GET_MODE_SIZE (GET_MODE (x1)), 0)); - - return cc_loc_result; -} - -/* Output a proper Dwarf location descriptor for a variable or parameter - which is either allocated in a register or in a memory location. For a - register, we just generate an OP_REG and the register number. For a - memory location we provide a Dwarf postfix expression describing how to - generate the (dynamic) address of the object onto the address stack. */ - -static dw_loc_descr_ref -loc_descriptor (rtl) - register rtx rtl; -{ - dw_loc_descr_ref loc_result = NULL; - switch (GET_CODE (rtl)) - { - case SUBREG: - /* The case of a subreg may arise when we have a local (register) - variable or a formal (register) parameter which doesn't quite fill - up an entire register. For now, just assume that it is - legitimate to make the Dwarf info refer to the whole register which - contains the given subreg. */ - rtl = XEXP (rtl, 0); - - /* ... fall through ... */ - - case REG: - loc_result = reg_loc_descriptor (rtl); - break; - - case MEM: - loc_result = mem_loc_descriptor (XEXP (rtl, 0)); - break; - - case CONCAT: - loc_result = concat_loc_descriptor (XEXP (rtl, 0), XEXP (rtl, 1)); - break; - - default: - abort (); - } - - return loc_result; -} - -/* Given an unsigned value, round it up to the lowest multiple of `boundary' - which is not less than the value itself. */ - -static inline unsigned -ceiling (value, boundary) - register unsigned value; - register unsigned boundary; -{ - return (((value + boundary - 1) / boundary) * boundary); -} - -/* Given a pointer to what is assumed to be a FIELD_DECL node, return a - pointer to the declared type for the relevant field variable, or return - `integer_type_node' if the given node turns out to be an - ERROR_MARK node. */ - -static inline tree -field_type (decl) - register tree decl; -{ - register tree type; - - if (TREE_CODE (decl) == ERROR_MARK) - return integer_type_node; - - type = DECL_BIT_FIELD_TYPE (decl); - if (type == NULL_TREE) - type = TREE_TYPE (decl); - - return type; -} - -/* Given a pointer to a tree node, assumed to be some kind of a ..._TYPE - node, return the alignment in bits for the type, or else return - BITS_PER_WORD if the node actually turns out to be an - ERROR_MARK node. */ - -static inline unsigned -simple_type_align_in_bits (type) - register tree type; -{ - return (TREE_CODE (type) != ERROR_MARK) ? TYPE_ALIGN (type) : BITS_PER_WORD; -} - -/* Given a pointer to a tree node, assumed to be some kind of a ..._TYPE - node, return the size in bits for the type if it is a constant, or else - return the alignment for the type if the type's size is not constant, or - else return BITS_PER_WORD if the type actually turns out to be an - ERROR_MARK node. */ - -static inline unsigned -simple_type_size_in_bits (type) - register tree type; -{ - if (TREE_CODE (type) == ERROR_MARK) - return BITS_PER_WORD; - else - { - register tree type_size_tree = TYPE_SIZE (type); - - if (TREE_CODE (type_size_tree) != INTEGER_CST) - return TYPE_ALIGN (type); - - return (unsigned) TREE_INT_CST_LOW (type_size_tree); - } -} - -/* Given a pointer to what is assumed to be a FIELD_DECL node, compute and - return the byte offset of the lowest addressed byte of the "containing - object" for the given FIELD_DECL, or return 0 if we are unable to - determine what that offset is, either because the argument turns out to - be a pointer to an ERROR_MARK node, or because the offset is actually - variable. (We can't handle the latter case just yet). */ - -static unsigned -field_byte_offset (decl) - register tree decl; -{ - register unsigned type_align_in_bytes; - register unsigned type_align_in_bits; - register unsigned type_size_in_bits; - register unsigned object_offset_in_align_units; - register unsigned object_offset_in_bits; - register unsigned object_offset_in_bytes; - register tree type; - register tree bitpos_tree; - register tree field_size_tree; - register unsigned bitpos_int; - register unsigned deepest_bitpos; - register unsigned field_size_in_bits; - - if (TREE_CODE (decl) == ERROR_MARK) - return 0; - - if (TREE_CODE (decl) != FIELD_DECL) - abort (); - - type = field_type (decl); - - bitpos_tree = DECL_FIELD_BITPOS (decl); - field_size_tree = DECL_SIZE (decl); - - /* We cannot yet cope with fields whose positions or sizes are variable, so - for now, when we see such things, we simply return 0. Someday, we may - be able to handle such cases, but it will be damn difficult. */ - if (TREE_CODE (bitpos_tree) != INTEGER_CST) - return 0; - bitpos_int = (unsigned) TREE_INT_CST_LOW (bitpos_tree); - - if (TREE_CODE (field_size_tree) != INTEGER_CST) - return 0; - - field_size_in_bits = (unsigned) TREE_INT_CST_LOW (field_size_tree); - type_size_in_bits = simple_type_size_in_bits (type); - type_align_in_bits = simple_type_align_in_bits (type); - type_align_in_bytes = type_align_in_bits / BITS_PER_UNIT; - - /* Note that the GCC front-end doesn't make any attempt to keep track of - the starting bit offset (relative to the start of the containing - structure type) of the hypothetical "containing object" for a bit- - field. Thus, when computing the byte offset value for the start of the - "containing object" of a bit-field, we must deduce this information on - our own. This can be rather tricky to do in some cases. For example, - handling the following structure type definition when compiling for an - i386/i486 target (which only aligns long long's to 32-bit boundaries) - can be very tricky: - - struct S { int field1; long long field2:31; }; - - Fortunately, there is a simple rule-of-thumb which can be - used in such cases. When compiling for an i386/i486, GCC will allocate - 8 bytes for the structure shown above. It decides to do this based upon - one simple rule for bit-field allocation. Quite simply, GCC allocates - each "containing object" for each bit-field at the first (i.e. lowest - addressed) legitimate alignment boundary (based upon the required - minimum alignment for the declared type of the field) which it can - possibly use, subject to the condition that there is still enough - available space remaining in the containing object (when allocated at - the selected point) to fully accommodate all of the bits of the - bit-field itself. This simple rule makes it obvious why GCC allocates - 8 bytes for each object of the structure type shown above. When looking - for a place to allocate the "containing object" for `field2', the - compiler simply tries to allocate a 64-bit "containing object" at each - successive 32-bit boundary (starting at zero) until it finds a place to - allocate that 64- bit field such that at least 31 contiguous (and - previously unallocated) bits remain within that selected 64 bit field. - (As it turns out, for the example above, the compiler finds that it is - OK to allocate the "containing object" 64-bit field at bit-offset zero - within the structure type.) Here we attempt to work backwards from the - limited set of facts we're given, and we try to deduce from those facts, - where GCC must have believed that the containing object started (within - the structure type). The value we deduce is then used (by the callers of - this routine) to generate DW_AT_location and DW_AT_bit_offset attributes - for fields (both bit-fields and, in the case of DW_AT_location, regular - fields as well). */ - - /* Figure out the bit-distance from the start of the structure to the - "deepest" bit of the bit-field. */ - deepest_bitpos = bitpos_int + field_size_in_bits; - - /* This is the tricky part. Use some fancy footwork to deduce where the - lowest addressed bit of the containing object must be. */ - object_offset_in_bits - = ceiling (deepest_bitpos, type_align_in_bits) - type_size_in_bits; - - /* Compute the offset of the containing object in "alignment units". */ - object_offset_in_align_units = object_offset_in_bits / type_align_in_bits; - - /* Compute the offset of the containing object in bytes. */ - object_offset_in_bytes = object_offset_in_align_units * type_align_in_bytes; - - return object_offset_in_bytes; -} - -/* The following routines define various Dwarf attributes and any data - associated with them. */ - -/* Add a location description attribute value to a DIE. - - This emits location attributes suitable for whole variables and - whole parameters. Note that the location attributes for struct fields are - generated by the routine `data_member_location_attribute' below. */ - -static void -add_AT_location_description (die, attr_kind, rtl) - dw_die_ref die; - enum dwarf_attribute attr_kind; - register rtx rtl; -{ - /* Handle a special case. If we are about to output a location descriptor - for a variable or parameter which has been optimized out of existence, - don't do that. A variable which has been optimized out - of existence will have a DECL_RTL value which denotes a pseudo-reg. - Currently, in some rare cases, variables can have DECL_RTL values which - look like (MEM (REG pseudo-reg#)). These cases are due to bugs - elsewhere in the compiler. We treat such cases as if the variable(s) in - question had been optimized out of existence. */ - - if (is_pseudo_reg (rtl) - || (GET_CODE (rtl) == MEM - && is_pseudo_reg (XEXP (rtl, 0))) - || (GET_CODE (rtl) == CONCAT - && is_pseudo_reg (XEXP (rtl, 0)) - && is_pseudo_reg (XEXP (rtl, 1)))) - return; - - add_AT_loc (die, attr_kind, loc_descriptor (rtl)); -} - -/* Attach the specialized form of location attribute used for data - members of struct and union types. In the special case of a - FIELD_DECL node which represents a bit-field, the "offset" part - of this special location descriptor must indicate the distance - in bytes from the lowest-addressed byte of the containing struct - or union type to the lowest-addressed byte of the "containing - object" for the bit-field. (See the `field_byte_offset' function - above).. For any given bit-field, the "containing object" is a - hypothetical object (of some integral or enum type) within which - the given bit-field lives. The type of this hypothetical - "containing object" is always the same as the declared type of - the individual bit-field itself (for GCC anyway... the DWARF - spec doesn't actually mandate this). Note that it is the size - (in bytes) of the hypothetical "containing object" which will - be given in the DW_AT_byte_size attribute for this bit-field. - (See the `byte_size_attribute' function below.) It is also used - when calculating the value of the DW_AT_bit_offset attribute. - (See the `bit_offset_attribute' function below). */ - -static void -add_data_member_location_attribute (die, decl) - register dw_die_ref die; - register tree decl; -{ - register unsigned long offset; - register dw_loc_descr_ref loc_descr; - register enum dwarf_location_atom op; - - if (TREE_CODE (decl) == TREE_VEC) - offset = TREE_INT_CST_LOW (BINFO_OFFSET (decl)); - else - offset = field_byte_offset (decl); - - /* The DWARF2 standard says that we should assume that the structure address - is already on the stack, so we can specify a structure field address - by using DW_OP_plus_uconst. */ - -#ifdef MIPS_DEBUGGING_INFO - /* ??? The SGI dwarf reader does not handle the DW_OP_plus_uconst operator - correctly. It works only if we leave the offset on the stack. */ - op = DW_OP_constu; -#else - op = DW_OP_plus_uconst; -#endif - - loc_descr = new_loc_descr (op, offset, 0); - add_AT_loc (die, DW_AT_data_member_location, loc_descr); -} - -/* Attach an DW_AT_const_value attribute for a variable or a parameter which - does not have a "location" either in memory or in a register. These - things can arise in GNU C when a constant is passed as an actual parameter - to an inlined function. They can also arise in C++ where declared - constants do not necessarily get memory "homes". */ - -static void -add_const_value_attribute (die, rtl) - register dw_die_ref die; - register rtx rtl; -{ - switch (GET_CODE (rtl)) - { - case CONST_INT: - /* Note that a CONST_INT rtx could represent either an integer or a - floating-point constant. A CONST_INT is used whenever the constant - will fit into a single word. In all such cases, the original mode - of the constant value is wiped out, and the CONST_INT rtx is - assigned VOIDmode. */ - add_AT_unsigned (die, DW_AT_const_value, (unsigned) INTVAL (rtl)); - break; - - case CONST_DOUBLE: - /* Note that a CONST_DOUBLE rtx could represent either an integer or a - floating-point constant. A CONST_DOUBLE is used whenever the - constant requires more than one word in order to be adequately - represented. We output CONST_DOUBLEs as blocks. */ - { - register enum machine_mode mode = GET_MODE (rtl); - - if (GET_MODE_CLASS (mode) == MODE_FLOAT) - { - register unsigned length = GET_MODE_SIZE (mode) / sizeof (long); - long array[4]; - REAL_VALUE_TYPE rv; - - REAL_VALUE_FROM_CONST_DOUBLE (rv, rtl); - switch (mode) - { - case SFmode: - REAL_VALUE_TO_TARGET_SINGLE (rv, array[0]); - break; - - case DFmode: - REAL_VALUE_TO_TARGET_DOUBLE (rv, array); - break; - - case XFmode: - case TFmode: - REAL_VALUE_TO_TARGET_LONG_DOUBLE (rv, array); - break; - - default: - abort (); - } - - add_AT_float (die, DW_AT_const_value, length, array); - } - else - add_AT_long_long (die, DW_AT_const_value, - CONST_DOUBLE_HIGH (rtl), CONST_DOUBLE_LOW (rtl)); - } - break; - - case CONST_STRING: - add_AT_string (die, DW_AT_const_value, XSTR (rtl, 0)); - break; - - case SYMBOL_REF: - case LABEL_REF: - case CONST: - add_AT_addr (die, DW_AT_const_value, save_rtx (rtl)); - break; - - case PLUS: - /* In cases where an inlined instance of an inline function is passed - the address of an `auto' variable (which is local to the caller) we - can get a situation where the DECL_RTL of the artificial local - variable (for the inlining) which acts as a stand-in for the - corresponding formal parameter (of the inline function) will look - like (plus:SI (reg:SI FRAME_PTR) (const_int ...)). This is not - exactly a compile-time constant expression, but it isn't the address - of the (artificial) local variable either. Rather, it represents the - *value* which the artificial local variable always has during its - lifetime. We currently have no way to represent such quasi-constant - values in Dwarf, so for now we just punt and generate nothing. */ - break; - - default: - /* No other kinds of rtx should be possible here. */ - abort (); - } - -} - -/* Generate *either* an DW_AT_location attribute or else an DW_AT_const_value - data attribute for a variable or a parameter. We generate the - DW_AT_const_value attribute only in those cases where the given variable - or parameter does not have a true "location" either in memory or in a - register. This can happen (for example) when a constant is passed as an - actual argument in a call to an inline function. (It's possible that - these things can crop up in other ways also.) Note that one type of - constant value which can be passed into an inlined function is a constant - pointer. This can happen for example if an actual argument in an inlined - function call evaluates to a compile-time constant address. */ - -static void -add_location_or_const_value_attribute (die, decl) - register dw_die_ref die; - register tree decl; -{ - register rtx rtl; - register tree declared_type; - register tree passed_type; - - if (TREE_CODE (decl) == ERROR_MARK) - return; - - if (TREE_CODE (decl) != VAR_DECL && TREE_CODE (decl) != PARM_DECL) - abort (); - - /* Here we have to decide where we are going to say the parameter "lives" - (as far as the debugger is concerned). We only have a couple of - choices. GCC provides us with DECL_RTL and with DECL_INCOMING_RTL. - - DECL_RTL normally indicates where the parameter lives during most of the - activation of the function. If optimization is enabled however, this - could be either NULL or else a pseudo-reg. Both of those cases indicate - that the parameter doesn't really live anywhere (as far as the code - generation parts of GCC are concerned) during most of the function's - activation. That will happen (for example) if the parameter is never - referenced within the function. - - We could just generate a location descriptor here for all non-NULL - non-pseudo values of DECL_RTL and ignore all of the rest, but we can be - a little nicer than that if we also consider DECL_INCOMING_RTL in cases - where DECL_RTL is NULL or is a pseudo-reg. - - Note however that we can only get away with using DECL_INCOMING_RTL as - a backup substitute for DECL_RTL in certain limited cases. In cases - where DECL_ARG_TYPE (decl) indicates the same type as TREE_TYPE (decl), - we can be sure that the parameter was passed using the same type as it is - declared to have within the function, and that its DECL_INCOMING_RTL - points us to a place where a value of that type is passed. - - In cases where DECL_ARG_TYPE (decl) and TREE_TYPE (decl) are different, - we cannot (in general) use DECL_INCOMING_RTL as a substitute for DECL_RTL - because in these cases DECL_INCOMING_RTL points us to a value of some - type which is *different* from the type of the parameter itself. Thus, - if we tried to use DECL_INCOMING_RTL to generate a location attribute in - such cases, the debugger would end up (for example) trying to fetch a - `float' from a place which actually contains the first part of a - `double'. That would lead to really incorrect and confusing - output at debug-time. - - So, in general, we *do not* use DECL_INCOMING_RTL as a backup for DECL_RTL - in cases where DECL_ARG_TYPE (decl) != TREE_TYPE (decl). There - are a couple of exceptions however. On little-endian machines we can - get away with using DECL_INCOMING_RTL even when DECL_ARG_TYPE (decl) is - not the same as TREE_TYPE (decl), but only when DECL_ARG_TYPE (decl) is - an integral type that is smaller than TREE_TYPE (decl). These cases arise - when (on a little-endian machine) a non-prototyped function has a - parameter declared to be of type `short' or `char'. In such cases, - TREE_TYPE (decl) will be `short' or `char', DECL_ARG_TYPE (decl) will - be `int', and DECL_INCOMING_RTL will point to the lowest-order byte of the - passed `int' value. If the debugger then uses that address to fetch - a `short' or a `char' (on a little-endian machine) the result will be - the correct data, so we allow for such exceptional cases below. - - Note that our goal here is to describe the place where the given formal - parameter lives during most of the function's activation (i.e. between - the end of the prologue and the start of the epilogue). We'll do that - as best as we can. Note however that if the given formal parameter is - modified sometime during the execution of the function, then a stack - backtrace (at debug-time) will show the function as having been - called with the *new* value rather than the value which was - originally passed in. This happens rarely enough that it is not - a major problem, but it *is* a problem, and I'd like to fix it. - - A future version of dwarf2out.c may generate two additional - attributes for any given DW_TAG_formal_parameter DIE which will - describe the "passed type" and the "passed location" for the - given formal parameter in addition to the attributes we now - generate to indicate the "declared type" and the "active - location" for each parameter. This additional set of attributes - could be used by debuggers for stack backtraces. Separately, note - that sometimes DECL_RTL can be NULL and DECL_INCOMING_RTL can be - NULL also. This happens (for example) for inlined-instances of - inline function formal parameters which are never referenced. - This really shouldn't be happening. All PARM_DECL nodes should - get valid non-NULL DECL_INCOMING_RTL values, but integrate.c - doesn't currently generate these values for inlined instances of - inline function parameters, so when we see such cases, we are - just out-of-luck for the time being (until integrate.c - gets fixed). */ - - /* Use DECL_RTL as the "location" unless we find something better. */ - rtl = DECL_RTL (decl); - - if (TREE_CODE (decl) == PARM_DECL) - { - if (rtl == NULL_RTX || is_pseudo_reg (rtl)) - { - declared_type = type_main_variant (TREE_TYPE (decl)); - passed_type = type_main_variant (DECL_ARG_TYPE (decl)); - - /* This decl represents a formal parameter which was optimized out. - Note that DECL_INCOMING_RTL may be NULL in here, but we handle - all* cases where (rtl == NULL_RTX) just below. */ - if (declared_type == passed_type) - rtl = DECL_INCOMING_RTL (decl); - else if (! BYTES_BIG_ENDIAN - && TREE_CODE (declared_type) == INTEGER_TYPE - && TYPE_SIZE (declared_type) <= TYPE_SIZE (passed_type)) - rtl = DECL_INCOMING_RTL (decl); - } - } - - if (rtl == NULL_RTX) - return; - - rtl = eliminate_regs (rtl, 0, NULL_RTX); -#ifdef LEAF_REG_REMAP - if (leaf_function) - leaf_renumber_regs_insn (rtl); -#endif - - switch (GET_CODE (rtl)) - { - case ADDRESSOF: - /* The address of a variable that was optimized away; don't emit - anything. */ - break; - - case CONST_INT: - case CONST_DOUBLE: - case CONST_STRING: - case SYMBOL_REF: - case LABEL_REF: - case CONST: - case PLUS: - /* DECL_RTL could be (plus (reg ...) (const_int ...)) */ - add_const_value_attribute (die, rtl); - break; - - case MEM: - case REG: - case SUBREG: - case CONCAT: - add_AT_location_description (die, DW_AT_location, rtl); - break; - - default: - abort (); - } -} - -/* Generate an DW_AT_name attribute given some string value to be included as - the value of the attribute. */ - -static inline void -add_name_attribute (die, name_string) - register dw_die_ref die; - register char *name_string; -{ - if (name_string != NULL && *name_string != 0) - add_AT_string (die, DW_AT_name, name_string); -} - -/* Given a tree node describing an array bound (either lower or upper) output - a representation for that bound. */ - -static void -add_bound_info (subrange_die, bound_attr, bound) - register dw_die_ref subrange_die; - register enum dwarf_attribute bound_attr; - register tree bound; -{ - register unsigned bound_value = 0; - - /* If this is an Ada unconstrained array type, then don't emit any debug - info because the array bounds are unknown. They are parameterized when - the type is instantiated. */ - if (contains_placeholder_p (bound)) - return; - - switch (TREE_CODE (bound)) - { - case ERROR_MARK: - return; - - /* All fixed-bounds are represented by INTEGER_CST nodes. */ - case INTEGER_CST: - bound_value = TREE_INT_CST_LOW (bound); - if (bound_attr == DW_AT_lower_bound - && ((is_c_family () && bound_value == 0) - || (is_fortran () && bound_value == 1))) - /* use the default */; - else - add_AT_unsigned (subrange_die, bound_attr, bound_value); - break; - - case CONVERT_EXPR: - case NOP_EXPR: - case NON_LVALUE_EXPR: - add_bound_info (subrange_die, bound_attr, TREE_OPERAND (bound, 0)); - break; - - case SAVE_EXPR: - /* If optimization is turned on, the SAVE_EXPRs that describe how to - access the upper bound values may be bogus. If they refer to a - register, they may only describe how to get at these values at the - points in the generated code right after they have just been - computed. Worse yet, in the typical case, the upper bound values - will not even *be* computed in the optimized code (though the - number of elements will), so these SAVE_EXPRs are entirely - bogus. In order to compensate for this fact, we check here to see - if optimization is enabled, and if so, we don't add an attribute - for the (unknown and unknowable) upper bound. This should not - cause too much trouble for existing (stupid?) debuggers because - they have to deal with empty upper bounds location descriptions - anyway in order to be able to deal with incomplete array types. - Of course an intelligent debugger (GDB?) should be able to - comprehend that a missing upper bound specification in a array - type used for a storage class `auto' local array variable - indicates that the upper bound is both unknown (at compile- time) - and unknowable (at run-time) due to optimization. - - We assume that a MEM rtx is safe because gcc wouldn't put the - value there unless it was going to be used repeatedly in the - function, i.e. for cleanups. */ - if (! optimize || GET_CODE (SAVE_EXPR_RTL (bound)) == MEM) - { - register dw_die_ref ctx = lookup_decl_die (current_function_decl); - register dw_die_ref decl_die = new_die (DW_TAG_variable, ctx); - register rtx loc = SAVE_EXPR_RTL (bound); - - /* If the RTL for the SAVE_EXPR is memory, handle the case where - it references an outer function's frame. */ - - if (GET_CODE (loc) == MEM) - { - rtx new_addr = fix_lexical_addr (XEXP (loc, 0), bound); - - if (XEXP (loc, 0) != new_addr) - loc = gen_rtx (MEM, GET_MODE (loc), new_addr); - } - - add_AT_flag (decl_die, DW_AT_artificial, 1); - add_type_attribute (decl_die, TREE_TYPE (bound), 1, 0, ctx); - add_AT_location_description (decl_die, DW_AT_location, loc); - add_AT_die_ref (subrange_die, bound_attr, decl_die); - } - - /* Else leave out the attribute. */ - break; - - case MAX_EXPR: - case VAR_DECL: - case COMPONENT_REF: - /* ??? These types of bounds can be created by the Ada front end, - and it isn't clear how to emit debug info for them. */ - break; - - default: - abort (); - } -} - -/* Note that the block of subscript information for an array type also - includes information about the element type of type given array type. */ - -static void -add_subscript_info (type_die, type) - register dw_die_ref type_die; - register tree type; -{ -#ifndef MIPS_DEBUGGING_INFO - register unsigned dimension_number; -#endif - register tree lower, upper; - register dw_die_ref subrange_die; - - /* The GNU compilers represent multidimensional array types as sequences of - one dimensional array types whose element types are themselves array - types. Here we squish that down, so that each multidimensional array - type gets only one array_type DIE in the Dwarf debugging info. The draft - Dwarf specification say that we are allowed to do this kind of - compression in C (because there is no difference between an array or - arrays and a multidimensional array in C) but for other source languages - (e.g. Ada) we probably shouldn't do this. */ - - /* ??? The SGI dwarf reader fails for multidimensional arrays with a - const enum type. E.g. const enum machine_mode insn_operand_mode[2][10]. - We work around this by disabling this feature. See also - gen_array_type_die. */ -#ifndef MIPS_DEBUGGING_INFO - for (dimension_number = 0; - TREE_CODE (type) == ARRAY_TYPE; - type = TREE_TYPE (type), dimension_number++) - { -#endif - register tree domain = TYPE_DOMAIN (type); - - /* Arrays come in three flavors: Unspecified bounds, fixed bounds, - and (in GNU C only) variable bounds. Handle all three forms - here. */ - subrange_die = new_die (DW_TAG_subrange_type, type_die); - if (domain) - { - /* We have an array type with specified bounds. */ - lower = TYPE_MIN_VALUE (domain); - upper = TYPE_MAX_VALUE (domain); - - /* define the index type. */ - if (TREE_TYPE (domain)) - { - /* ??? This is probably an Ada unnamed subrange type. Ignore the - TREE_TYPE field. We can't emit debug info for this - because it is an unnamed integral type. */ - if (TREE_CODE (domain) == INTEGER_TYPE - && TYPE_NAME (domain) == NULL_TREE - && TREE_CODE (TREE_TYPE (domain)) == INTEGER_TYPE - && TYPE_NAME (TREE_TYPE (domain)) == NULL_TREE) - ; - else - add_type_attribute (subrange_die, TREE_TYPE (domain), 0, 0, - type_die); - } - - /* ??? If upper is NULL, the array has unspecified length, - but it does have a lower bound. This happens with Fortran - dimension arr(N:*) - Since the debugger is definitely going to need to know N - to produce useful results, go ahead and output the lower - bound solo, and hope the debugger can cope. */ - - add_bound_info (subrange_die, DW_AT_lower_bound, lower); - if (upper) - add_bound_info (subrange_die, DW_AT_upper_bound, upper); - } - else - /* We have an array type with an unspecified length. The DWARF-2 - spec does not say how to handle this; let's just leave out the - bounds. */ - {;} - - -#ifndef MIPS_DEBUGGING_INFO - } -#endif -} - -static void -add_byte_size_attribute (die, tree_node) - dw_die_ref die; - register tree tree_node; -{ - register unsigned size; - - switch (TREE_CODE (tree_node)) - { - case ERROR_MARK: - size = 0; - break; - case ENUMERAL_TYPE: - case RECORD_TYPE: - case UNION_TYPE: - case QUAL_UNION_TYPE: - size = int_size_in_bytes (tree_node); - break; - case FIELD_DECL: - /* For a data member of a struct or union, the DW_AT_byte_size is - generally given as the number of bytes normally allocated for an - object of the *declared* type of the member itself. This is true - even for bit-fields. */ - size = simple_type_size_in_bits (field_type (tree_node)) / BITS_PER_UNIT; - break; - default: - abort (); - } - - /* Note that `size' might be -1 when we get to this point. If it is, that - indicates that the byte size of the entity in question is variable. We - have no good way of expressing this fact in Dwarf at the present time, - so just let the -1 pass on through. */ - - add_AT_unsigned (die, DW_AT_byte_size, size); -} - -/* For a FIELD_DECL node which represents a bit-field, output an attribute - which specifies the distance in bits from the highest order bit of the - "containing object" for the bit-field to the highest order bit of the - bit-field itself. - - For any given bit-field, the "containing object" is a hypothetical - object (of some integral or enum type) within which the given bit-field - lives. The type of this hypothetical "containing object" is always the - same as the declared type of the individual bit-field itself. The - determination of the exact location of the "containing object" for a - bit-field is rather complicated. It's handled by the - `field_byte_offset' function (above). - - Note that it is the size (in bytes) of the hypothetical "containing object" - which will be given in the DW_AT_byte_size attribute for this bit-field. - (See `byte_size_attribute' above). */ - -static inline void -add_bit_offset_attribute (die, decl) - register dw_die_ref die; - register tree decl; -{ - register unsigned object_offset_in_bytes = field_byte_offset (decl); - register tree type = DECL_BIT_FIELD_TYPE (decl); - register tree bitpos_tree = DECL_FIELD_BITPOS (decl); - register unsigned bitpos_int; - register unsigned highest_order_object_bit_offset; - register unsigned highest_order_field_bit_offset; - register unsigned bit_offset; - - /* Must be a field and a bit field. */ - if (!type - || TREE_CODE (decl) != FIELD_DECL) - abort (); - - /* We can't yet handle bit-fields whose offsets are variable, so if we - encounter such things, just return without generating any attribute - whatsoever. */ - if (TREE_CODE (bitpos_tree) != INTEGER_CST) - return; - - bitpos_int = (unsigned) TREE_INT_CST_LOW (bitpos_tree); - - /* Note that the bit offset is always the distance (in bits) from the - highest-order bit of the "containing object" to the highest-order bit of - the bit-field itself. Since the "high-order end" of any object or field - is different on big-endian and little-endian machines, the computation - below must take account of these differences. */ - highest_order_object_bit_offset = object_offset_in_bytes * BITS_PER_UNIT; - highest_order_field_bit_offset = bitpos_int; - - if (! BYTES_BIG_ENDIAN) - { - highest_order_field_bit_offset - += (unsigned) TREE_INT_CST_LOW (DECL_SIZE (decl)); - - highest_order_object_bit_offset += simple_type_size_in_bits (type); - } - - bit_offset - = (! BYTES_BIG_ENDIAN - ? highest_order_object_bit_offset - highest_order_field_bit_offset - : highest_order_field_bit_offset - highest_order_object_bit_offset); - - add_AT_unsigned (die, DW_AT_bit_offset, bit_offset); -} - -/* For a FIELD_DECL node which represents a bit field, output an attribute - which specifies the length in bits of the given field. */ - -static inline void -add_bit_size_attribute (die, decl) - register dw_die_ref die; - register tree decl; -{ - /* Must be a field and a bit field. */ - if (TREE_CODE (decl) != FIELD_DECL - || ! DECL_BIT_FIELD_TYPE (decl)) - abort (); - add_AT_unsigned (die, DW_AT_bit_size, - (unsigned) TREE_INT_CST_LOW (DECL_SIZE (decl))); -} - -/* If the compiled language is ANSI C, then add a 'prototyped' - attribute, if arg types are given for the parameters of a function. */ - -static inline void -add_prototyped_attribute (die, func_type) - register dw_die_ref die; - register tree func_type; -{ - if (get_AT_unsigned (comp_unit_die, DW_AT_language) == DW_LANG_C89 - && TYPE_ARG_TYPES (func_type) != NULL) - add_AT_flag (die, DW_AT_prototyped, 1); -} - - -/* Add an 'abstract_origin' attribute below a given DIE. The DIE is found - by looking in either the type declaration or object declaration - equate table. */ - -static inline void -add_abstract_origin_attribute (die, origin) - register dw_die_ref die; - register tree origin; -{ - dw_die_ref origin_die = NULL; - if (TREE_CODE_CLASS (TREE_CODE (origin)) == 'd') - origin_die = lookup_decl_die (origin); - else if (TREE_CODE_CLASS (TREE_CODE (origin)) == 't') - origin_die = lookup_type_die (origin); - - add_AT_die_ref (die, DW_AT_abstract_origin, origin_die); -} - -/* We do not currently support the pure_virtual attribute. */ - -static inline void -add_pure_or_virtual_attribute (die, func_decl) - register dw_die_ref die; - register tree func_decl; -{ - if (DECL_VINDEX (func_decl)) - { - add_AT_unsigned (die, DW_AT_virtuality, DW_VIRTUALITY_virtual); - add_AT_loc (die, DW_AT_vtable_elem_location, - new_loc_descr (DW_OP_constu, - TREE_INT_CST_LOW (DECL_VINDEX (func_decl)), - 0)); - - /* GNU extension: Record what type this method came from originally. */ - if (debug_info_level > DINFO_LEVEL_TERSE) - add_AT_die_ref (die, DW_AT_containing_type, - lookup_type_die (DECL_CONTEXT (func_decl))); - } -} - -/* Add source coordinate attributes for the given decl. */ - -static void -add_src_coords_attributes (die, decl) - register dw_die_ref die; - register tree decl; -{ - register unsigned file_index = lookup_filename (DECL_SOURCE_FILE (decl)); - - add_AT_unsigned (die, DW_AT_decl_file, file_index); - add_AT_unsigned (die, DW_AT_decl_line, DECL_SOURCE_LINE (decl)); -} - -/* Add an DW_AT_name attribute and source coordinate attribute for the - given decl, but only if it actually has a name. */ - -static void -add_name_and_src_coords_attributes (die, decl) - register dw_die_ref die; - register tree decl; -{ - register tree decl_name; - - decl_name = DECL_NAME (decl); - if (decl_name != NULL && IDENTIFIER_POINTER (decl_name) != NULL) - { - add_name_attribute (die, dwarf2_name (decl, 0)); - add_src_coords_attributes (die, decl); - if ((TREE_CODE (decl) == FUNCTION_DECL || TREE_CODE (decl) == VAR_DECL) - && DECL_ASSEMBLER_NAME (decl) != DECL_NAME (decl)) - add_AT_string (die, DW_AT_MIPS_linkage_name, - IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))); - } -} - -/* Push a new declaration scope. */ - -static void -push_decl_scope (scope) - tree scope; -{ - tree containing_scope; - int i; - - /* Make room in the decl_scope_table, if necessary. */ - if (decl_scope_table_allocated == decl_scope_depth) - { - decl_scope_table_allocated += DECL_SCOPE_TABLE_INCREMENT; - decl_scope_table - = (decl_scope_node *) xrealloc (decl_scope_table, - (decl_scope_table_allocated - * sizeof (decl_scope_node))); - } - - decl_scope_table[decl_scope_depth].scope = scope; - - /* Sometimes, while recursively emitting subtypes within a class type, - we end up recuring on a subtype at a higher level then the current - subtype. In such a case, we need to search the decl_scope_table to - find the parent of this subtype. */ - - if (AGGREGATE_TYPE_P (scope)) - containing_scope = TYPE_CONTEXT (scope); - else - containing_scope = NULL_TREE; - - /* The normal case. */ - if (decl_scope_depth == 0 - || containing_scope == NULL_TREE - /* Ignore namespaces for the moment. */ - || TREE_CODE (containing_scope) == NAMESPACE_DECL - || containing_scope == decl_scope_table[decl_scope_depth - 1].scope) - decl_scope_table[decl_scope_depth].previous = decl_scope_depth - 1; - else - { - /* We need to search for the containing_scope. */ - for (i = 0; i < decl_scope_depth; i++) - if (decl_scope_table[i].scope == containing_scope) - break; - - if (i == decl_scope_depth) - abort (); - else - decl_scope_table[decl_scope_depth].previous = i; - } - - decl_scope_depth++; -} - -/* Return the DIE for the scope that immediately contains this declaration. */ - -static dw_die_ref -scope_die_for (t, context_die) - register tree t; - register dw_die_ref context_die; -{ - register dw_die_ref scope_die = NULL; - register tree containing_scope; - register int i; - - /* Walk back up the declaration tree looking for a place to define - this type. */ - if (TREE_CODE_CLASS (TREE_CODE (t)) == 't') - containing_scope = TYPE_CONTEXT (t); - else if (TREE_CODE (t) == FUNCTION_DECL && DECL_VINDEX (t)) - containing_scope = decl_class_context (t); - else - containing_scope = DECL_CONTEXT (t); - - /* Ignore namespaces for the moment. */ - if (containing_scope && TREE_CODE (containing_scope) == NAMESPACE_DECL) - containing_scope = NULL_TREE; - - /* Ignore function type "scopes" from the C frontend. They mean that - a tagged type is local to a parmlist of a function declarator, but - that isn't useful to DWARF. */ - if (containing_scope && TREE_CODE (containing_scope) == FUNCTION_TYPE) - containing_scope = NULL_TREE; - - /* Function-local tags and functions get stuck in limbo until they are - fixed up by decls_for_scope. */ - if (context_die == NULL && containing_scope != NULL_TREE - && (TREE_CODE (t) == FUNCTION_DECL || is_tagged_type (t))) - return NULL; - - if (containing_scope == NULL_TREE) - scope_die = comp_unit_die; - else - { - for (i = decl_scope_depth - 1, scope_die = context_die; - i >= 0 && decl_scope_table[i].scope != containing_scope; - (scope_die = scope_die->die_parent, - i = decl_scope_table[i].previous)) - ; - - /* ??? Integrate_decl_tree does not handle BLOCK_TYPE_TAGS, nor - does it try to handle types defined by TYPE_DECLs. Such types - thus have an incorrect TYPE_CONTEXT, which points to the block - they were originally defined in, instead of the current block - created by function inlining. We try to detect that here and - work around it. */ - - if (i < 0 && scope_die == comp_unit_die - && TREE_CODE (containing_scope) == BLOCK - && is_tagged_type (t) - && (block_ultimate_origin (decl_scope_table[decl_scope_depth - 1].scope) - == containing_scope)) - { - scope_die = context_die; - /* Since the checks below are no longer applicable. */ - i = 0; - } - - if (i < 0) - { - if (scope_die != comp_unit_die - || TREE_CODE_CLASS (TREE_CODE (containing_scope)) != 't') - abort (); - if (debug_info_level > DINFO_LEVEL_TERSE - && !TREE_ASM_WRITTEN (containing_scope)) - abort (); - } - } - - return scope_die; -} - -/* Pop a declaration scope. */ -static inline void -pop_decl_scope () -{ - if (decl_scope_depth <= 0) - abort (); - --decl_scope_depth; -} - -/* Many forms of DIEs require a "type description" attribute. This - routine locates the proper "type descriptor" die for the type given - by 'type', and adds an DW_AT_type attribute below the given die. */ - -static void -add_type_attribute (object_die, type, decl_const, decl_volatile, context_die) - register dw_die_ref object_die; - register tree type; - register int decl_const; - register int decl_volatile; - register dw_die_ref context_die; -{ - register enum tree_code code = TREE_CODE (type); - register dw_die_ref type_die = NULL; - - /* ??? If this type is an unnamed subrange type of an integral or - floating-point type, use the inner type. This is because we have no - support for unnamed types in base_type_die. This can happen if this is - an Ada subrange type. Correct solution is emit a subrange type die. */ - if ((code == INTEGER_TYPE || code == REAL_TYPE) - && TREE_TYPE (type) != 0 && TYPE_NAME (type) == 0) - type = TREE_TYPE (type), code = TREE_CODE (type); - - if (code == ERROR_MARK) - return; - - /* Handle a special case. For functions whose return type is void, we - generate *no* type attribute. (Note that no object may have type - `void', so this only applies to function return types). */ - if (code == VOID_TYPE) - return; - - type_die = modified_type_die (type, - decl_const || TYPE_READONLY (type), - decl_volatile || TYPE_VOLATILE (type), - context_die); - if (type_die != NULL) - add_AT_die_ref (object_die, DW_AT_type, type_die); -} - -/* Given a tree pointer to a struct, class, union, or enum type node, return - a pointer to the (string) tag name for the given type, or zero if the type - was declared without a tag. */ - -static char * -type_tag (type) - register tree type; -{ - register char *name = 0; - - if (TYPE_NAME (type) != 0) - { - register tree t = 0; - - /* Find the IDENTIFIER_NODE for the type name. */ - if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE) - t = TYPE_NAME (type); - - /* The g++ front end makes the TYPE_NAME of *each* tagged type point to - a TYPE_DECL node, regardless of whether or not a `typedef' was - involved. */ - else if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL - && ! DECL_IGNORED_P (TYPE_NAME (type))) - t = DECL_NAME (TYPE_NAME (type)); - - /* Now get the name as a string, or invent one. */ - if (t != 0) - name = IDENTIFIER_POINTER (t); - } - - return (name == 0 || *name == '\0') ? 0 : name; -} - -/* Return the type associated with a data member, make a special check - for bit field types. */ - -static inline tree -member_declared_type (member) - register tree member; -{ - return (DECL_BIT_FIELD_TYPE (member) - ? DECL_BIT_FIELD_TYPE (member) - : TREE_TYPE (member)); -} - -/* Get the decl's label, as described by its RTL. This may be different - from the DECL_NAME name used in the source file. */ - -#if 0 -static char * -decl_start_label (decl) - register tree decl; -{ - rtx x; - char *fnname; - x = DECL_RTL (decl); - if (GET_CODE (x) != MEM) - abort (); - - x = XEXP (x, 0); - if (GET_CODE (x) != SYMBOL_REF) - abort (); - - fnname = XSTR (x, 0); - return fnname; -} -#endif - -/* These routines generate the internal representation of the DIE's for - the compilation unit. Debugging information is collected by walking - the declaration trees passed in from dwarf2out_decl(). */ - -static void -gen_array_type_die (type, context_die) - register tree type; - register dw_die_ref context_die; -{ - register dw_die_ref scope_die = scope_die_for (type, context_die); - register dw_die_ref array_die; - register tree element_type; - - /* ??? The SGI dwarf reader fails for array of array of enum types unless - the inner array type comes before the outer array type. Thus we must - call gen_type_die before we call new_die. See below also. */ -#ifdef MIPS_DEBUGGING_INFO - gen_type_die (TREE_TYPE (type), context_die); -#endif - - array_die = new_die (DW_TAG_array_type, scope_die); - -#if 0 - /* We default the array ordering. SDB will probably do - the right things even if DW_AT_ordering is not present. It's not even - an issue until we start to get into multidimensional arrays anyway. If - SDB is ever caught doing the Wrong Thing for multi-dimensional arrays, - then we'll have to put the DW_AT_ordering attribute back in. (But if - and when we find out that we need to put these in, we will only do so - for multidimensional arrays. */ - add_AT_unsigned (array_die, DW_AT_ordering, DW_ORD_row_major); -#endif - -#ifdef MIPS_DEBUGGING_INFO - /* The SGI compilers handle arrays of unknown bound by setting - AT_declaration and not emitting any subrange DIEs. */ - if (! TYPE_DOMAIN (type)) - add_AT_unsigned (array_die, DW_AT_declaration, 1); - else -#endif - add_subscript_info (array_die, type); - - equate_type_number_to_die (type, array_die); - - /* Add representation of the type of the elements of this array type. */ - element_type = TREE_TYPE (type); - - /* ??? The SGI dwarf reader fails for multidimensional arrays with a - const enum type. E.g. const enum machine_mode insn_operand_mode[2][10]. - We work around this by disabling this feature. See also - add_subscript_info. */ -#ifndef MIPS_DEBUGGING_INFO - while (TREE_CODE (element_type) == ARRAY_TYPE) - element_type = TREE_TYPE (element_type); - - gen_type_die (element_type, context_die); -#endif - - add_type_attribute (array_die, element_type, 0, 0, context_die); -} - -static void -gen_set_type_die (type, context_die) - register tree type; - register dw_die_ref context_die; -{ - register dw_die_ref type_die - = new_die (DW_TAG_set_type, scope_die_for (type, context_die)); - - equate_type_number_to_die (type, type_die); - add_type_attribute (type_die, TREE_TYPE (type), 0, 0, context_die); -} - -#if 0 -static void -gen_entry_point_die (decl, context_die) - register tree decl; - register dw_die_ref context_die; -{ - register tree origin = decl_ultimate_origin (decl); - register dw_die_ref decl_die = new_die (DW_TAG_entry_point, context_die); - if (origin != NULL) - add_abstract_origin_attribute (decl_die, origin); - else - { - add_name_and_src_coords_attributes (decl_die, decl); - add_type_attribute (decl_die, TREE_TYPE (TREE_TYPE (decl)), - 0, 0, context_die); - } - - if (DECL_ABSTRACT (decl)) - equate_decl_number_to_die (decl, decl_die); - else - add_AT_lbl_id (decl_die, DW_AT_low_pc, decl_start_label (decl)); -} -#endif - -/* Remember a type in the pending_types_list. */ - -static void -pend_type (type) - register tree type; -{ - if (pending_types == pending_types_allocated) - { - pending_types_allocated += PENDING_TYPES_INCREMENT; - pending_types_list - = (tree *) xrealloc (pending_types_list, - sizeof (tree) * pending_types_allocated); - } - - pending_types_list[pending_types++] = type; -} - -/* Output any pending types (from the pending_types list) which we can output - now (taking into account the scope that we are working on now). - - For each type output, remove the given type from the pending_types_list - *before* we try to output it. */ - -static void -output_pending_types_for_scope (context_die) - register dw_die_ref context_die; -{ - register tree type; - - while (pending_types) - { - --pending_types; - type = pending_types_list[pending_types]; - gen_type_die (type, context_die); - if (!TREE_ASM_WRITTEN (type)) - abort (); - } -} - -/* Generate a DIE to represent an inlined instance of an enumeration type. */ - -static void -gen_inlined_enumeration_type_die (type, context_die) - register tree type; - register dw_die_ref context_die; -{ - register dw_die_ref type_die = new_die (DW_TAG_enumeration_type, - scope_die_for (type, context_die)); - - if (!TREE_ASM_WRITTEN (type)) - abort (); - add_abstract_origin_attribute (type_die, type); -} - -/* Generate a DIE to represent an inlined instance of a structure type. */ - -static void -gen_inlined_structure_type_die (type, context_die) - register tree type; - register dw_die_ref context_die; -{ - register dw_die_ref type_die = new_die (DW_TAG_structure_type, - scope_die_for (type, context_die)); - - if (!TREE_ASM_WRITTEN (type)) - abort (); - add_abstract_origin_attribute (type_die, type); -} - -/* Generate a DIE to represent an inlined instance of a union type. */ - -static void -gen_inlined_union_type_die (type, context_die) - register tree type; - register dw_die_ref context_die; -{ - register dw_die_ref type_die = new_die (DW_TAG_union_type, - scope_die_for (type, context_die)); - - if (!TREE_ASM_WRITTEN (type)) - abort (); - add_abstract_origin_attribute (type_die, type); -} - -/* Generate a DIE to represent an enumeration type. Note that these DIEs - include all of the information about the enumeration values also. Each - enumerated type name/value is listed as a child of the enumerated type - DIE. */ - -static void -gen_enumeration_type_die (type, context_die) - register tree type; - register dw_die_ref context_die; -{ - register dw_die_ref type_die = lookup_type_die (type); - - if (type_die == NULL) - { - type_die = new_die (DW_TAG_enumeration_type, - scope_die_for (type, context_die)); - equate_type_number_to_die (type, type_die); - add_name_attribute (type_die, type_tag (type)); - } - else if (! TYPE_SIZE (type)) - return; - else - remove_AT (type_die, DW_AT_declaration); - - /* Handle a GNU C/C++ extension, i.e. incomplete enum types. If the - given enum type is incomplete, do not generate the DW_AT_byte_size - attribute or the DW_AT_element_list attribute. */ - if (TYPE_SIZE (type)) - { - register tree link; - - TREE_ASM_WRITTEN (type) = 1; - add_byte_size_attribute (type_die, type); - if (TYPE_STUB_DECL (type) != NULL_TREE) - add_src_coords_attributes (type_die, TYPE_STUB_DECL (type)); - - /* If the first reference to this type was as the return type of an - inline function, then it may not have a parent. Fix this now. */ - if (type_die->die_parent == NULL) - add_child_die (scope_die_for (type, context_die), type_die); - - for (link = TYPE_FIELDS (type); - link != NULL; link = TREE_CHAIN (link)) - { - register dw_die_ref enum_die = new_die (DW_TAG_enumerator, type_die); - - add_name_attribute (enum_die, - IDENTIFIER_POINTER (TREE_PURPOSE (link))); - add_AT_unsigned (enum_die, DW_AT_const_value, - (unsigned) TREE_INT_CST_LOW (TREE_VALUE (link))); - } - } - else - add_AT_flag (type_die, DW_AT_declaration, 1); -} - - -/* Generate a DIE to represent either a real live formal parameter decl or to - represent just the type of some formal parameter position in some function - type. - - Note that this routine is a bit unusual because its argument may be a - ..._DECL node (i.e. either a PARM_DECL or perhaps a VAR_DECL which - represents an inlining of some PARM_DECL) or else some sort of a ..._TYPE - node. If it's the former then this function is being called to output a - DIE to represent a formal parameter object (or some inlining thereof). If - it's the latter, then this function is only being called to output a - DW_TAG_formal_parameter DIE to stand as a placeholder for some formal - argument type of some subprogram type. */ - -static dw_die_ref -gen_formal_parameter_die (node, context_die) - register tree node; - register dw_die_ref context_die; -{ - register dw_die_ref parm_die - = new_die (DW_TAG_formal_parameter, context_die); - register tree origin; - - switch (TREE_CODE_CLASS (TREE_CODE (node))) - { - case 'd': - origin = decl_ultimate_origin (node); - if (origin != NULL) - add_abstract_origin_attribute (parm_die, origin); - else - { - add_name_and_src_coords_attributes (parm_die, node); - add_type_attribute (parm_die, TREE_TYPE (node), - TREE_READONLY (node), - TREE_THIS_VOLATILE (node), - context_die); - if (DECL_ARTIFICIAL (node)) - add_AT_flag (parm_die, DW_AT_artificial, 1); - } - - equate_decl_number_to_die (node, parm_die); - if (! DECL_ABSTRACT (node)) - add_location_or_const_value_attribute (parm_die, node); - - break; - - case 't': - /* We were called with some kind of a ..._TYPE node. */ - add_type_attribute (parm_die, node, 0, 0, context_die); - break; - - default: - abort (); - } - - return parm_die; -} - -/* Generate a special type of DIE used as a stand-in for a trailing ellipsis - at the end of an (ANSI prototyped) formal parameters list. */ - -static void -gen_unspecified_parameters_die (decl_or_type, context_die) - register tree decl_or_type; - register dw_die_ref context_die; -{ - new_die (DW_TAG_unspecified_parameters, context_die); -} - -/* Generate a list of nameless DW_TAG_formal_parameter DIEs (and perhaps a - DW_TAG_unspecified_parameters DIE) to represent the types of the formal - parameters as specified in some function type specification (except for - those which appear as part of a function *definition*). - - Note we must be careful here to output all of the parameter DIEs before* - we output any DIEs needed to represent the types of the formal parameters. - This keeps svr4 SDB happy because it (incorrectly) thinks that the first - non-parameter DIE it sees ends the formal parameter list. */ - -static void -gen_formal_types_die (function_or_method_type, context_die) - register tree function_or_method_type; - register dw_die_ref context_die; -{ - register tree link; - register tree formal_type = NULL; - register tree first_parm_type = TYPE_ARG_TYPES (function_or_method_type); - -#if 0 - /* In the case where we are generating a formal types list for a C++ - non-static member function type, skip over the first thing on the - TYPE_ARG_TYPES list because it only represents the type of the hidden - `this pointer'. The debugger should be able to figure out (without - being explicitly told) that this non-static member function type takes a - `this pointer' and should be able to figure what the type of that hidden - parameter is from the DW_AT_member attribute of the parent - DW_TAG_subroutine_type DIE. */ - if (TREE_CODE (function_or_method_type) == METHOD_TYPE) - first_parm_type = TREE_CHAIN (first_parm_type); -#endif - - /* Make our first pass over the list of formal parameter types and output a - DW_TAG_formal_parameter DIE for each one. */ - for (link = first_parm_type; link; link = TREE_CHAIN (link)) - { - register dw_die_ref parm_die; - - formal_type = TREE_VALUE (link); - if (formal_type == void_type_node) - break; - - /* Output a (nameless) DIE to represent the formal parameter itself. */ - parm_die = gen_formal_parameter_die (formal_type, context_die); - if (TREE_CODE (function_or_method_type) == METHOD_TYPE - && link == first_parm_type) - add_AT_flag (parm_die, DW_AT_artificial, 1); - } - - /* If this function type has an ellipsis, add a - DW_TAG_unspecified_parameters DIE to the end of the parameter list. */ - if (formal_type != void_type_node) - gen_unspecified_parameters_die (function_or_method_type, context_die); - - /* Make our second (and final) pass over the list of formal parameter types - and output DIEs to represent those types (as necessary). */ - for (link = TYPE_ARG_TYPES (function_or_method_type); - link; - link = TREE_CHAIN (link)) - { - formal_type = TREE_VALUE (link); - if (formal_type == void_type_node) - break; - - gen_type_die (formal_type, context_die); - } -} - -/* Generate a DIE to represent a declared function (either file-scope or - block-local). */ - -static void -gen_subprogram_die (decl, context_die) - register tree decl; - register dw_die_ref context_die; -{ - char label_id[MAX_ARTIFICIAL_LABEL_BYTES]; - register tree origin = decl_ultimate_origin (decl); - register dw_die_ref subr_die; - register rtx fp_reg; - register tree fn_arg_types; - register tree outer_scope; - register dw_die_ref old_die = lookup_decl_die (decl); - register int declaration - = (current_function_decl != decl - || (context_die - && (context_die->die_tag == DW_TAG_structure_type - || context_die->die_tag == DW_TAG_union_type))); - - if (origin != NULL) - { - subr_die = new_die (DW_TAG_subprogram, context_die); - add_abstract_origin_attribute (subr_die, origin); - } - else if (old_die && DECL_ABSTRACT (decl) - && get_AT_unsigned (old_die, DW_AT_inline)) - { - /* This must be a redefinition of an extern inline function. - We can just reuse the old die here. */ - subr_die = old_die; - - /* Clear out the inlined attribute and parm types. */ - remove_AT (subr_die, DW_AT_inline); - remove_children (subr_die); - } - else if (old_die) - { - register unsigned file_index - = lookup_filename (DECL_SOURCE_FILE (decl)); - - if (get_AT_flag (old_die, DW_AT_declaration) != 1) - { - /* ??? This can happen if there is a bug in the program, for - instance, if it has duplicate function definitions. Ideally, - we should detect this case and ignore it. For now, if we have - already reported an error, any error at all, then assume that - we got here because of a input error, not a dwarf2 bug. */ - extern int errorcount; - if (errorcount) - return; - abort (); - } - - /* If the definition comes from the same place as the declaration, - maybe use the old DIE. We always want the DIE for this function - that has the *_pc attributes to be under comp_unit_die so the - debugger can find it. For inlines, that is the concrete instance, - so we can use the old DIE here. For non-inline methods, we want a - specification DIE at toplevel, so we need a new DIE. For local - class methods, this does not apply. */ - if ((DECL_ABSTRACT (decl) || old_die->die_parent == comp_unit_die - || context_die == NULL) - && get_AT_unsigned (old_die, DW_AT_decl_file) == file_index - && (get_AT_unsigned (old_die, DW_AT_decl_line) - == DECL_SOURCE_LINE (decl))) - { - subr_die = old_die; - - /* Clear out the declaration attribute and the parm types. */ - remove_AT (subr_die, DW_AT_declaration); - remove_children (subr_die); - } - else - { - subr_die = new_die (DW_TAG_subprogram, context_die); - add_AT_die_ref (subr_die, DW_AT_specification, old_die); - if (get_AT_unsigned (old_die, DW_AT_decl_file) != file_index) - add_AT_unsigned (subr_die, DW_AT_decl_file, file_index); - if (get_AT_unsigned (old_die, DW_AT_decl_line) - != DECL_SOURCE_LINE (decl)) - add_AT_unsigned - (subr_die, DW_AT_decl_line, DECL_SOURCE_LINE (decl)); - } - } - else - { - register dw_die_ref scope_die; - - if (DECL_CONTEXT (decl)) - scope_die = scope_die_for (decl, context_die); - else - /* Don't put block extern declarations under comp_unit_die. */ - scope_die = context_die; - - subr_die = new_die (DW_TAG_subprogram, scope_die); - - if (TREE_PUBLIC (decl)) - add_AT_flag (subr_die, DW_AT_external, 1); - - add_name_and_src_coords_attributes (subr_die, decl); - if (debug_info_level > DINFO_LEVEL_TERSE) - { - register tree type = TREE_TYPE (decl); - - add_prototyped_attribute (subr_die, type); - add_type_attribute (subr_die, TREE_TYPE (type), 0, 0, context_die); - } - - add_pure_or_virtual_attribute (subr_die, decl); - if (DECL_ARTIFICIAL (decl)) - add_AT_flag (subr_die, DW_AT_artificial, 1); - if (TREE_PROTECTED (decl)) - add_AT_unsigned (subr_die, DW_AT_accessibility, DW_ACCESS_protected); - else if (TREE_PRIVATE (decl)) - add_AT_unsigned (subr_die, DW_AT_accessibility, DW_ACCESS_private); - } - - if (declaration) - { - add_AT_flag (subr_die, DW_AT_declaration, 1); - - /* The first time we see a member function, it is in the context of - the class to which it belongs. We make sure of this by emitting - the class first. The next time is the definition, which is - handled above. The two may come from the same source text. */ - if (DECL_CONTEXT (decl)) - equate_decl_number_to_die (decl, subr_die); - } - else if (DECL_ABSTRACT (decl)) - { - /* ??? Checking DECL_DEFER_OUTPUT is correct for static inline functions, - but not for extern inline functions. We can't get this completely - correct because information about whether the function was declared - inline is not saved anywhere. */ - if (DECL_DEFER_OUTPUT (decl)) - { - if (DECL_INLINE (decl)) - add_AT_unsigned (subr_die, DW_AT_inline, DW_INL_declared_inlined); - else - add_AT_unsigned (subr_die, DW_AT_inline, - DW_INL_declared_not_inlined); - } - else if (DECL_INLINE (decl)) - add_AT_unsigned (subr_die, DW_AT_inline, DW_INL_inlined); - else - abort (); - - equate_decl_number_to_die (decl, subr_die); - } - else if (!DECL_EXTERNAL (decl)) - { - if (origin == NULL_TREE) - equate_decl_number_to_die (decl, subr_die); - - ASM_GENERATE_INTERNAL_LABEL (label_id, FUNC_BEGIN_LABEL, - current_funcdef_number); - add_AT_lbl_id (subr_die, DW_AT_low_pc, label_id); - ASM_GENERATE_INTERNAL_LABEL (label_id, FUNC_END_LABEL, - current_funcdef_number); - add_AT_lbl_id (subr_die, DW_AT_high_pc, label_id); - - add_pubname (decl, subr_die); - add_arange (decl, subr_die); - -#ifdef MIPS_DEBUGGING_INFO - /* Add a reference to the FDE for this routine. */ - add_AT_fde_ref (subr_die, DW_AT_MIPS_fde, current_funcdef_fde); -#endif - - /* Define the "frame base" location for this routine. We use the - frame pointer or stack pointer registers, since the RTL for local - variables is relative to one of them. */ - fp_reg - = frame_pointer_needed ? hard_frame_pointer_rtx : stack_pointer_rtx; - add_AT_loc (subr_die, DW_AT_frame_base, reg_loc_descriptor (fp_reg)); - -#if 0 - /* ??? This fails for nested inline functions, because context_display - is not part of the state saved/restored for inline functions. */ - if (current_function_needs_context) - add_AT_location_description (subr_die, DW_AT_static_link, - lookup_static_chain (decl)); -#endif - } - - /* Now output descriptions of the arguments for this function. This gets - (unnecessarily?) complex because of the fact that the DECL_ARGUMENT list - for a FUNCTION_DECL doesn't indicate cases where there was a trailing - `...' at the end of the formal parameter list. In order to find out if - there was a trailing ellipsis or not, we must instead look at the type - associated with the FUNCTION_DECL. This will be a node of type - FUNCTION_TYPE. If the chain of type nodes hanging off of this - FUNCTION_TYPE node ends with a void_type_node then there should *not* be - an ellipsis at the end. */ - push_decl_scope (decl); - - /* In the case where we are describing a mere function declaration, all we - need to do here (and all we *can* do here) is to describe the *types* of - its formal parameters. */ - if (debug_info_level <= DINFO_LEVEL_TERSE) - ; - else if (declaration) - gen_formal_types_die (TREE_TYPE (decl), subr_die); - else - { - /* Generate DIEs to represent all known formal parameters */ - register tree arg_decls = DECL_ARGUMENTS (decl); - register tree parm; - - /* When generating DIEs, generate the unspecified_parameters DIE - instead if we come across the arg "__builtin_va_alist" */ - for (parm = arg_decls; parm; parm = TREE_CHAIN (parm)) - if (TREE_CODE (parm) == PARM_DECL) - { - if (DECL_NAME (parm) - && !strcmp (IDENTIFIER_POINTER (DECL_NAME (parm)), - "__builtin_va_alist")) - gen_unspecified_parameters_die (parm, subr_die); - else - gen_decl_die (parm, subr_die); - } - - /* Decide whether we need a unspecified_parameters DIE at the end. - There are 2 more cases to do this for: 1) the ansi ... declaration - - this is detectable when the end of the arg list is not a - void_type_node 2) an unprototyped function declaration (not a - definition). This just means that we have no info about the - parameters at all. */ - fn_arg_types = TYPE_ARG_TYPES (TREE_TYPE (decl)); - if (fn_arg_types != NULL) - { - /* this is the prototyped case, check for ... */ - if (TREE_VALUE (tree_last (fn_arg_types)) != void_type_node) - gen_unspecified_parameters_die (decl, subr_die); - } - else if (DECL_INITIAL (decl) == NULL_TREE) - gen_unspecified_parameters_die (decl, subr_die); - } - - /* Output Dwarf info for all of the stuff within the body of the function - (if it has one - it may be just a declaration). */ - outer_scope = DECL_INITIAL (decl); - - /* Note that here, `outer_scope' is a pointer to the outermost BLOCK - node created to represent a function. This outermost BLOCK actually - represents the outermost binding contour for the function, i.e. the - contour in which the function's formal parameters and labels get - declared. Curiously, it appears that the front end doesn't actually - put the PARM_DECL nodes for the current function onto the BLOCK_VARS - list for this outer scope. (They are strung off of the DECL_ARGUMENTS - list for the function instead.) The BLOCK_VARS list for the - `outer_scope' does provide us with a list of the LABEL_DECL nodes for - the function however, and we output DWARF info for those in - decls_for_scope. Just within the `outer_scope' there will be a BLOCK - node representing the function's outermost pair of curly braces, and - any blocks used for the base and member initializers of a C++ - constructor function. */ - if (! declaration && TREE_CODE (outer_scope) != ERROR_MARK) - { - current_function_has_inlines = 0; - decls_for_scope (outer_scope, subr_die, 0); - -#if 0 && defined (MIPS_DEBUGGING_INFO) - if (current_function_has_inlines) - { - add_AT_flag (subr_die, DW_AT_MIPS_has_inlines, 1); - if (! comp_unit_has_inlines) - { - add_AT_flag (comp_unit_die, DW_AT_MIPS_has_inlines, 1); - comp_unit_has_inlines = 1; - } - } -#endif - } - - pop_decl_scope (); -} - -/* Generate a DIE to represent a declared data object. */ - -static void -gen_variable_die (decl, context_die) - register tree decl; - register dw_die_ref context_die; -{ - register tree origin = decl_ultimate_origin (decl); - register dw_die_ref var_die = new_die (DW_TAG_variable, context_die); - - dw_die_ref old_die = lookup_decl_die (decl); - int declaration - = (DECL_EXTERNAL (decl) - || current_function_decl != decl_function_context (decl) - || context_die->die_tag == DW_TAG_structure_type - || context_die->die_tag == DW_TAG_union_type); - - if (origin != NULL) - add_abstract_origin_attribute (var_die, origin); - /* Loop unrolling can create multiple blocks that refer to the same - static variable, so we must test for the DW_AT_declaration flag. */ - /* ??? Loop unrolling/reorder_blocks should perhaps be rewritten to - copy decls and set the DECL_ABSTRACT flag on them instead of - sharing them. */ - else if (old_die && TREE_STATIC (decl) - && get_AT_flag (old_die, DW_AT_declaration) == 1) - { - /* ??? This is an instantiation of a C++ class level static. */ - add_AT_die_ref (var_die, DW_AT_specification, old_die); - if (DECL_NAME (decl)) - { - register unsigned file_index - = lookup_filename (DECL_SOURCE_FILE (decl)); - - if (get_AT_unsigned (old_die, DW_AT_decl_file) != file_index) - add_AT_unsigned (var_die, DW_AT_decl_file, file_index); - - if (get_AT_unsigned (old_die, DW_AT_decl_line) - != DECL_SOURCE_LINE (decl)) - - add_AT_unsigned (var_die, DW_AT_decl_line, - DECL_SOURCE_LINE (decl)); - } - } - else - { - add_name_and_src_coords_attributes (var_die, decl); - add_type_attribute (var_die, TREE_TYPE (decl), - TREE_READONLY (decl), - TREE_THIS_VOLATILE (decl), context_die); - - if (TREE_PUBLIC (decl)) - add_AT_flag (var_die, DW_AT_external, 1); - - if (DECL_ARTIFICIAL (decl)) - add_AT_flag (var_die, DW_AT_artificial, 1); - - if (TREE_PROTECTED (decl)) - add_AT_unsigned (var_die, DW_AT_accessibility, DW_ACCESS_protected); - - else if (TREE_PRIVATE (decl)) - add_AT_unsigned (var_die, DW_AT_accessibility, DW_ACCESS_private); - } - - if (declaration) - add_AT_flag (var_die, DW_AT_declaration, 1); - - if ((declaration && decl_class_context (decl)) || DECL_ABSTRACT (decl)) - equate_decl_number_to_die (decl, var_die); - - if (! declaration && ! DECL_ABSTRACT (decl)) - { - equate_decl_number_to_die (decl, var_die); - add_location_or_const_value_attribute (var_die, decl); - add_pubname (decl, var_die); - } -} - -/* Generate a DIE to represent a label identifier. */ - -static void -gen_label_die (decl, context_die) - register tree decl; - register dw_die_ref context_die; -{ - register tree origin = decl_ultimate_origin (decl); - register dw_die_ref lbl_die = new_die (DW_TAG_label, context_die); - register rtx insn; - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - char label2[MAX_ARTIFICIAL_LABEL_BYTES]; - - if (origin != NULL) - add_abstract_origin_attribute (lbl_die, origin); - else - add_name_and_src_coords_attributes (lbl_die, decl); - - if (DECL_ABSTRACT (decl)) - equate_decl_number_to_die (decl, lbl_die); - else - { - insn = DECL_RTL (decl); - if (GET_CODE (insn) == CODE_LABEL) - { - /* When optimization is enabled (via -O) some parts of the compiler - (e.g. jump.c and cse.c) may try to delete CODE_LABEL insns which - represent source-level labels which were explicitly declared by - the user. This really shouldn't be happening though, so catch - it if it ever does happen. */ - if (INSN_DELETED_P (insn)) - abort (); - - sprintf (label2, INSN_LABEL_FMT, current_funcdef_number); - ASM_GENERATE_INTERNAL_LABEL (label, label2, - (unsigned) INSN_UID (insn)); - add_AT_lbl_id (lbl_die, DW_AT_low_pc, label); - } - } -} - -/* Generate a DIE for a lexical block. */ - -static void -gen_lexical_block_die (stmt, context_die, depth) - register tree stmt; - register dw_die_ref context_die; - int depth; -{ - register dw_die_ref stmt_die = new_die (DW_TAG_lexical_block, context_die); - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - if (! BLOCK_ABSTRACT (stmt)) - { - ASM_GENERATE_INTERNAL_LABEL (label, BLOCK_BEGIN_LABEL, - next_block_number); - add_AT_lbl_id (stmt_die, DW_AT_low_pc, label); - ASM_GENERATE_INTERNAL_LABEL (label, BLOCK_END_LABEL, next_block_number); - add_AT_lbl_id (stmt_die, DW_AT_high_pc, label); - } - - push_decl_scope (stmt); - decls_for_scope (stmt, stmt_die, depth); - pop_decl_scope (); -} - -/* Generate a DIE for an inlined subprogram. */ - -static void -gen_inlined_subroutine_die (stmt, context_die, depth) - register tree stmt; - register dw_die_ref context_die; - int depth; -{ - if (! BLOCK_ABSTRACT (stmt)) - { - register dw_die_ref subr_die - = new_die (DW_TAG_inlined_subroutine, context_die); - register tree decl = block_ultimate_origin (stmt); - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - add_abstract_origin_attribute (subr_die, decl); - ASM_GENERATE_INTERNAL_LABEL (label, BLOCK_BEGIN_LABEL, - next_block_number); - add_AT_lbl_id (subr_die, DW_AT_low_pc, label); - ASM_GENERATE_INTERNAL_LABEL (label, BLOCK_END_LABEL, next_block_number); - add_AT_lbl_id (subr_die, DW_AT_high_pc, label); - push_decl_scope (decl); - decls_for_scope (stmt, subr_die, depth); - pop_decl_scope (); - current_function_has_inlines = 1; - } -} - -/* Generate a DIE for a field in a record, or structure. */ - -static void -gen_field_die (decl, context_die) - register tree decl; - register dw_die_ref context_die; -{ - register dw_die_ref decl_die = new_die (DW_TAG_member, context_die); - - add_name_and_src_coords_attributes (decl_die, decl); - add_type_attribute (decl_die, member_declared_type (decl), - TREE_READONLY (decl), TREE_THIS_VOLATILE (decl), - context_die); - - /* If this is a bit field... */ - if (DECL_BIT_FIELD_TYPE (decl)) - { - add_byte_size_attribute (decl_die, decl); - add_bit_size_attribute (decl_die, decl); - add_bit_offset_attribute (decl_die, decl); - } - - if (TREE_CODE (DECL_FIELD_CONTEXT (decl)) != UNION_TYPE) - add_data_member_location_attribute (decl_die, decl); - - if (DECL_ARTIFICIAL (decl)) - add_AT_flag (decl_die, DW_AT_artificial, 1); - - if (TREE_PROTECTED (decl)) - add_AT_unsigned (decl_die, DW_AT_accessibility, DW_ACCESS_protected); - - else if (TREE_PRIVATE (decl)) - add_AT_unsigned (decl_die, DW_AT_accessibility, DW_ACCESS_private); -} - -#if 0 -/* Don't generate either pointer_type DIEs or reference_type DIEs here. - Use modified_type_die instead. - We keep this code here just in case these types of DIEs may be needed to - represent certain things in other languages (e.g. Pascal) someday. */ -static void -gen_pointer_type_die (type, context_die) - register tree type; - register dw_die_ref context_die; -{ - register dw_die_ref ptr_die - = new_die (DW_TAG_pointer_type, scope_die_for (type, context_die)); - - equate_type_number_to_die (type, ptr_die); - add_type_attribute (ptr_die, TREE_TYPE (type), 0, 0, context_die); - add_AT_unsigned (mod_type_die, DW_AT_byte_size, PTR_SIZE); -} - -/* Don't generate either pointer_type DIEs or reference_type DIEs here. - Use modified_type_die instead. - We keep this code here just in case these types of DIEs may be needed to - represent certain things in other languages (e.g. Pascal) someday. */ -static void -gen_reference_type_die (type, context_die) - register tree type; - register dw_die_ref context_die; -{ - register dw_die_ref ref_die - = new_die (DW_TAG_reference_type, scope_die_for (type, context_die)); - - equate_type_number_to_die (type, ref_die); - add_type_attribute (ref_die, TREE_TYPE (type), 0, 0, context_die); - add_AT_unsigned (mod_type_die, DW_AT_byte_size, PTR_SIZE); -} -#endif - -/* Generate a DIE for a pointer to a member type. */ -static void -gen_ptr_to_mbr_type_die (type, context_die) - register tree type; - register dw_die_ref context_die; -{ - register dw_die_ref ptr_die - = new_die (DW_TAG_ptr_to_member_type, scope_die_for (type, context_die)); - - equate_type_number_to_die (type, ptr_die); - add_AT_die_ref (ptr_die, DW_AT_containing_type, - lookup_type_die (TYPE_OFFSET_BASETYPE (type))); - add_type_attribute (ptr_die, TREE_TYPE (type), 0, 0, context_die); -} - -/* Generate the DIE for the compilation unit. */ - -static void -gen_compile_unit_die (main_input_filename) - register char *main_input_filename; -{ - char producer[250]; - char *wd = getpwd (); - - comp_unit_die = new_die (DW_TAG_compile_unit, NULL); - add_name_attribute (comp_unit_die, main_input_filename); - - if (wd != NULL) - add_AT_string (comp_unit_die, DW_AT_comp_dir, wd); - - sprintf (producer, "%s %s", language_string, version_string); - -#ifdef MIPS_DEBUGGING_INFO - /* The MIPS/SGI compilers place the 'cc' command line options in the producer - string. The SGI debugger looks for -g, -g1, -g2, or -g3; if they do - not appear in the producer string, the debugger reaches the conclusion - that the object file is stripped and has no debugging information. - To get the MIPS/SGI debugger to believe that there is debugging - information in the object file, we add a -g to the producer string. */ - if (debug_info_level > DINFO_LEVEL_TERSE) - strcat (producer, " -g"); -#endif - - add_AT_string (comp_unit_die, DW_AT_producer, producer); - - if (strcmp (language_string, "GNU C++") == 0) - add_AT_unsigned (comp_unit_die, DW_AT_language, DW_LANG_C_plus_plus); - - else if (strcmp (language_string, "GNU Ada") == 0) - add_AT_unsigned (comp_unit_die, DW_AT_language, DW_LANG_Ada83); - - else if (strcmp (language_string, "GNU F77") == 0) - add_AT_unsigned (comp_unit_die, DW_AT_language, DW_LANG_Fortran77); - - else if (strcmp (language_string, "GNU Pascal") == 0) - add_AT_unsigned (comp_unit_die, DW_AT_language, DW_LANG_Pascal83); - - else if (flag_traditional) - add_AT_unsigned (comp_unit_die, DW_AT_language, DW_LANG_C); - - else - add_AT_unsigned (comp_unit_die, DW_AT_language, DW_LANG_C89); - -#if 0 /* unimplemented */ - if (debug_info_level >= DINFO_LEVEL_VERBOSE) - add_AT_unsigned (comp_unit_die, DW_AT_macro_info, 0); -#endif -} - -/* Generate a DIE for a string type. */ - -static void -gen_string_type_die (type, context_die) - register tree type; - register dw_die_ref context_die; -{ - register dw_die_ref type_die - = new_die (DW_TAG_string_type, scope_die_for (type, context_die)); - - equate_type_number_to_die (type, type_die); - - /* Fudge the string length attribute for now. */ - - /* TODO: add string length info. - string_length_attribute (TYPE_MAX_VALUE (TYPE_DOMAIN (type))); - bound_representation (upper_bound, 0, 'u'); */ -} - -/* Generate the DIE for a base class. */ - -static void -gen_inheritance_die (binfo, context_die) - register tree binfo; - register dw_die_ref context_die; -{ - dw_die_ref die = new_die (DW_TAG_inheritance, context_die); - - add_type_attribute (die, BINFO_TYPE (binfo), 0, 0, context_die); - add_data_member_location_attribute (die, binfo); - - if (TREE_VIA_VIRTUAL (binfo)) - add_AT_unsigned (die, DW_AT_virtuality, DW_VIRTUALITY_virtual); - if (TREE_VIA_PUBLIC (binfo)) - add_AT_unsigned (die, DW_AT_accessibility, DW_ACCESS_public); - else if (TREE_VIA_PROTECTED (binfo)) - add_AT_unsigned (die, DW_AT_accessibility, DW_ACCESS_protected); -} - -/* Generate a DIE for a class member. */ - -static void -gen_member_die (type, context_die) - register tree type; - register dw_die_ref context_die; -{ - register tree member; - - /* If this is not an incomplete type, output descriptions of each of its - members. Note that as we output the DIEs necessary to represent the - members of this record or union type, we will also be trying to output - DIEs to represent the *types* of those members. However the `type' - function (above) will specifically avoid generating type DIEs for member - types *within* the list of member DIEs for this (containing) type execpt - for those types (of members) which are explicitly marked as also being - members of this (containing) type themselves. The g++ front- end can - force any given type to be treated as a member of some other - (containing) type by setting the TYPE_CONTEXT of the given (member) type - to point to the TREE node representing the appropriate (containing) - type. */ - - /* First output info about the base classes. */ - if (TYPE_BINFO (type) && TYPE_BINFO_BASETYPES (type)) - { - register tree bases = TYPE_BINFO_BASETYPES (type); - register int n_bases = TREE_VEC_LENGTH (bases); - register int i; - - for (i = 0; i < n_bases; i++) - gen_inheritance_die (TREE_VEC_ELT (bases, i), context_die); - } - - /* Now output info about the data members and type members. */ - for (member = TYPE_FIELDS (type); member; member = TREE_CHAIN (member)) - gen_decl_die (member, context_die); - - /* Now output info about the function members (if any). */ - for (member = TYPE_METHODS (type); member; member = TREE_CHAIN (member)) - gen_decl_die (member, context_die); -} - -/* Generate a DIE for a structure or union type. */ - -static void -gen_struct_or_union_type_die (type, context_die) - register tree type; - register dw_die_ref context_die; -{ - register dw_die_ref type_die = lookup_type_die (type); - register dw_die_ref scope_die = 0; - register int nested = 0; - - if (type_die && ! TYPE_SIZE (type)) - return; - - if (TYPE_CONTEXT (type) != NULL_TREE - && AGGREGATE_TYPE_P (TYPE_CONTEXT (type))) - nested = 1; - - scope_die = scope_die_for (type, context_die); - - if (! type_die || (nested && scope_die == comp_unit_die)) - /* First occurrence of type or toplevel definition of nested class. */ - { - register dw_die_ref old_die = type_die; - - type_die = new_die (TREE_CODE (type) == RECORD_TYPE - ? DW_TAG_structure_type : DW_TAG_union_type, - scope_die); - equate_type_number_to_die (type, type_die); - add_name_attribute (type_die, type_tag (type)); - if (old_die) - add_AT_die_ref (type_die, DW_AT_specification, old_die); - } - else - remove_AT (type_die, DW_AT_declaration); - - /* If we're not in the right context to be defining this type, defer to - avoid tricky recursion. */ - if (TYPE_SIZE (type) && decl_scope_depth > 0 && scope_die == comp_unit_die) - { - add_AT_flag (type_die, DW_AT_declaration, 1); - pend_type (type); - } - /* If this type has been completed, then give it a byte_size attribute and - then give a list of members. */ - else if (TYPE_SIZE (type)) - { - /* Prevent infinite recursion in cases where the type of some member of - this type is expressed in terms of this type itself. */ - TREE_ASM_WRITTEN (type) = 1; - add_byte_size_attribute (type_die, type); - if (TYPE_STUB_DECL (type) != NULL_TREE) - add_src_coords_attributes (type_die, TYPE_STUB_DECL (type)); - - /* If the first reference to this type was as the return type of an - inline function, then it may not have a parent. Fix this now. */ - if (type_die->die_parent == NULL) - add_child_die (scope_die, type_die); - - push_decl_scope (type); - gen_member_die (type, type_die); - pop_decl_scope (); - - /* GNU extension: Record what type our vtable lives in. */ - if (TYPE_VFIELD (type)) - { - tree vtype = DECL_FCONTEXT (TYPE_VFIELD (type)); - - gen_type_die (vtype, context_die); - add_AT_die_ref (type_die, DW_AT_containing_type, - lookup_type_die (vtype)); - } - } - else - add_AT_flag (type_die, DW_AT_declaration, 1); -} - -/* Generate a DIE for a subroutine _type_. */ - -static void -gen_subroutine_type_die (type, context_die) - register tree type; - register dw_die_ref context_die; -{ - register tree return_type = TREE_TYPE (type); - register dw_die_ref subr_die - = new_die (DW_TAG_subroutine_type, scope_die_for (type, context_die)); - - equate_type_number_to_die (type, subr_die); - add_prototyped_attribute (subr_die, type); - add_type_attribute (subr_die, return_type, 0, 0, context_die); - gen_formal_types_die (type, subr_die); -} - -/* Generate a DIE for a type definition */ - -static void -gen_typedef_die (decl, context_die) - register tree decl; - register dw_die_ref context_die; -{ - register dw_die_ref type_die; - register tree origin; - - if (TREE_ASM_WRITTEN (decl)) - return; - TREE_ASM_WRITTEN (decl) = 1; - - type_die = new_die (DW_TAG_typedef, scope_die_for (decl, context_die)); - origin = decl_ultimate_origin (decl); - if (origin != NULL) - add_abstract_origin_attribute (type_die, origin); - else - { - register tree type; - add_name_and_src_coords_attributes (type_die, decl); - if (DECL_ORIGINAL_TYPE (decl)) - { - type = DECL_ORIGINAL_TYPE (decl); - equate_type_number_to_die (TREE_TYPE (decl), type_die); - } - else - type = TREE_TYPE (decl); - add_type_attribute (type_die, type, TREE_READONLY (decl), - TREE_THIS_VOLATILE (decl), context_die); - } - - if (DECL_ABSTRACT (decl)) - equate_decl_number_to_die (decl, type_die); -} - -/* Generate a type description DIE. */ - -static void -gen_type_die (type, context_die) - register tree type; - register dw_die_ref context_die; -{ - if (type == NULL_TREE || type == error_mark_node) - return; - - /* We are going to output a DIE to represent the unqualified version of - this type (i.e. without any const or volatile qualifiers) so get the - main variant (i.e. the unqualified version) of this type now. */ - type = type_main_variant (type); - - if (TREE_ASM_WRITTEN (type)) - return; - - if (TYPE_NAME (type) && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL - && DECL_ORIGINAL_TYPE (TYPE_NAME (type))) - { - TREE_ASM_WRITTEN (type) = 1; - gen_decl_die (TYPE_NAME (type), context_die); - return; - } - - switch (TREE_CODE (type)) - { - case ERROR_MARK: - break; - - case POINTER_TYPE: - case REFERENCE_TYPE: - /* We must set TREE_ASM_WRITTEN in case this is a recursive type. This - ensures that the gen_type_die recursion will terminate even if the - type is recursive. Recursive types are possible in Ada. */ - /* ??? We could perhaps do this for all types before the switch - statement. */ - TREE_ASM_WRITTEN (type) = 1; - - /* For these types, all that is required is that we output a DIE (or a - set of DIEs) to represent the "basis" type. */ - gen_type_die (TREE_TYPE (type), context_die); - break; - - case OFFSET_TYPE: - /* This code is used for C++ pointer-to-data-member types. - Output a description of the relevant class type. */ - gen_type_die (TYPE_OFFSET_BASETYPE (type), context_die); - - /* Output a description of the type of the object pointed to. */ - gen_type_die (TREE_TYPE (type), context_die); - - /* Now output a DIE to represent this pointer-to-data-member type - itself. */ - gen_ptr_to_mbr_type_die (type, context_die); - break; - - case SET_TYPE: - gen_type_die (TYPE_DOMAIN (type), context_die); - gen_set_type_die (type, context_die); - break; - - case FILE_TYPE: - gen_type_die (TREE_TYPE (type), context_die); - abort (); /* No way to represent these in Dwarf yet! */ - break; - - case FUNCTION_TYPE: - /* Force out return type (in case it wasn't forced out already). */ - gen_type_die (TREE_TYPE (type), context_die); - gen_subroutine_type_die (type, context_die); - break; - - case METHOD_TYPE: - /* Force out return type (in case it wasn't forced out already). */ - gen_type_die (TREE_TYPE (type), context_die); - gen_subroutine_type_die (type, context_die); - break; - - case ARRAY_TYPE: - if (TYPE_STRING_FLAG (type) && TREE_CODE (TREE_TYPE (type)) == CHAR_TYPE) - { - gen_type_die (TREE_TYPE (type), context_die); - gen_string_type_die (type, context_die); - } - else - gen_array_type_die (type, context_die); - break; - - case ENUMERAL_TYPE: - case RECORD_TYPE: - case UNION_TYPE: - case QUAL_UNION_TYPE: - /* If this is a nested type whose containing class hasn't been - written out yet, writing it out will cover this one, too. */ - if (TYPE_CONTEXT (type) - && AGGREGATE_TYPE_P (TYPE_CONTEXT (type)) - && ! TREE_ASM_WRITTEN (TYPE_CONTEXT (type))) - { - gen_type_die (TYPE_CONTEXT (type), context_die); - - if (TREE_ASM_WRITTEN (TYPE_CONTEXT (type))) - return; - - /* If that failed, attach ourselves to the stub. */ - push_decl_scope (TYPE_CONTEXT (type)); - context_die = lookup_type_die (TYPE_CONTEXT (type)); - } - - if (TREE_CODE (type) == ENUMERAL_TYPE) - gen_enumeration_type_die (type, context_die); - else - gen_struct_or_union_type_die (type, context_die); - - if (TYPE_CONTEXT (type) - && AGGREGATE_TYPE_P (TYPE_CONTEXT (type)) - && ! TREE_ASM_WRITTEN (TYPE_CONTEXT (type))) - pop_decl_scope (); - - /* Don't set TREE_ASM_WRITTEN on an incomplete struct; we want to fix - it up if it is ever completed. gen_*_type_die will set it for us - when appropriate. */ - return; - - case VOID_TYPE: - case INTEGER_TYPE: - case REAL_TYPE: - case COMPLEX_TYPE: - case BOOLEAN_TYPE: - case CHAR_TYPE: - /* No DIEs needed for fundamental types. */ - break; - - case LANG_TYPE: - /* No Dwarf representation currently defined. */ - break; - - default: - abort (); - } - - TREE_ASM_WRITTEN (type) = 1; -} - -/* Generate a DIE for a tagged type instantiation. */ - -static void -gen_tagged_type_instantiation_die (type, context_die) - register tree type; - register dw_die_ref context_die; -{ - if (type == NULL_TREE || type == error_mark_node) - return; - - /* We are going to output a DIE to represent the unqualified version of - this type (i.e. without any const or volatile qualifiers) so make sure - that we have the main variant (i.e. the unqualified version) of this - type now. */ - if (type != type_main_variant (type) - || !TREE_ASM_WRITTEN (type)) - abort (); - - switch (TREE_CODE (type)) - { - case ERROR_MARK: - break; - - case ENUMERAL_TYPE: - gen_inlined_enumeration_type_die (type, context_die); - break; - - case RECORD_TYPE: - gen_inlined_structure_type_die (type, context_die); - break; - - case UNION_TYPE: - case QUAL_UNION_TYPE: - gen_inlined_union_type_die (type, context_die); - break; - - default: - abort (); - } -} - -/* Generate a DW_TAG_lexical_block DIE followed by DIEs to represent all of the - things which are local to the given block. */ - -static void -gen_block_die (stmt, context_die, depth) - register tree stmt; - register dw_die_ref context_die; - int depth; -{ - register int must_output_die = 0; - register tree origin; - register tree decl; - register enum tree_code origin_code; - - /* Ignore blocks never really used to make RTL. */ - - if (stmt == NULL_TREE || !TREE_USED (stmt)) - return; - - /* Determine the "ultimate origin" of this block. This block may be an - inlined instance of an inlined instance of inline function, so we have - to trace all of the way back through the origin chain to find out what - sort of node actually served as the original seed for the creation of - the current block. */ - origin = block_ultimate_origin (stmt); - origin_code = (origin != NULL) ? TREE_CODE (origin) : ERROR_MARK; - - /* Determine if we need to output any Dwarf DIEs at all to represent this - block. */ - if (origin_code == FUNCTION_DECL) - /* The outer scopes for inlinings *must* always be represented. We - generate DW_TAG_inlined_subroutine DIEs for them. (See below.) */ - must_output_die = 1; - else - { - /* In the case where the current block represents an inlining of the - "body block" of an inline function, we must *NOT* output any DIE for - this block because we have already output a DIE to represent the - whole inlined function scope and the "body block" of any function - doesn't really represent a different scope according to ANSI C - rules. So we check here to make sure that this block does not - represent a "body block inlining" before trying to set the - `must_output_die' flag. */ - if (! is_body_block (origin ? origin : stmt)) - { - /* Determine if this block directly contains any "significant" - local declarations which we will need to output DIEs for. */ - if (debug_info_level > DINFO_LEVEL_TERSE) - /* We are not in terse mode so *any* local declaration counts - as being a "significant" one. */ - must_output_die = (BLOCK_VARS (stmt) != NULL); - else - /* We are in terse mode, so only local (nested) function - definitions count as "significant" local declarations. */ - for (decl = BLOCK_VARS (stmt); - decl != NULL; decl = TREE_CHAIN (decl)) - if (TREE_CODE (decl) == FUNCTION_DECL - && DECL_INITIAL (decl)) - { - must_output_die = 1; - break; - } - } - } - - /* It would be a waste of space to generate a Dwarf DW_TAG_lexical_block - DIE for any block which contains no significant local declarations at - all. Rather, in such cases we just call `decls_for_scope' so that any - needed Dwarf info for any sub-blocks will get properly generated. Note - that in terse mode, our definition of what constitutes a "significant" - local declaration gets restricted to include only inlined function - instances and local (nested) function definitions. */ - if (must_output_die) - { - if (origin_code == FUNCTION_DECL) - gen_inlined_subroutine_die (stmt, context_die, depth); - else - gen_lexical_block_die (stmt, context_die, depth); - } - else - decls_for_scope (stmt, context_die, depth); -} - -/* Generate all of the decls declared within a given scope and (recursively) - all of its sub-blocks. */ - -static void -decls_for_scope (stmt, context_die, depth) - register tree stmt; - register dw_die_ref context_die; - int depth; -{ - register tree decl; - register tree subblocks; - - /* Ignore blocks never really used to make RTL. */ - if (stmt == NULL_TREE || ! TREE_USED (stmt)) - return; - - if (!BLOCK_ABSTRACT (stmt) && depth > 0) - next_block_number++; - - /* Output the DIEs to represent all of the data objects and typedefs - declared directly within this block but not within any nested - sub-blocks. Also, nested function and tag DIEs have been - generated with a parent of NULL; fix that up now. */ - for (decl = BLOCK_VARS (stmt); - decl != NULL; decl = TREE_CHAIN (decl)) - { - register dw_die_ref die; - - if (TREE_CODE (decl) == FUNCTION_DECL) - die = lookup_decl_die (decl); - else if (TREE_CODE (decl) == TYPE_DECL && TYPE_DECL_IS_STUB (decl)) - die = lookup_type_die (TREE_TYPE (decl)); - else - die = NULL; - - if (die != NULL && die->die_parent == NULL) - add_child_die (context_die, die); - else - gen_decl_die (decl, context_die); - } - - /* Output the DIEs to represent all sub-blocks (and the items declared - therein) of this block. */ - for (subblocks = BLOCK_SUBBLOCKS (stmt); - subblocks != NULL; - subblocks = BLOCK_CHAIN (subblocks)) - gen_block_die (subblocks, context_die, depth + 1); -} - -/* Is this a typedef we can avoid emitting? */ - -static inline int -is_redundant_typedef (decl) - register tree decl; -{ - if (TYPE_DECL_IS_STUB (decl)) - return 1; - - if (DECL_ARTIFICIAL (decl) - && DECL_CONTEXT (decl) - && is_tagged_type (DECL_CONTEXT (decl)) - && TREE_CODE (TYPE_NAME (DECL_CONTEXT (decl))) == TYPE_DECL - && DECL_NAME (decl) == DECL_NAME (TYPE_NAME (DECL_CONTEXT (decl)))) - /* Also ignore the artificial member typedef for the class name. */ - return 1; - - return 0; -} - -/* Generate Dwarf debug information for a decl described by DECL. */ - -static void -gen_decl_die (decl, context_die) - register tree decl; - register dw_die_ref context_die; -{ - register tree origin; - - /* Make a note of the decl node we are going to be working on. We may need - to give the user the source coordinates of where it appeared in case we - notice (later on) that something about it looks screwy. */ - dwarf_last_decl = decl; - - if (TREE_CODE (decl) == ERROR_MARK) - return; - - /* If this ..._DECL node is marked to be ignored, then ignore it. But don't - ignore a function definition, since that would screw up our count of - blocks, and that in turn will completely screw up the labels we will - reference in subsequent DW_AT_low_pc and DW_AT_high_pc attributes (for - subsequent blocks). */ - if (DECL_IGNORED_P (decl) && TREE_CODE (decl) != FUNCTION_DECL) - return; - - switch (TREE_CODE (decl)) - { - case CONST_DECL: - /* The individual enumerators of an enum type get output when we output - the Dwarf representation of the relevant enum type itself. */ - break; - - case FUNCTION_DECL: - /* Don't output any DIEs to represent mere function declarations, - unless they are class members or explicit block externs. */ - if (DECL_INITIAL (decl) == NULL_TREE && DECL_CONTEXT (decl) == NULL_TREE - && (current_function_decl == NULL_TREE || ! DECL_ARTIFICIAL (decl))) - break; - - if (debug_info_level > DINFO_LEVEL_TERSE) - { - /* Before we describe the FUNCTION_DECL itself, make sure that we - have described its return type. */ - gen_type_die (TREE_TYPE (TREE_TYPE (decl)), context_die); - - /* And its containing type. */ - origin = decl_class_context (decl); - if (origin != NULL_TREE) - gen_type_die (origin, context_die); - - /* And its virtual context. */ - if (DECL_VINDEX (decl) != NULL_TREE) - gen_type_die (DECL_CONTEXT (decl), context_die); - } - - /* Now output a DIE to represent the function itself. */ - gen_subprogram_die (decl, context_die); - break; - - case TYPE_DECL: - /* If we are in terse mode, don't generate any DIEs to represent any - actual typedefs. */ - if (debug_info_level <= DINFO_LEVEL_TERSE) - break; - - /* In the special case of a TYPE_DECL node representing the - declaration of some type tag, if the given TYPE_DECL is marked as - having been instantiated from some other (original) TYPE_DECL node - (e.g. one which was generated within the original definition of an - inline function) we have to generate a special (abbreviated) - DW_TAG_structure_type, DW_TAG_union_type, or DW_TAG_enumeration_type - DIE here. */ - if (TYPE_DECL_IS_STUB (decl) && DECL_ABSTRACT_ORIGIN (decl) != NULL_TREE) - { - gen_tagged_type_instantiation_die (TREE_TYPE (decl), context_die); - break; - } - - if (is_redundant_typedef (decl)) - gen_type_die (TREE_TYPE (decl), context_die); - else - /* Output a DIE to represent the typedef itself. */ - gen_typedef_die (decl, context_die); - break; - - case LABEL_DECL: - if (debug_info_level >= DINFO_LEVEL_NORMAL) - gen_label_die (decl, context_die); - break; - - case VAR_DECL: - /* If we are in terse mode, don't generate any DIEs to represent any - variable declarations or definitions. */ - if (debug_info_level <= DINFO_LEVEL_TERSE) - break; - - /* Output any DIEs that are needed to specify the type of this data - object. */ - gen_type_die (TREE_TYPE (decl), context_die); - - /* And its containing type. */ - origin = decl_class_context (decl); - if (origin != NULL_TREE) - gen_type_die (origin, context_die); - - /* Now output the DIE to represent the data object itself. This gets - complicated because of the possibility that the VAR_DECL really - represents an inlined instance of a formal parameter for an inline - function. */ - origin = decl_ultimate_origin (decl); - if (origin != NULL_TREE && TREE_CODE (origin) == PARM_DECL) - gen_formal_parameter_die (decl, context_die); - else - gen_variable_die (decl, context_die); - break; - - case FIELD_DECL: - /* Ignore the nameless fields that are used to skip bits, but - handle C++ anonymous unions. */ - if (DECL_NAME (decl) != NULL_TREE - || TREE_CODE (TREE_TYPE (decl)) == UNION_TYPE) - { - gen_type_die (member_declared_type (decl), context_die); - gen_field_die (decl, context_die); - } - break; - - case PARM_DECL: - gen_type_die (TREE_TYPE (decl), context_die); - gen_formal_parameter_die (decl, context_die); - break; - - default: - abort (); - } -} - -/* Write the debugging output for DECL. */ - -void -dwarf2out_decl (decl) - register tree decl; -{ - register dw_die_ref context_die = comp_unit_die; - - if (TREE_CODE (decl) == ERROR_MARK) - return; - - /* If this ..._DECL node is marked to be ignored, then ignore it. We gotta - hope that the node in question doesn't represent a function definition. - If it does, then totally ignoring it is bound to screw up our count of - blocks, and that in turn will completely screw up the labels we will - reference in subsequent DW_AT_low_pc and DW_AT_high_pc attributes (for - subsequent blocks). (It's too bad that BLOCK nodes don't carry their - own sequence numbers with them!) */ - if (DECL_IGNORED_P (decl)) - { - if (TREE_CODE (decl) == FUNCTION_DECL - && DECL_INITIAL (decl) != NULL) - abort (); - - return; - } - - switch (TREE_CODE (decl)) - { - case FUNCTION_DECL: - /* Ignore this FUNCTION_DECL if it refers to a builtin declaration of a - builtin function. Explicit programmer-supplied declarations of - these same functions should NOT be ignored however. */ - if (DECL_EXTERNAL (decl) && DECL_FUNCTION_CODE (decl)) - return; - - /* What we would really like to do here is to filter out all mere - file-scope declarations of file-scope functions which are never - referenced later within this translation unit (and keep all of ones - that *are* referenced later on) but we aren't clairvoyant, so we have - no idea which functions will be referenced in the future (i.e. later - on within the current translation unit). So here we just ignore all - file-scope function declarations which are not also definitions. If - and when the debugger needs to know something about these functions, - it wil have to hunt around and find the DWARF information associated - with the definition of the function. Note that we can't just check - `DECL_EXTERNAL' to find out which FUNCTION_DECL nodes represent - definitions and which ones represent mere declarations. We have to - check `DECL_INITIAL' instead. That's because the C front-end - supports some weird semantics for "extern inline" function - definitions. These can get inlined within the current translation - unit (an thus, we need to generate DWARF info for their abstract - instances so that the DWARF info for the concrete inlined instances - can have something to refer to) but the compiler never generates any - out-of-lines instances of such things (despite the fact that they - *are* definitions). The important point is that the C front-end - marks these "extern inline" functions as DECL_EXTERNAL, but we need - to generate DWARF for them anyway. Note that the C++ front-end also - plays some similar games for inline function definitions appearing - within include files which also contain - `#pragma interface' pragmas. */ - if (DECL_INITIAL (decl) == NULL_TREE) - return; - - /* If we're a nested function, initially use a parent of NULL; if we're - a plain function, this will be fixed up in decls_for_scope. If - we're a method, it will be ignored, since we already have a DIE. */ - if (decl_function_context (decl)) - context_die = NULL; - - break; - - case VAR_DECL: - /* Ignore this VAR_DECL if it refers to a file-scope extern data object - declaration and if the declaration was never even referenced from - within this entire compilation unit. We suppress these DIEs in - order to save space in the .debug section (by eliminating entries - which are probably useless). Note that we must not suppress - block-local extern declarations (whether used or not) because that - would screw-up the debugger's name lookup mechanism and cause it to - miss things which really ought to be in scope at a given point. */ - if (DECL_EXTERNAL (decl) && !TREE_USED (decl)) - return; - - /* If we are in terse mode, don't generate any DIEs to represent any - variable declarations or definitions. */ - if (debug_info_level <= DINFO_LEVEL_TERSE) - return; - break; - - case TYPE_DECL: - /* Don't bother trying to generate any DIEs to represent any of the - normal built-in types for the language we are compiling. */ - if (DECL_SOURCE_LINE (decl) == 0) - { - /* OK, we need to generate one for `bool' so GDB knows what type - comparisons have. */ - if ((get_AT_unsigned (comp_unit_die, DW_AT_language) - == DW_LANG_C_plus_plus) - && TREE_CODE (TREE_TYPE (decl)) == BOOLEAN_TYPE) - modified_type_die (TREE_TYPE (decl), 0, 0, NULL); - - return; - } - - /* If we are in terse mode, don't generate any DIEs for types. */ - if (debug_info_level <= DINFO_LEVEL_TERSE) - return; - - /* If we're a function-scope tag, initially use a parent of NULL; - this will be fixed up in decls_for_scope. */ - if (decl_function_context (decl)) - context_die = NULL; - - break; - - default: - return; - } - - gen_decl_die (decl, context_die); - output_pending_types_for_scope (comp_unit_die); -} - -/* Output a marker (i.e. a label) for the beginning of the generated code for - a lexical block. */ - -void -dwarf2out_begin_block (blocknum) - register unsigned blocknum; -{ - function_section (current_function_decl); - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, BLOCK_BEGIN_LABEL, blocknum); -} - -/* Output a marker (i.e. a label) for the end of the generated code for a - lexical block. */ - -void -dwarf2out_end_block (blocknum) - register unsigned blocknum; -{ - function_section (current_function_decl); - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, BLOCK_END_LABEL, blocknum); -} - -/* Output a marker (i.e. a label) at a point in the assembly code which - corresponds to a given source level label. */ - -void -dwarf2out_label (insn) - register rtx insn; -{ - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - if (debug_info_level >= DINFO_LEVEL_NORMAL) - { - function_section (current_function_decl); - sprintf (label, INSN_LABEL_FMT, current_funcdef_number); - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, label, - (unsigned) INSN_UID (insn)); - } -} - -/* Lookup a filename (in the list of filenames that we know about here in - dwarf2out.c) and return its "index". The index of each (known) filename is - just a unique number which is associated with only that one filename. - We need such numbers for the sake of generating labels - (in the .debug_sfnames section) and references to those - files numbers (in the .debug_srcinfo and.debug_macinfo sections). - If the filename given as an argument is not found in our current list, - add it to the list and assign it the next available unique index number. - In order to speed up searches, we remember the index of the filename - was looked up last. This handles the majority of all searches. */ - -static unsigned -lookup_filename (file_name) - char *file_name; -{ - static unsigned last_file_lookup_index = 0; - register unsigned i; - - /* Check to see if the file name that was searched on the previous call - matches this file name. If so, return the index. */ - if (last_file_lookup_index != 0) - if (strcmp (file_name, file_table[last_file_lookup_index]) == 0) - return last_file_lookup_index; - - /* Didn't match the previous lookup, search the table */ - for (i = 1; i < file_table_in_use; ++i) - if (strcmp (file_name, file_table[i]) == 0) - { - last_file_lookup_index = i; - return i; - } - - /* Prepare to add a new table entry by making sure there is enough space in - the table to do so. If not, expand the current table. */ - if (file_table_in_use == file_table_allocated) - { - file_table_allocated += FILE_TABLE_INCREMENT; - file_table - = (char **) xrealloc (file_table, - file_table_allocated * sizeof (char *)); - } - - /* Add the new entry to the end of the filename table. */ - file_table[file_table_in_use] = xstrdup (file_name); - last_file_lookup_index = file_table_in_use++; - - return last_file_lookup_index; -} - -/* Output a label to mark the beginning of a source code line entry - and record information relating to this source line, in - 'line_info_table' for later output of the .debug_line section. */ - -void -dwarf2out_line (filename, line) - register char *filename; - register unsigned line; -{ - if (debug_info_level >= DINFO_LEVEL_NORMAL) - { - function_section (current_function_decl); - - if (DECL_SECTION_NAME (current_function_decl)) - { - register dw_separate_line_info_ref line_info; - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, SEPARATE_LINE_CODE_LABEL, - separate_line_info_table_in_use); - fputc ('\n', asm_out_file); - - /* expand the line info table if necessary */ - if (separate_line_info_table_in_use - == separate_line_info_table_allocated) - { - separate_line_info_table_allocated += LINE_INFO_TABLE_INCREMENT; - separate_line_info_table - = (dw_separate_line_info_ref) - xrealloc (separate_line_info_table, - separate_line_info_table_allocated - * sizeof (dw_separate_line_info_entry)); - } - - /* Add the new entry at the end of the line_info_table. */ - line_info - = &separate_line_info_table[separate_line_info_table_in_use++]; - line_info->dw_file_num = lookup_filename (filename); - line_info->dw_line_num = line; - line_info->function = current_funcdef_number; - } - else - { - register dw_line_info_ref line_info; - - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, LINE_CODE_LABEL, - line_info_table_in_use); - fputc ('\n', asm_out_file); - - /* Expand the line info table if necessary. */ - if (line_info_table_in_use == line_info_table_allocated) - { - line_info_table_allocated += LINE_INFO_TABLE_INCREMENT; - line_info_table - = (dw_line_info_ref) - xrealloc (line_info_table, - (line_info_table_allocated - * sizeof (dw_line_info_entry))); - } - - /* Add the new entry at the end of the line_info_table. */ - line_info = &line_info_table[line_info_table_in_use++]; - line_info->dw_file_num = lookup_filename (filename); - line_info->dw_line_num = line; - } - } -} - -/* Record the beginning of a new source file, for later output - of the .debug_macinfo section. At present, unimplemented. */ - -void -dwarf2out_start_source_file (filename) - register char *filename ATTRIBUTE_UNUSED; -{ -} - -/* Record the end of a source file, for later output - of the .debug_macinfo section. At present, unimplemented. */ - -void -dwarf2out_end_source_file () -{ -} - -/* Called from check_newline in c-parse.y. The `buffer' parameter contains - the tail part of the directive line, i.e. the part which is past the - initial whitespace, #, whitespace, directive-name, whitespace part. */ - -void -dwarf2out_define (lineno, buffer) - register unsigned lineno; - register char *buffer; -{ - static int initialized = 0; - if (!initialized) - { - dwarf2out_start_source_file (primary_filename); - initialized = 1; - } -} - -/* Called from check_newline in c-parse.y. The `buffer' parameter contains - the tail part of the directive line, i.e. the part which is past the - initial whitespace, #, whitespace, directive-name, whitespace part. */ - -void -dwarf2out_undef (lineno, buffer) - register unsigned lineno ATTRIBUTE_UNUSED; - register char *buffer ATTRIBUTE_UNUSED; -{ -} - -/* Set up for Dwarf output at the start of compilation. */ - -void -dwarf2out_init (asm_out_file, main_input_filename) - register FILE *asm_out_file; - register char *main_input_filename; -{ - /* Remember the name of the primary input file. */ - primary_filename = main_input_filename; - - /* Allocate the initial hunk of the file_table. */ - file_table = (char **) xmalloc (FILE_TABLE_INCREMENT * sizeof (char *)); - zero_memory ((char *) file_table, FILE_TABLE_INCREMENT * sizeof (char *)); - file_table_allocated = FILE_TABLE_INCREMENT; - - /* Skip the first entry - file numbers begin at 1. */ - file_table_in_use = 1; - - /* Allocate the initial hunk of the decl_die_table. */ - decl_die_table - = (dw_die_ref *) xmalloc (DECL_DIE_TABLE_INCREMENT * sizeof (dw_die_ref)); - zero_memory ((char *) decl_die_table, - DECL_DIE_TABLE_INCREMENT * sizeof (dw_die_ref)); - decl_die_table_allocated = DECL_DIE_TABLE_INCREMENT; - decl_die_table_in_use = 0; - - /* Allocate the initial hunk of the decl_scope_table. */ - decl_scope_table - = (decl_scope_node *) xmalloc (DECL_SCOPE_TABLE_INCREMENT - * sizeof (decl_scope_node)); - zero_memory ((char *) decl_scope_table, - DECL_SCOPE_TABLE_INCREMENT * sizeof (decl_scope_node)); - decl_scope_table_allocated = DECL_SCOPE_TABLE_INCREMENT; - decl_scope_depth = 0; - - /* Allocate the initial hunk of the abbrev_die_table. */ - abbrev_die_table - = (dw_die_ref *) xmalloc (ABBREV_DIE_TABLE_INCREMENT - * sizeof (dw_die_ref)); - zero_memory ((char *) abbrev_die_table, - ABBREV_DIE_TABLE_INCREMENT * sizeof (dw_die_ref)); - abbrev_die_table_allocated = ABBREV_DIE_TABLE_INCREMENT; - /* Zero-th entry is allocated, but unused */ - abbrev_die_table_in_use = 1; - - /* Allocate the initial hunk of the line_info_table. */ - line_info_table - = (dw_line_info_ref) xmalloc (LINE_INFO_TABLE_INCREMENT - * sizeof (dw_line_info_entry)); - zero_memory ((char *) line_info_table, - LINE_INFO_TABLE_INCREMENT * sizeof (dw_line_info_entry)); - line_info_table_allocated = LINE_INFO_TABLE_INCREMENT; - /* Zero-th entry is allocated, but unused */ - line_info_table_in_use = 1; - - /* Generate the initial DIE for the .debug section. Note that the (string) - value given in the DW_AT_name attribute of the DW_TAG_compile_unit DIE - will (typically) be a relative pathname and that this pathname should be - taken as being relative to the directory from which the compiler was - invoked when the given (base) source file was compiled. */ - gen_compile_unit_die (main_input_filename); - - ASM_GENERATE_INTERNAL_LABEL (text_end_label, TEXT_END_LABEL, 0); -} - -/* Output stuff that dwarf requires at the end of every file, - and generate the DWARF-2 debugging info. */ - -void -dwarf2out_finish () -{ - limbo_die_node *node, *next_node; - dw_die_ref die; - dw_attr_ref a; - - /* Traverse the limbo die list, and add parent/child links. The only - dies without parents that should be here are concrete instances of - inline functions, and the comp_unit_die. We can ignore the comp_unit_die. - For concrete instances, we can get the parent die from the abstract - instance. */ - for (node = limbo_die_list; node; node = next_node) - { - next_node = node->next; - die = node->die; - - if (die->die_parent == NULL) - { - a = get_AT (die, DW_AT_abstract_origin); - if (a) - add_child_die (a->dw_attr_val.v.val_die_ref->die_parent, die); - else if (die == comp_unit_die) - ; - else - abort (); - } - free (node); - } - - /* Traverse the DIE tree and add sibling attributes to those DIE's - that have children. */ - add_sibling_attributes (comp_unit_die); - - /* Output a terminator label for the .text section. */ - fputc ('\n', asm_out_file); - ASM_OUTPUT_SECTION (asm_out_file, TEXT_SECTION); - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, TEXT_END_LABEL, 0); - -#if 0 - /* Output a terminator label for the .data section. */ - fputc ('\n', asm_out_file); - ASM_OUTPUT_SECTION (asm_out_file, DATA_SECTION); - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, DATA_END_LABEL, 0); - - /* Output a terminator label for the .bss section. */ - fputc ('\n', asm_out_file); - ASM_OUTPUT_SECTION (asm_out_file, BSS_SECTION); - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, BSS_END_LABEL, 0); -#endif - - /* Output the source line correspondence table. */ - if (line_info_table_in_use > 1 || separate_line_info_table_in_use) - { - fputc ('\n', asm_out_file); - ASM_OUTPUT_SECTION (asm_out_file, DEBUG_LINE_SECTION); - output_line_info (); - - /* We can only use the low/high_pc attributes if all of the code - was in .text. */ - if (separate_line_info_table_in_use == 0) - { - add_AT_lbl_id (comp_unit_die, DW_AT_low_pc, - stripattributes (TEXT_SECTION)); - add_AT_lbl_id (comp_unit_die, DW_AT_high_pc, text_end_label); - } - - add_AT_section_offset (comp_unit_die, DW_AT_stmt_list, DEBUG_LINE_SECTION); - } - - /* Output the abbreviation table. */ - fputc ('\n', asm_out_file); - ASM_OUTPUT_SECTION (asm_out_file, ABBREV_SECTION); - build_abbrev_table (comp_unit_die); - output_abbrev_section (); - - /* Initialize the beginning DIE offset - and calculate sizes/offsets. */ - next_die_offset = DWARF_COMPILE_UNIT_HEADER_SIZE; - calc_die_sizes (comp_unit_die); - - /* Output debugging information. */ - fputc ('\n', asm_out_file); - ASM_OUTPUT_SECTION (asm_out_file, DEBUG_INFO_SECTION); - output_compilation_unit_header (); - output_die (comp_unit_die); - - if (pubname_table_in_use) - { - /* Output public names table. */ - fputc ('\n', asm_out_file); - ASM_OUTPUT_SECTION (asm_out_file, PUBNAMES_SECTION); - output_pubnames (); - } - - if (fde_table_in_use) - { - /* Output the address range information. */ - fputc ('\n', asm_out_file); - ASM_OUTPUT_SECTION (asm_out_file, ARANGES_SECTION); - output_aranges (); - } -} -#endif /* DWARF2_DEBUGGING_INFO */ diff --git a/gcc/function.BAK b/gcc/function.BAK deleted file mode 100755 index 3c99619..0000000 --- a/gcc/function.BAK +++ /dev/null @@ -1,6650 +0,0 @@ -/* Expands front end tree to back end RTL for GNU C-Compiler
- Copyright (C) 1987, 88, 89, 91-98, 1999, 2000 Free Software Foundation, Inc.
-
-This file is part of GNU CC.
-
-GNU CC 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 2, or (at your option)
-any later version.
-
-GNU CC 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 GNU CC; see the file COPYING. If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA. */
-
-
-/* This file handles the generation of rtl code from tree structure
- at the level of the function as a whole.
- It creates the rtl expressions for parameters and auto variables
- and has full responsibility for allocating stack slots.
-
- `expand_function_start' is called at the beginning of a function,
- before the function body is parsed, and `expand_function_end' is
- called after parsing the body.
-
- Call `assign_stack_local' to allocate a stack slot for a local variable.
- This is usually done during the RTL generation for the function body,
- but it can also be done in the reload pass when a pseudo-register does
- not get a hard register.
-
- Call `put_var_into_stack' when you learn, belatedly, that a variable
- previously given a pseudo-register must in fact go in the stack.
- This function changes the DECL_RTL to be a stack slot instead of a reg
- then scans all the RTL instructions so far generated to correct them. */
-
-#include "config.h"
-#include "system.h"
-#include "rtl.h"
-#include "tree.h"
-#include "flags.h"
-#include "except.h"
-#include "function.h"
-#include "insn-flags.h"
-#include "expr.h"
-#include "insn-codes.h"
-#include "regs.h"
-#include "hard-reg-set.h"
-#include "insn-config.h"
-#include "recog.h"
-#include "output.h"
-#include "basic-block.h"
-#include "obstack.h"
-#include "toplev.h"
-
-#if !defined PREFERRED_STACK_BOUNDARY && defined STACK_BOUNDARY
-#define PREFERRED_STACK_BOUNDARY STACK_BOUNDARY
-#endif
-
-#ifndef TRAMPOLINE_ALIGNMENT
-#define TRAMPOLINE_ALIGNMENT FUNCTION_BOUNDARY
-#endif
-
-/* Some systems use __main in a way incompatible with its use in gcc, in these
- cases use the macros NAME__MAIN to give a quoted symbol and SYMBOL__MAIN to
- give the same symbol without quotes for an alternative entry point. You
- must define both, or neither. */
-#ifndef NAME__MAIN
-#define NAME__MAIN "__main"
-#define SYMBOL__MAIN __main
-#endif
-
-/* Round a value to the lowest integer less than it that is a multiple of
- the required alignment. Avoid using division in case the value is
- negative. Assume the alignment is a power of two. */
-#define FLOOR_ROUND(VALUE,ALIGN) ((VALUE) & ~((ALIGN) - 1))
-
-/* Similar, but round to the next highest integer that meets the
- alignment. */
-#define CEIL_ROUND(VALUE,ALIGN) (((VALUE) + (ALIGN) - 1) & ~((ALIGN)- 1))
-
-/* NEED_SEPARATE_AP means that we cannot derive ap from the value of fp
- during rtl generation. If they are different register numbers, this is
- always true. It may also be true if
- FIRST_PARM_OFFSET - STARTING_FRAME_OFFSET is not a constant during rtl
- generation. See fix_lexical_addr for details. */
-
-#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
-#define NEED_SEPARATE_AP
-#endif
-
-/* Number of bytes of args popped by function being compiled on its return.
- Zero if no bytes are to be popped.
- May affect compilation of return insn or of function epilogue. */
-
-int current_function_pops_args;
-
-/* Nonzero if function being compiled needs to be given an address
- where the value should be stored. */
-
-int current_function_returns_struct;
-
-/* Nonzero if function being compiled needs to
- return the address of where it has put a structure value. */
-
-int current_function_returns_pcc_struct;
-
-/* Nonzero if function being compiled needs to be passed a static chain. */
-
-int current_function_needs_context;
-
-/* Nonzero if function being compiled can call setjmp. */
-
-int current_function_calls_setjmp;
-
-/* Nonzero if function being compiled can call longjmp. */
-
-int current_function_calls_longjmp;
-
-/* Nonzero if function being compiled receives nonlocal gotos
- from nested functions. */
-
-int current_function_has_nonlocal_label;
-
-/* Nonzero if function being compiled has nonlocal gotos to parent
- function. */
-
-int current_function_has_nonlocal_goto;
-
-/* Nonzero if this function has a computed goto.
-
- It is computed during find_basic_blocks or during stupid life
- analysis. */
-
-int current_function_has_computed_jump;
-
-/* Nonzero if function being compiled contains nested functions. */
-
-int current_function_contains_functions;
-
-/* Nonzero if function being compiled doesn't modify the stack pointer
- (ignoring the prologue and epilogue). This is only valid after
- life_analysis has run. */
-
-int current_function_sp_is_unchanging;
-
-/* Nonzero if the current function is a thunk (a lightweight function that
- just adjusts one of its arguments and forwards to another function), so
- we should try to cut corners where we can. */
-int current_function_is_thunk;
-
-/* Nonzero if function being compiled can call alloca,
- either as a subroutine or builtin. */
-
-int current_function_calls_alloca;
-
-/* Nonzero if the current function returns a pointer type */
-
-int current_function_returns_pointer;
-
-/* If some insns can be deferred to the delay slots of the epilogue, the
- delay list for them is recorded here. */
-
-rtx current_function_epilogue_delay_list;
-
-/* If function's args have a fixed size, this is that size, in bytes.
- Otherwise, it is -1.
- May affect compilation of return insn or of function epilogue. */
-
-int current_function_args_size;
-
-/* # bytes the prologue should push and pretend that the caller pushed them.
- The prologue must do this, but only if parms can be passed in registers. */
-
-int current_function_pretend_args_size;
-
-/* # of bytes of outgoing arguments. If ACCUMULATE_OUTGOING_ARGS is
- defined, the needed space is pushed by the prologue. */
-
-int current_function_outgoing_args_size;
-
-/* This is the offset from the arg pointer to the place where the first
- anonymous arg can be found, if there is one. */
-
-rtx current_function_arg_offset_rtx;
-
-/* Nonzero if current function uses varargs.h or equivalent.
- Zero for functions that use stdarg.h. */
-
-int current_function_varargs;
-
-/* Nonzero if current function uses stdarg.h or equivalent.
- Zero for functions that use varargs.h. */
-
-int current_function_stdarg;
-
-/* Quantities of various kinds of registers
- used for the current function's args. */
-
-CUMULATIVE_ARGS current_function_args_info;
-
-/* Name of function now being compiled. */
-
-char *current_function_name;
-
-/* If non-zero, an RTL expression for the location at which the current
- function returns its result. If the current function returns its
- result in a register, current_function_return_rtx will always be
- the hard register containing the result. */
-
-rtx current_function_return_rtx;
-
-/* Nonzero if the current function uses the constant pool. */
-
-int current_function_uses_const_pool;
-
-/* Nonzero if the current function uses pic_offset_table_rtx. */
-int current_function_uses_pic_offset_table;
-
-/* The arg pointer hard register, or the pseudo into which it was copied. */
-rtx current_function_internal_arg_pointer;
-
-/* CYGNUS LOCAL -- Branch Prediction */
-/* The current function uses __builtin_expect for branch prediction. */
-int current_function_uses_expect;
-
-/* The current function is currently expanding the first argument to
- __builtin_expect. */
-int current_function_processing_expect;
-/* END CYGNUS LOCAL -- Branch Prediction */
-
-/* Language-specific reason why the current function cannot be made inline. */
-char *current_function_cannot_inline;
-
-/* Nonzero if instrumentation calls for function entry and exit should be
- generated. */
-int current_function_instrument_entry_exit;
-
-/* Nonzero if memory access checking be enabled in the current function. */
-int current_function_check_memory_usage;
-
-/* The FUNCTION_DECL for an inline function currently being expanded. */
-tree inline_function_decl;
-
-/* Number of function calls seen so far in current function. */
-
-int function_call_count;
-
-/* List (chain of TREE_LIST) of LABEL_DECLs for all nonlocal labels
- (labels to which there can be nonlocal gotos from nested functions)
- in this function. */
-
-tree nonlocal_labels;
-
-/* List (chain of EXPR_LIST) of stack slots that hold the current handlers
- for nonlocal gotos. There is one for every nonlocal label in the function;
- this list matches the one in nonlocal_labels.
- Zero when function does not have nonlocal labels. */
-
-rtx nonlocal_goto_handler_slots;
-
-/* RTX for stack slot that holds the stack pointer value to restore
- for a nonlocal goto.
- Zero when function does not have nonlocal labels. */
-
-rtx nonlocal_goto_stack_level;
-
-/* Label that will go on parm cleanup code, if any.
- Jumping to this label runs cleanup code for parameters, if
- such code must be run. Following this code is the logical return label. */
-
-rtx cleanup_label;
-
-/* Label that will go on function epilogue.
- Jumping to this label serves as a "return" instruction
- on machines which require execution of the epilogue on all returns. */
-
-rtx return_label;
-
-/* List (chain of EXPR_LISTs) of pseudo-regs of SAVE_EXPRs.
- So we can mark them all live at the end of the function, if nonopt. */
-rtx save_expr_regs;
-
-/* List (chain of EXPR_LISTs) of all stack slots in this function.
- Made for the sake of unshare_all_rtl. */
-rtx stack_slot_list;
-
-/* Chain of all RTL_EXPRs that have insns in them. */
-tree rtl_expr_chain;
-
-/* Label to jump back to for tail recursion, or 0 if we have
- not yet needed one for this function. */
-rtx tail_recursion_label;
-
-/* Place after which to insert the tail_recursion_label if we need one. */
-rtx tail_recursion_reentry;
-
-/* Location at which to save the argument pointer if it will need to be
- referenced. There are two cases where this is done: if nonlocal gotos
- exist, or if vars stored at an offset from the argument pointer will be
- needed by inner routines. */
-
-rtx arg_pointer_save_area;
-
-/* Offset to end of allocated area of stack frame.
- If stack grows down, this is the address of the last stack slot allocated.
- If stack grows up, this is the address for the next slot. */
-HOST_WIDE_INT frame_offset;
-
-/* List (chain of TREE_LISTs) of static chains for containing functions.
- Each link has a FUNCTION_DECL in the TREE_PURPOSE and a reg rtx
- in an RTL_EXPR in the TREE_VALUE. */
-static tree context_display;
-
-/* List (chain of TREE_LISTs) of trampolines for nested functions.
- The trampoline sets up the static chain and jumps to the function.
- We supply the trampoline's address when the function's address is requested.
-
- Each link has a FUNCTION_DECL in the TREE_PURPOSE and a reg rtx
- in an RTL_EXPR in the TREE_VALUE. */
-static tree trampoline_list;
-
-/* Insn after which register parms and SAVE_EXPRs are born, if nonopt. */
-static rtx parm_birth_insn;
-
-#if 0
-/* Nonzero if a stack slot has been generated whose address is not
- actually valid. It means that the generated rtl must all be scanned
- to detect and correct the invalid addresses where they occur. */
-static int invalid_stack_slot;
-#endif
-
-/* Last insn of those whose job was to put parms into their nominal homes. */
-static rtx last_parm_insn;
-
-/* 1 + last pseudo register number possibly used for loading a copy
- of a parameter of this function. */
-int max_parm_reg;
-
-/* Vector indexed by REGNO, containing location on stack in which
- to put the parm which is nominally in pseudo register REGNO,
- if we discover that that parm must go in the stack. The highest
- element in this vector is one less than MAX_PARM_REG, above. */
-rtx *parm_reg_stack_loc;
-
-/* Nonzero once virtual register instantiation has been done.
- assign_stack_local uses frame_pointer_rtx when this is nonzero. */
-static int virtuals_instantiated;
-
-/* These variables hold pointers to functions to
- save and restore machine-specific data,
- in push_function_context and pop_function_context. */
-void (*save_machine_status) PROTO((struct function *));
-void (*restore_machine_status) PROTO((struct function *));
-
-/* Nonzero if we need to distinguish between the return value of this function
- and the return value of a function called by this function. This helps
- integrate.c */
-
-extern int rtx_equal_function_value_matters;
-extern tree sequence_rtl_expr;
-
-/* In order to evaluate some expressions, such as function calls returning
- structures in memory, we need to temporarily allocate stack locations.
- We record each allocated temporary in the following structure.
-
- Associated with each temporary slot is a nesting level. When we pop up
- one level, all temporaries associated with the previous level are freed.
- Normally, all temporaries are freed after the execution of the statement
- in which they were created. However, if we are inside a ({...}) grouping,
- the result may be in a temporary and hence must be preserved. If the
- result could be in a temporary, we preserve it if we can determine which
- one it is in. If we cannot determine which temporary may contain the
- result, all temporaries are preserved. A temporary is preserved by
- pretending it was allocated at the previous nesting level.
-
- Automatic variables are also assigned temporary slots, at the nesting
- level where they are defined. They are marked a "kept" so that
- free_temp_slots will not free them. */
-
-struct temp_slot
-{
- /* Points to next temporary slot. */
- struct temp_slot *next;
- /* The rtx to used to reference the slot. */
- rtx slot;
- /* The rtx used to represent the address if not the address of the
- slot above. May be an EXPR_LIST if multiple addresses exist. */
- rtx address;
- /* The size, in units, of the slot. */
- HOST_WIDE_INT size;
- /* The value of `sequence_rtl_expr' when this temporary is allocated. */
- tree rtl_expr;
- /* Non-zero if this temporary is currently in use. */
- char in_use;
- /* Non-zero if this temporary has its address taken. */
- char addr_taken;
- /* Nesting level at which this slot is being used. */
- int level;
- /* Non-zero if this should survive a call to free_temp_slots. */
- int keep;
- /* The offset of the slot from the frame_pointer, including extra space
- for alignment. This info is for combine_temp_slots. */
- HOST_WIDE_INT base_offset;
- /* The size of the slot, including extra space for alignment. This
- info is for combine_temp_slots. */
- HOST_WIDE_INT full_size;
-};
-
-/* List of all temporaries allocated, both available and in use. */
-
-struct temp_slot *temp_slots;
-
-/* Current nesting level for temporaries. */
-
-int temp_slot_level;
-
-/* Current nesting level for variables in a block. */
-
-int var_temp_slot_level;
-
-/* When temporaries are created by TARGET_EXPRs, they are created at
- this level of temp_slot_level, so that they can remain allocated
- until no longer needed. CLEANUP_POINT_EXPRs define the lifetime
- of TARGET_EXPRs. */
-int target_temp_slot_level;
-
-/* This structure is used to record MEMs or pseudos used to replace VAR, any
- SUBREGs of VAR, and any MEMs containing VAR as an address. We need to
- maintain this list in case two operands of an insn were required to match;
- in that case we must ensure we use the same replacement. */
-
-struct fixup_replacement
-{
- rtx old;
- rtx new;
- struct fixup_replacement *next;
-};
-
-/* Forward declarations. */
-
-static rtx assign_outer_stack_local PROTO ((enum machine_mode, HOST_WIDE_INT,
- int, struct function *));
-static struct temp_slot *find_temp_slot_from_address PROTO((rtx));
-static void put_reg_into_stack PROTO((struct function *, rtx, tree,
- enum machine_mode, enum machine_mode,
- int, int, int));
-static void fixup_var_refs PROTO((rtx, enum machine_mode, int));
-static struct fixup_replacement
- *find_fixup_replacement PROTO((struct fixup_replacement **, rtx));
-static void fixup_var_refs_insns PROTO((rtx, enum machine_mode, int,
- rtx, int));
-static void fixup_var_refs_1 PROTO((rtx, enum machine_mode, rtx *, rtx,
- struct fixup_replacement **));
-static rtx fixup_memory_subreg PROTO((rtx, rtx, int));
-static rtx walk_fixup_memory_subreg PROTO((rtx, rtx, int));
-static rtx fixup_stack_1 PROTO((rtx, rtx));
-static void optimize_bit_field PROTO((rtx, rtx, rtx *));
-static void instantiate_decls PROTO((tree, int));
-static void instantiate_decls_1 PROTO((tree, int));
-static void instantiate_decl PROTO((rtx, int, int));
-static int instantiate_virtual_regs_1 PROTO((rtx *, rtx, int));
-static void delete_handlers PROTO((void));
-static void pad_to_arg_alignment PROTO((struct args_size *, int));
-#ifndef ARGS_GROW_DOWNWARD
-static void pad_below PROTO((struct args_size *, enum machine_mode,
- tree));
-#endif
-#ifdef ARGS_GROW_DOWNWARD
-static tree round_down PROTO((tree, int));
-#endif
-static rtx round_trampoline_addr PROTO((rtx));
-static tree blocks_nreverse PROTO((tree));
-static int all_blocks PROTO((tree, tree *));
-#if defined (HAVE_prologue) || defined (HAVE_epilogue)
-static int *record_insns PROTO((rtx));
-static int contains PROTO((rtx, int *));
-#endif /* HAVE_prologue || HAVE_epilogue */
-static void put_addressof_into_stack PROTO((rtx));
-static void purge_addressof_1 PROTO((rtx *, rtx, int, int));
-
-/* Pointer to chain of `struct function' for containing functions. */
-struct function *outer_function_chain;
-
-/* Given a function decl for a containing function,
- return the `struct function' for it. */
-
-struct function *
-find_function_data (decl)
- tree decl;
-{
- struct function *p;
-
- for (p = outer_function_chain; p; p = p->next)
- if (p->decl == decl)
- return p;
-
- abort ();
-}
-
-/* Save the current context for compilation of a nested function.
- This is called from language-specific code.
- The caller is responsible for saving any language-specific status,
- since this function knows only about language-independent variables. */
-
-void
-push_function_context_to (context)
- tree context;
-{
- struct function *p = (struct function *) xmalloc (sizeof (struct function));
-
- p->next = outer_function_chain;
- outer_function_chain = p;
-
- p->name = current_function_name;
- p->decl = current_function_decl;
- p->pops_args = current_function_pops_args;
- p->returns_struct = current_function_returns_struct;
- p->returns_pcc_struct = current_function_returns_pcc_struct;
- p->returns_pointer = current_function_returns_pointer;
- p->needs_context = current_function_needs_context;
- p->calls_setjmp = current_function_calls_setjmp;
- p->calls_longjmp = current_function_calls_longjmp;
- p->calls_alloca = current_function_calls_alloca;
- p->has_nonlocal_label = current_function_has_nonlocal_label;
- p->has_nonlocal_goto = current_function_has_nonlocal_goto;
- p->contains_functions = current_function_contains_functions;
- p->is_thunk = current_function_is_thunk;
- p->args_size = current_function_args_size;
- p->pretend_args_size = current_function_pretend_args_size;
- p->arg_offset_rtx = current_function_arg_offset_rtx;
- p->varargs = current_function_varargs;
- p->stdarg = current_function_stdarg;
- p->uses_const_pool = current_function_uses_const_pool;
- p->uses_pic_offset_table = current_function_uses_pic_offset_table;
- p->internal_arg_pointer = current_function_internal_arg_pointer;
- p->cannot_inline = current_function_cannot_inline;
- p->max_parm_reg = max_parm_reg;
- p->parm_reg_stack_loc = parm_reg_stack_loc;
- p->outgoing_args_size = current_function_outgoing_args_size;
- p->return_rtx = current_function_return_rtx;
- p->nonlocal_goto_handler_slots = nonlocal_goto_handler_slots;
- p->nonlocal_goto_stack_level = nonlocal_goto_stack_level;
- p->nonlocal_labels = nonlocal_labels;
- p->cleanup_label = cleanup_label;
- p->return_label = return_label;
- p->save_expr_regs = save_expr_regs;
- p->stack_slot_list = stack_slot_list;
- p->parm_birth_insn = parm_birth_insn;
- p->frame_offset = frame_offset;
- p->tail_recursion_label = tail_recursion_label;
- p->tail_recursion_reentry = tail_recursion_reentry;
- p->arg_pointer_save_area = arg_pointer_save_area;
- p->rtl_expr_chain = rtl_expr_chain;
- p->last_parm_insn = last_parm_insn;
- p->context_display = context_display;
- p->trampoline_list = trampoline_list;
- p->function_call_count = function_call_count;
- p->temp_slots = temp_slots;
- p->temp_slot_level = temp_slot_level;
- p->target_temp_slot_level = target_temp_slot_level;
- p->var_temp_slot_level = var_temp_slot_level;
- p->fixup_var_refs_queue = 0;
- p->epilogue_delay_list = current_function_epilogue_delay_list;
- p->args_info = current_function_args_info;
- p->check_memory_usage = current_function_check_memory_usage;
- p->instrument_entry_exit = current_function_instrument_entry_exit;
- /* CYGNUS LOCAL -- Branch Prediction */
- p->uses_expect = current_function_uses_expect;
- /* END CYGNUS LOCAL -- Branch Prediction */
-
- save_tree_status (p, context);
- save_storage_status (p);
- save_emit_status (p);
- save_expr_status (p);
- save_stmt_status (p);
- save_varasm_status (p, context);
- if (save_machine_status)
- (*save_machine_status) (p);
-}
-
-void
-push_function_context ()
-{
- push_function_context_to (current_function_decl);
-}
-
-/* Restore the last saved context, at the end of a nested function.
- This function is called from language-specific code. */
-
-void
-pop_function_context_from (context)
- tree context;
-{
- struct function *p = outer_function_chain;
- struct var_refs_queue *queue;
-
- outer_function_chain = p->next;
-
- current_function_contains_functions
- = p->contains_functions || p->inline_obstacks
- || context == current_function_decl;
- current_function_name = p->name;
- current_function_decl = p->decl;
- current_function_pops_args = p->pops_args;
- current_function_returns_struct = p->returns_struct;
- current_function_returns_pcc_struct = p->returns_pcc_struct;
- current_function_returns_pointer = p->returns_pointer;
- current_function_needs_context = p->needs_context;
- current_function_calls_setjmp = p->calls_setjmp;
- current_function_calls_longjmp = p->calls_longjmp;
- current_function_calls_alloca = p->calls_alloca;
- current_function_has_nonlocal_label = p->has_nonlocal_label;
- current_function_has_nonlocal_goto = p->has_nonlocal_goto;
- current_function_is_thunk = p->is_thunk;
- current_function_args_size = p->args_size;
- current_function_pretend_args_size = p->pretend_args_size;
- current_function_arg_offset_rtx = p->arg_offset_rtx;
- current_function_varargs = p->varargs;
- current_function_stdarg = p->stdarg;
- current_function_uses_const_pool = p->uses_const_pool;
- current_function_uses_pic_offset_table = p->uses_pic_offset_table;
- current_function_internal_arg_pointer = p->internal_arg_pointer;
- current_function_cannot_inline = p->cannot_inline;
- max_parm_reg = p->max_parm_reg;
- parm_reg_stack_loc = p->parm_reg_stack_loc;
- current_function_outgoing_args_size = p->outgoing_args_size;
- current_function_return_rtx = p->return_rtx;
- nonlocal_goto_handler_slots = p->nonlocal_goto_handler_slots;
- nonlocal_goto_stack_level = p->nonlocal_goto_stack_level;
- nonlocal_labels = p->nonlocal_labels;
- cleanup_label = p->cleanup_label;
- return_label = p->return_label;
- save_expr_regs = p->save_expr_regs;
- stack_slot_list = p->stack_slot_list;
- parm_birth_insn = p->parm_birth_insn;
- frame_offset = p->frame_offset;
- tail_recursion_label = p->tail_recursion_label;
- tail_recursion_reentry = p->tail_recursion_reentry;
- arg_pointer_save_area = p->arg_pointer_save_area;
- rtl_expr_chain = p->rtl_expr_chain;
- last_parm_insn = p->last_parm_insn;
- context_display = p->context_display;
- trampoline_list = p->trampoline_list;
- function_call_count = p->function_call_count;
- temp_slots = p->temp_slots;
- temp_slot_level = p->temp_slot_level;
- target_temp_slot_level = p->target_temp_slot_level;
- var_temp_slot_level = p->var_temp_slot_level;
- current_function_epilogue_delay_list = p->epilogue_delay_list;
- reg_renumber = 0;
- current_function_args_info = p->args_info;
- current_function_check_memory_usage = p->check_memory_usage;
- current_function_instrument_entry_exit = p->instrument_entry_exit;
- /* CYGNUS LOCAL -- Branch Prediction */
- current_function_uses_expect = p->uses_expect;
- /* END CYGNUS LOCAL -- Branch Prediction */
-
- restore_tree_status (p, context);
- restore_storage_status (p);
- restore_expr_status (p);
- restore_emit_status (p);
- restore_stmt_status (p);
- restore_varasm_status (p);
-
- if (restore_machine_status)
- (*restore_machine_status) (p);
-
- /* Finish doing put_var_into_stack for any of our variables
- which became addressable during the nested function. */
- for (queue = p->fixup_var_refs_queue; queue; queue = queue->next)
- fixup_var_refs (queue->modified, queue->promoted_mode, queue->unsignedp);
-
- free (p);
-
- /* Reset variables that have known state during rtx generation. */
- rtx_equal_function_value_matters = 1;
- virtuals_instantiated = 0;
-}
-
-void pop_function_context ()
-{
- pop_function_context_from (current_function_decl);
-}
-
-/* Allocate fixed slots in the stack frame of the current function. */
-
-/* Return size needed for stack frame based on slots so far allocated.
- This size counts from zero. It is not rounded to PREFERRED_STACK_BOUNDARY;
- the caller may have to do that. */
-
-HOST_WIDE_INT
-get_frame_size ()
-{
-#ifdef FRAME_GROWS_DOWNWARD
- return -frame_offset;
-#else
- return frame_offset;
-#endif
-}
-
-/* Allocate a stack slot of SIZE bytes and return a MEM rtx for it
- with machine mode MODE.
-
- ALIGN controls the amount of alignment for the address of the slot:
- 0 means according to MODE,
- -1 means use BIGGEST_ALIGNMENT and round size to multiple of that,
- positive specifies alignment boundary in bits.
-
- We do not round to stack_boundary here. */
-
-rtx
-assign_stack_local (mode, size, align)
- enum machine_mode mode;
- HOST_WIDE_INT size;
- int align;
-{
- register rtx x, addr;
- int bigend_correction = 0;
- int alignment;
-
- if (align == 0)
- {
- alignment = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
- if (mode == BLKmode)
- alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT;
- }
- else if (align == -1)
- {
- alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT;
- size = CEIL_ROUND (size, alignment);
- }
- else
- alignment = align / BITS_PER_UNIT;
-
- /* Round frame offset to that alignment.
- We must be careful here, since FRAME_OFFSET might be negative and
- division with a negative dividend isn't as well defined as we might
- like. So we instead assume that ALIGNMENT is a power of two and
- use logical operations which are unambiguous. */
-#ifdef FRAME_GROWS_DOWNWARD
- frame_offset = FLOOR_ROUND (frame_offset, alignment);
-#else
- frame_offset = CEIL_ROUND (frame_offset, alignment);
-#endif
-
- /* On a big-endian machine, if we are allocating more space than we will use,
- use the least significant bytes of those that are allocated. */
- if (BYTES_BIG_ENDIAN && mode != BLKmode)
- bigend_correction = size - GET_MODE_SIZE (mode);
-
-#ifdef FRAME_GROWS_DOWNWARD
- frame_offset -= size;
-#endif
-
- /* If we have already instantiated virtual registers, return the actual
- address relative to the frame pointer. */
- if (virtuals_instantiated)
- addr = plus_constant (frame_pointer_rtx,
- (frame_offset + bigend_correction
- + STARTING_FRAME_OFFSET));
- else
- addr = plus_constant (virtual_stack_vars_rtx,
- frame_offset + bigend_correction);
-
-#ifndef FRAME_GROWS_DOWNWARD
- frame_offset += size;
-#endif
-
- x = gen_rtx_MEM (mode, addr);
-
- stack_slot_list = gen_rtx_EXPR_LIST (VOIDmode, x, stack_slot_list);
-
- return x;
-}
-
-/* Assign a stack slot in a containing function.
- First three arguments are same as in preceding function.
- The last argument specifies the function to allocate in. */
-
-static rtx
-assign_outer_stack_local (mode, size, align, function)
- enum machine_mode mode;
- HOST_WIDE_INT size;
- int align;
- struct function *function;
-{
- register rtx x, addr;
- int bigend_correction = 0;
- int alignment;
-
- /* Allocate in the memory associated with the function in whose frame
- we are assigning. */
- push_obstacks (function->function_obstack,
- function->function_maybepermanent_obstack);
-
- if (align == 0)
- {
- alignment = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
- if (mode == BLKmode)
- alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT;
- }
- else if (align == -1)
- {
- alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT;
- size = CEIL_ROUND (size, alignment);
- }
- else
- alignment = align / BITS_PER_UNIT;
-
- /* Round frame offset to that alignment. */
-#ifdef FRAME_GROWS_DOWNWARD
- function->frame_offset = FLOOR_ROUND (function->frame_offset, alignment);
-#else
- function->frame_offset = CEIL_ROUND (function->frame_offset, alignment);
-#endif
-
- /* On a big-endian machine, if we are allocating more space than we will use,
- use the least significant bytes of those that are allocated. */
- if (BYTES_BIG_ENDIAN && mode != BLKmode)
- bigend_correction = size - GET_MODE_SIZE (mode);
-
-#ifdef FRAME_GROWS_DOWNWARD
- function->frame_offset -= size;
-#endif
- addr = plus_constant (virtual_stack_vars_rtx,
- function->frame_offset + bigend_correction);
-#ifndef FRAME_GROWS_DOWNWARD
- function->frame_offset += size;
-#endif
-
- x = gen_rtx_MEM (mode, addr);
-
- function->stack_slot_list
- = gen_rtx_EXPR_LIST (VOIDmode, x, function->stack_slot_list);
-
- pop_obstacks ();
-
- return x;
-}
-
-/* Allocate a temporary stack slot and record it for possible later
- reuse.
-
- MODE is the machine mode to be given to the returned rtx.
-
- SIZE is the size in units of the space required. We do no rounding here
- since assign_stack_local will do any required rounding.
-
- KEEP is 1 if this slot is to be retained after a call to
- free_temp_slots. Automatic variables for a block are allocated
- with this flag. KEEP is 2 if we allocate a longer term temporary,
- whose lifetime is controlled by CLEANUP_POINT_EXPRs. KEEP is 3
- if we are to allocate something at an inner level to be treated as
- a variable in the block (e.g., a SAVE_EXPR). */
-
-rtx
-assign_stack_temp (mode, size, keep)
- enum machine_mode mode;
- HOST_WIDE_INT size;
- int keep;
-{
- struct temp_slot *p, *best_p = 0;
-
- /* If SIZE is -1 it means that somebody tried to allocate a temporary
- of a variable size. */
- if (size == -1)
- abort ();
-
- /* First try to find an available, already-allocated temporary that is the
- exact size we require. */
- for (p = temp_slots; p; p = p->next)
- if (p->size == size && GET_MODE (p->slot) == mode && ! p->in_use)
- break;
-
- /* If we didn't find, one, try one that is larger than what we want. We
- find the smallest such. */
- if (p == 0)
- for (p = temp_slots; p; p = p->next)
- if (p->size > size && GET_MODE (p->slot) == mode && ! p->in_use
- && (best_p == 0 || best_p->size > p->size))
- best_p = p;
-
- /* Make our best, if any, the one to use. */
- if (best_p)
- {
- /* If there are enough aligned bytes left over, make them into a new
- temp_slot so that the extra bytes don't get wasted. Do this only
- for BLKmode slots, so that we can be sure of the alignment. */
- if (GET_MODE (best_p->slot) == BLKmode)
- {
- int alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT;
- HOST_WIDE_INT rounded_size = CEIL_ROUND (size, alignment);
-
- if (best_p->size - rounded_size >= alignment)
- {
- p = (struct temp_slot *) oballoc (sizeof (struct temp_slot));
- p->in_use = p->addr_taken = 0;
- p->size = best_p->size - rounded_size;
- p->base_offset = best_p->base_offset + rounded_size;
- p->full_size = best_p->full_size - rounded_size;
- p->slot = gen_rtx_MEM (BLKmode,
- plus_constant (XEXP (best_p->slot, 0),
- rounded_size));
- p->address = 0;
- p->rtl_expr = 0;
- p->next = temp_slots;
- temp_slots = p;
-
- stack_slot_list = gen_rtx_EXPR_LIST (VOIDmode, p->slot,
- stack_slot_list);
-
- best_p->size = rounded_size;
- best_p->full_size = rounded_size;
- }
- }
-
- p = best_p;
- }
-
- /* If we still didn't find one, make a new temporary. */
- if (p == 0)
- {
- HOST_WIDE_INT frame_offset_old = frame_offset;
-
- p = (struct temp_slot *) oballoc (sizeof (struct temp_slot));
-
- /* If the temp slot mode doesn't indicate the alignment,
- use the largest possible, so no one will be disappointed. */
- p->slot = assign_stack_local (mode, size, mode == BLKmode ? -1 : 0);
-
- /* The following slot size computation is necessary because we don't
- know the actual size of the temporary slot until assign_stack_local
- has performed all the frame alignment and size rounding for the
- requested temporary. Note that extra space added for alignment
- can be either above or below this stack slot depending on which
- way the frame grows. We include the extra space if and only if it
- is above this slot. */
-#ifdef FRAME_GROWS_DOWNWARD
- p->size = frame_offset_old - frame_offset;
-#else
- p->size = size;
-#endif
-
- /* Now define the fields used by combine_temp_slots. */
-#ifdef FRAME_GROWS_DOWNWARD
- p->base_offset = frame_offset;
- p->full_size = frame_offset_old - frame_offset;
-#else
- p->base_offset = frame_offset_old;
- p->full_size = frame_offset - frame_offset_old;
-#endif
- p->address = 0;
- p->next = temp_slots;
- temp_slots = p;
- }
-
- p->in_use = 1;
- p->addr_taken = 0;
- p->rtl_expr = sequence_rtl_expr;
-
- if (keep == 2)
- {
- p->level = target_temp_slot_level;
- p->keep = 0;
- }
- else if (keep == 3)
- {
- p->level = var_temp_slot_level;
- p->keep = 0;
- }
- else
- {
- p->level = temp_slot_level;
- p->keep = keep;
- }
-
- /* We may be reusing an old slot, so clear any MEM flags that may have been
- set from before. */
- RTX_UNCHANGING_P (p->slot) = 0;
- MEM_IN_STRUCT_P (p->slot) = 0;
- MEM_SCALAR_P (p->slot) = 0;
- MEM_ALIAS_SET (p->slot) = 0;
- return p->slot;
-}
-
-/* Assign a temporary of given TYPE.
- KEEP is as for assign_stack_temp.
- MEMORY_REQUIRED is 1 if the result must be addressable stack memory;
- it is 0 if a register is OK.
- DONT_PROMOTE is 1 if we should not promote values in register
- to wider modes. */
-
-rtx
-assign_temp (type, keep, memory_required, dont_promote)
- tree type;
- int keep;
- int memory_required;
- int dont_promote;
-{
- enum machine_mode mode = TYPE_MODE (type);
- int unsignedp = TREE_UNSIGNED (type);
-
- if (mode == BLKmode || memory_required)
- {
- HOST_WIDE_INT size = int_size_in_bytes (type);
- rtx tmp;
-
- /* Unfortunately, we don't yet know how to allocate variable-sized
- temporaries. However, sometimes we have a fixed upper limit on
- the size (which is stored in TYPE_ARRAY_MAX_SIZE) and can use that
- instead. This is the case for Chill variable-sized strings. */
- if (size == -1 && TREE_CODE (type) == ARRAY_TYPE
- && TYPE_ARRAY_MAX_SIZE (type) != NULL_TREE
- && TREE_CODE (TYPE_ARRAY_MAX_SIZE (type)) == INTEGER_CST)
- size = TREE_INT_CST_LOW (TYPE_ARRAY_MAX_SIZE (type));
-
- tmp = assign_stack_temp (mode, size, keep);
- MEM_SET_IN_STRUCT_P (tmp, AGGREGATE_TYPE_P (type));
- return tmp;
- }
-
-#ifndef PROMOTE_FOR_CALL_ONLY
- if (! dont_promote)
- mode = promote_mode (type, mode, &unsignedp, 0);
-#endif
-
- return gen_reg_rtx (mode);
-}
-
-/* Combine temporary stack slots which are adjacent on the stack.
-
- This allows for better use of already allocated stack space. This is only
- done for BLKmode slots because we can be sure that we won't have alignment
- problems in this case. */
-
-void
-combine_temp_slots ()
-{
- struct temp_slot *p, *q;
- struct temp_slot *prev_p, *prev_q;
- int num_slots;
-
- /* If there are a lot of temp slots, don't do anything unless
- high levels of optimizaton. */
- if (! flag_expensive_optimizations)
- for (p = temp_slots, num_slots = 0; p; p = p->next, num_slots++)
- if (num_slots > 100 || (num_slots > 10 && optimize == 0))
- return;
-
- for (p = temp_slots, prev_p = 0; p; p = prev_p ? prev_p->next : temp_slots)
- {
- int delete_p = 0;
-
- if (! p->in_use && GET_MODE (p->slot) == BLKmode)
- for (q = p->next, prev_q = p; q; q = prev_q->next)
- {
- int delete_q = 0;
- if (! q->in_use && GET_MODE (q->slot) == BLKmode)
- {
- if (p->base_offset + p->full_size == q->base_offset)
- {
- /* Q comes after P; combine Q into P. */
- p->size += q->size;
- p->full_size += q->full_size;
- delete_q = 1;
- }
- else if (q->base_offset + q->full_size == p->base_offset)
- {
- /* P comes after Q; combine P into Q. */
- q->size += p->size;
- q->full_size += p->full_size;
- delete_p = 1;
- break;
- }
- }
- /* Either delete Q or advance past it. */
- if (delete_q)
- prev_q->next = q->next;
- else
- prev_q = q;
- }
- /* Either delete P or advance past it. */
- if (delete_p)
- {
- if (prev_p)
- prev_p->next = p->next;
- else
- temp_slots = p->next;
- }
- else
- prev_p = p;
- }
-}
-
-/* Find the temp slot corresponding to the object at address X. */
-
-static struct temp_slot *
-find_temp_slot_from_address (x)
- rtx x;
-{
- struct temp_slot *p;
- rtx next;
-
- for (p = temp_slots; p; p = p->next)
- {
- if (! p->in_use)
- continue;
-
- else if (XEXP (p->slot, 0) == x
- || p->address == x
- || (GET_CODE (x) == PLUS
- && XEXP (x, 0) == virtual_stack_vars_rtx
- && GET_CODE (XEXP (x, 1)) == CONST_INT
- && INTVAL (XEXP (x, 1)) >= p->base_offset
- && INTVAL (XEXP (x, 1)) < p->base_offset + p->full_size))
- return p;
-
- else if (p->address != 0 && GET_CODE (p->address) == EXPR_LIST)
- for (next = p->address; next; next = XEXP (next, 1))
- if (XEXP (next, 0) == x)
- return p;
- }
-
- return 0;
-}
-
-/* Indicate that NEW is an alternate way of referring to the temp slot
- that previously was known by OLD. */
-
-void
-update_temp_slot_address (old, new)
- rtx old, new;
-{
- struct temp_slot *p = find_temp_slot_from_address (old);
-
- /* If none, return. Else add NEW as an alias. */
- if (p == 0)
- return;
- else if (p->address == 0)
- p->address = new;
- else
- {
- if (GET_CODE (p->address) != EXPR_LIST)
- p->address = gen_rtx_EXPR_LIST (VOIDmode, p->address, NULL_RTX);
-
- p->address = gen_rtx_EXPR_LIST (VOIDmode, new, p->address);
- }
-}
-
-/* If X could be a reference to a temporary slot, mark the fact that its
- address was taken. */
-
-void
-mark_temp_addr_taken (x)
- rtx x;
-{
- struct temp_slot *p;
-
- if (x == 0)
- return;
-
- /* If X is not in memory or is at a constant address, it cannot be in
- a temporary slot. */
- if (GET_CODE (x) != MEM || CONSTANT_P (XEXP (x, 0)))
- return;
-
- p = find_temp_slot_from_address (XEXP (x, 0));
- if (p != 0)
- p->addr_taken = 1;
-}
-
-/* If X could be a reference to a temporary slot, mark that slot as
- belonging to the to one level higher than the current level. If X
- matched one of our slots, just mark that one. Otherwise, we can't
- easily predict which it is, so upgrade all of them. Kept slots
- need not be touched.
-
- This is called when an ({...}) construct occurs and a statement
- returns a value in memory. */
-
-void
-preserve_temp_slots (x)
- rtx x;
-{
- struct temp_slot *p = 0;
-
- /* If there is no result, we still might have some objects whose address
- were taken, so we need to make sure they stay around. */
- if (x == 0)
- {
- for (p = temp_slots; p; p = p->next)
- if (p->in_use && p->level == temp_slot_level && p->addr_taken)
- p->level--;
-
- return;
- }
-
- /* If X is a register that is being used as a pointer, see if we have
- a temporary slot we know it points to. To be consistent with
- the code below, we really should preserve all non-kept slots
- if we can't find a match, but that seems to be much too costly. */
- if (GET_CODE (x) == REG && REGNO_POINTER_FLAG (REGNO (x)))
- p = find_temp_slot_from_address (x);
-
- /* If X is not in memory or is at a constant address, it cannot be in
- a temporary slot, but it can contain something whose address was
- taken. */
- if (p == 0 && (GET_CODE (x) != MEM || CONSTANT_P (XEXP (x, 0))))
- {
- for (p = temp_slots; p; p = p->next)
- if (p->in_use && p->level == temp_slot_level && p->addr_taken)
- p->level--;
-
- return;
- }
-
- /* First see if we can find a match. */
- if (p == 0)
- p = find_temp_slot_from_address (XEXP (x, 0));
-
- if (p != 0)
- {
- /* Move everything at our level whose address was taken to our new
- level in case we used its address. */
- struct temp_slot *q;
-
- if (p->level == temp_slot_level)
- {
- for (q = temp_slots; q; q = q->next)
- if (q != p && q->addr_taken && q->level == p->level)
- q->level--;
-
- p->level--;
- p->addr_taken = 0;
- }
- return;
- }
-
- /* Otherwise, preserve all non-kept slots at this level. */
- for (p = temp_slots; p; p = p->next)
- if (p->in_use && p->level == temp_slot_level && ! p->keep)
- p->level--;
-}
-
-/* X is the result of an RTL_EXPR. If it is a temporary slot associated
- with that RTL_EXPR, promote it into a temporary slot at the present
- level so it will not be freed when we free slots made in the
- RTL_EXPR. */
-
-void
-preserve_rtl_expr_result (x)
- rtx x;
-{
- struct temp_slot *p;
-
- /* If X is not in memory or is at a constant address, it cannot be in
- a temporary slot. */
- if (x == 0 || GET_CODE (x) != MEM || CONSTANT_P (XEXP (x, 0)))
- return;
-
- /* If we can find a match, move it to our level unless it is already at
- an upper level. */
- p = find_temp_slot_from_address (XEXP (x, 0));
- if (p != 0)
- {
- p->level = MIN (p->level, temp_slot_level);
- p->rtl_expr = 0;
- }
-
- return;
-}
-
-/* Free all temporaries used so far. This is normally called at the end
- of generating code for a statement. Don't free any temporaries
- currently in use for an RTL_EXPR that hasn't yet been emitted.
- We could eventually do better than this since it can be reused while
- generating the same RTL_EXPR, but this is complex and probably not
- worthwhile. */
-
-void
-free_temp_slots ()
-{
- struct temp_slot *p;
-
- for (p = temp_slots; p; p = p->next)
- if (p->in_use && p->level == temp_slot_level && ! p->keep
- && p->rtl_expr == 0)
- p->in_use = 0;
-
- combine_temp_slots ();
-}
-
-/* Free all temporary slots used in T, an RTL_EXPR node. */
-
-void
-free_temps_for_rtl_expr (t)
- tree t;
-{
- struct temp_slot *p;
-
- for (p = temp_slots; p; p = p->next)
- if (p->rtl_expr == t)
- p->in_use = 0;
-
- combine_temp_slots ();
-}
-
-/* Mark all temporaries ever allocated in this function as not suitable
- for reuse until the current level is exited. */
-
-void
-mark_all_temps_used ()
-{
- struct temp_slot *p;
-
- for (p = temp_slots; p; p = p->next)
- {
- p->in_use = p->keep = 1;
- p->level = MIN (p->level, temp_slot_level);
- }
-}
-
-/* Push deeper into the nesting level for stack temporaries. */
-
-void
-push_temp_slots ()
-{
- temp_slot_level++;
-}
-
-/* Likewise, but save the new level as the place to allocate variables
- for blocks. */
-
-void
-push_temp_slots_for_block ()
-{
- push_temp_slots ();
-
- var_temp_slot_level = temp_slot_level;
-}
-
-/* Likewise, but save the new level as the place to allocate temporaries
- for TARGET_EXPRs. */
-
-void
-push_temp_slots_for_target ()
-{
- push_temp_slots ();
-
- target_temp_slot_level = temp_slot_level;
-}
-
-/* Set and get the value of target_temp_slot_level. The only
- permitted use of these functions is to save and restore this value. */
-
-int
-get_target_temp_slot_level ()
-{
- return target_temp_slot_level;
-}
-
-void
-set_target_temp_slot_level (level)
- int level;
-{
- target_temp_slot_level = level;
-}
-
-/* Pop a temporary nesting level. All slots in use in the current level
- are freed. */
-
-void
-pop_temp_slots ()
-{
- struct temp_slot *p;
-
- for (p = temp_slots; p; p = p->next)
- if (p->in_use && p->level == temp_slot_level && p->rtl_expr == 0)
- p->in_use = 0;
-
- combine_temp_slots ();
-
- temp_slot_level--;
-}
-
-/* Initialize temporary slots. */
-
-void
-init_temp_slots ()
-{
- /* We have not allocated any temporaries yet. */
- temp_slots = 0;
- temp_slot_level = 0;
- var_temp_slot_level = 0;
- target_temp_slot_level = 0;
-}
-
-/* Retroactively move an auto variable from a register to a stack slot.
- This is done when an address-reference to the variable is seen. */
-
-void
-put_var_into_stack (decl)
- tree decl;
-{
- register rtx reg;
- enum machine_mode promoted_mode, decl_mode;
- struct function *function = 0;
- tree context;
- int can_use_addressof;
-
- context = decl_function_context (decl);
-
- /* Get the current rtl used for this object and its original mode. */
- reg = TREE_CODE (decl) == SAVE_EXPR ? SAVE_EXPR_RTL (decl) : DECL_RTL (decl);
-
- /* No need to do anything if decl has no rtx yet
- since in that case caller is setting TREE_ADDRESSABLE
- and a stack slot will be assigned when the rtl is made. */
- if (reg == 0)
- return;
-
- /* Get the declared mode for this object. */
- decl_mode = (TREE_CODE (decl) == SAVE_EXPR ? TYPE_MODE (TREE_TYPE (decl))
- : DECL_MODE (decl));
- /* Get the mode it's actually stored in. */
- promoted_mode = GET_MODE (reg);
-
- /* If this variable comes from an outer function,
- find that function's saved context. */
- if (context != current_function_decl && context != inline_function_decl)
- for (function = outer_function_chain; function; function = function->next)
- if (function->decl == context)
- break;
-
- /* If this is a variable-size object with a pseudo to address it,
- put that pseudo into the stack, if the var is nonlocal. */
- if (DECL_NONLOCAL (decl)
- && GET_CODE (reg) == MEM
- && GET_CODE (XEXP (reg, 0)) == REG
- && REGNO (XEXP (reg, 0)) > LAST_VIRTUAL_REGISTER)
- {
- reg = XEXP (reg, 0);
- decl_mode = promoted_mode = GET_MODE (reg);
- }
-
- can_use_addressof
- = (function == 0
- && optimize > 0
- /* FIXME make it work for promoted modes too */
- && decl_mode == promoted_mode
-#ifdef NON_SAVING_SETJMP
- && ! (NON_SAVING_SETJMP && current_function_calls_setjmp)
-#endif
- );
-
- /* If we can't use ADDRESSOF, make sure we see through one we already
- generated. */
- if (! can_use_addressof && GET_CODE (reg) == MEM
- && GET_CODE (XEXP (reg, 0)) == ADDRESSOF)
- reg = XEXP (XEXP (reg, 0), 0);
-
- /* Now we should have a value that resides in one or more pseudo regs. */
-
- if (GET_CODE (reg) == REG)
- {
- /* If this variable lives in the current function and we don't need
- to put things in the stack for the sake of setjmp, try to keep it
- in a register until we know we actually need the address. */
- if (can_use_addressof)
- gen_mem_addressof (reg, decl);
- else
- put_reg_into_stack (function, reg, TREE_TYPE (decl),
- promoted_mode, decl_mode,
- TREE_SIDE_EFFECTS (decl), 0,
- TREE_USED (decl)
- || DECL_INITIAL (decl) != 0);
- }
- else if (GET_CODE (reg) == CONCAT)
- {
- /* A CONCAT contains two pseudos; put them both in the stack.
- We do it so they end up consecutive. */
- enum machine_mode part_mode = GET_MODE (XEXP (reg, 0));
- tree part_type = TREE_TYPE (TREE_TYPE (decl));
-#ifdef FRAME_GROWS_DOWNWARD
- /* Since part 0 should have a lower address, do it second. */
- put_reg_into_stack (function, XEXP (reg, 1), part_type, part_mode,
- part_mode, TREE_SIDE_EFFECTS (decl), 0,
- TREE_USED (decl) || DECL_INITIAL (decl) != 0);
- put_reg_into_stack (function, XEXP (reg, 0), part_type, part_mode,
- part_mode, TREE_SIDE_EFFECTS (decl), 0,
- TREE_USED (decl) || DECL_INITIAL (decl) != 0);
-#else
- put_reg_into_stack (function, XEXP (reg, 0), part_type, part_mode,
- part_mode, TREE_SIDE_EFFECTS (decl), 0,
- TREE_USED (decl) || DECL_INITIAL (decl) != 0);
- put_reg_into_stack (function, XEXP (reg, 1), part_type, part_mode,
- part_mode, TREE_SIDE_EFFECTS (decl), 0,
- TREE_USED (decl) || DECL_INITIAL (decl) != 0);
-#endif
-
- /* Change the CONCAT into a combined MEM for both parts. */
- PUT_CODE (reg, MEM);
- MEM_VOLATILE_P (reg) = MEM_VOLATILE_P (XEXP (reg, 0));
- MEM_ALIAS_SET (reg) = get_alias_set (decl);
-
- /* The two parts are in memory order already.
- Use the lower parts address as ours. */
- XEXP (reg, 0) = XEXP (XEXP (reg, 0), 0);
- /* Prevent sharing of rtl that might lose. */
- if (GET_CODE (XEXP (reg, 0)) == PLUS)
- XEXP (reg, 0) = copy_rtx (XEXP (reg, 0));
- }
- else
- return;
-
- if (current_function_check_memory_usage)
- emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
- XEXP (reg, 0), ptr_mode,
- GEN_INT (GET_MODE_SIZE (GET_MODE (reg))),
- TYPE_MODE (sizetype),
- GEN_INT (MEMORY_USE_RW),
- TYPE_MODE (integer_type_node));
-}
-
-/* Subroutine of put_var_into_stack. This puts a single pseudo reg REG
- into the stack frame of FUNCTION (0 means the current function).
- DECL_MODE is the machine mode of the user-level data type.
- PROMOTED_MODE is the machine mode of the register.
- VOLATILE_P is nonzero if this is for a "volatile" decl.
- USED_P is nonzero if this reg might have already been used in an insn. */
-
-static void
-put_reg_into_stack (function, reg, type, promoted_mode, decl_mode, volatile_p,
- original_regno, used_p)
- struct function *function;
- rtx reg;
- tree type;
- enum machine_mode promoted_mode, decl_mode;
- int volatile_p;
- int original_regno;
- int used_p;
-{
- rtx new = 0;
- int regno = original_regno;
-
- if (regno == 0)
- regno = REGNO (reg);
-
- if (function)
- {
- if (regno < function->max_parm_reg)
- new = function->parm_reg_stack_loc[regno];
- if (new == 0)
- new = assign_outer_stack_local (decl_mode, GET_MODE_SIZE (decl_mode),
- 0, function);
- }
- else
- {
- if (regno < max_parm_reg)
- new = parm_reg_stack_loc[regno];
- if (new == 0)
- new = assign_stack_local (decl_mode, GET_MODE_SIZE (decl_mode), 0);
- }
-
- PUT_MODE (reg, decl_mode);
- XEXP (reg, 0) = XEXP (new, 0);
- /* `volatil' bit means one thing for MEMs, another entirely for REGs. */
- MEM_VOLATILE_P (reg) = volatile_p;
- PUT_CODE (reg, MEM);
-
- /* If this is a memory ref that contains aggregate components,
- mark it as such for cse and loop optimize. If we are reusing a
- previously generated stack slot, then we need to copy the bit in
- case it was set for other reasons. For instance, it is set for
- __builtin_va_alist. */
- MEM_SET_IN_STRUCT_P (reg,
- AGGREGATE_TYPE_P (type) || MEM_IN_STRUCT_P (new));
- MEM_ALIAS_SET (reg) = get_alias_set (type);
-
- /* Now make sure that all refs to the variable, previously made
- when it was a register, are fixed up to be valid again. */
-
- if (used_p && function != 0)
- {
- struct var_refs_queue *temp;
-
- /* Variable is inherited; fix it up when we get back to its function. */
- push_obstacks (function->function_obstack,
- function->function_maybepermanent_obstack);
-
- /* See comment in restore_tree_status in tree.c for why this needs to be
- on saveable obstack. */
- temp
- = (struct var_refs_queue *) savealloc (sizeof (struct var_refs_queue));
- temp->modified = reg;
- temp->promoted_mode = promoted_mode;
- temp->unsignedp = TREE_UNSIGNED (type);
- temp->next = function->fixup_var_refs_queue;
- function->fixup_var_refs_queue = temp;
- pop_obstacks ();
- }
- else if (used_p)
- /* Variable is local; fix it up now. */
- fixup_var_refs (reg, promoted_mode, TREE_UNSIGNED (type));
-}
-
-static void
-fixup_var_refs (var, promoted_mode, unsignedp)
- rtx var;
- enum machine_mode promoted_mode;
- int unsignedp;
-{
- tree pending;
- rtx first_insn = get_insns ();
- struct sequence_stack *stack = sequence_stack;
- tree rtl_exps = rtl_expr_chain;
-
- /* Must scan all insns for stack-refs that exceed the limit. */
- fixup_var_refs_insns (var, promoted_mode, unsignedp, first_insn, stack == 0);
-
- /* Scan all pending sequences too. */
- for (; stack; stack = stack->next)
- {
- push_to_sequence (stack->first);
- fixup_var_refs_insns (var, promoted_mode, unsignedp,
- stack->first, stack->next != 0);
- /* Update remembered end of sequence
- in case we added an insn at the end. */
- stack->last = get_last_insn ();
- end_sequence ();
- }
-
- /* Scan all waiting RTL_EXPRs too. */
- for (pending = rtl_exps; pending; pending = TREE_CHAIN (pending))
- {
- rtx seq = RTL_EXPR_SEQUENCE (TREE_VALUE (pending));
- if (seq != const0_rtx && seq != 0)
- {
- push_to_sequence (seq);
- fixup_var_refs_insns (var, promoted_mode, unsignedp, seq, 0);
- end_sequence ();
- }
- }
-
- /* Scan the catch clauses for exception handling too. */
- push_to_sequence (catch_clauses);
- fixup_var_refs_insns (var, promoted_mode, unsignedp, catch_clauses, 0);
- end_sequence ();
-}
-
-/* REPLACEMENTS is a pointer to a list of the struct fixup_replacement and X is
- some part of an insn. Return a struct fixup_replacement whose OLD
- value is equal to X. Allocate a new structure if no such entry exists. */
-
-static struct fixup_replacement *
-find_fixup_replacement (replacements, x)
- struct fixup_replacement **replacements;
- rtx x;
-{
- struct fixup_replacement *p;
-
- /* See if we have already replaced this. */
- for (p = *replacements; p && p->old != x; p = p->next)
- ;
-
- if (p == 0)
- {
- p = (struct fixup_replacement *) oballoc (sizeof (struct fixup_replacement));
- p->old = x;
- p->new = 0;
- p->next = *replacements;
- *replacements = p;
- }
-
- return p;
-}
-
-/* Scan the insn-chain starting with INSN for refs to VAR
- and fix them up. TOPLEVEL is nonzero if this chain is the
- main chain of insns for the current function. */
-
-static void
-fixup_var_refs_insns (var, promoted_mode, unsignedp, insn, toplevel)
- rtx var;
- enum machine_mode promoted_mode;
- int unsignedp;
- rtx insn;
- int toplevel;
-{
- rtx call_dest = 0;
-
- while (insn)
- {
- rtx next = NEXT_INSN (insn);
- rtx set, prev, prev_set;
- rtx note;
-
- if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
- {
- /* If this is a CLOBBER of VAR, delete it.
-
- If it has a REG_LIBCALL note, delete the REG_LIBCALL
- and REG_RETVAL notes too. */
- if (GET_CODE (PATTERN (insn)) == CLOBBER
- && (XEXP (PATTERN (insn), 0) == var
- || (GET_CODE (XEXP (PATTERN (insn), 0)) == CONCAT
- && (XEXP (XEXP (PATTERN (insn), 0), 0) == var
- || XEXP (XEXP (PATTERN (insn), 0), 1) == var))))
- {
- if ((note = find_reg_note (insn, REG_LIBCALL, NULL_RTX)) != 0)
- /* The REG_LIBCALL note will go away since we are going to
- turn INSN into a NOTE, so just delete the
- corresponding REG_RETVAL note. */
- remove_note (XEXP (note, 0),
- find_reg_note (XEXP (note, 0), REG_RETVAL,
- NULL_RTX));
-
- /* In unoptimized compilation, we shouldn't call delete_insn
- except in jump.c doing warnings. */
- PUT_CODE (insn, NOTE);
- NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (insn) = 0;
- }
-
- /* The insn to load VAR from a home in the arglist
- is now a no-op. When we see it, just delete it.
- Similarly if this is storing VAR from a register from which
- it was loaded in the previous insn. This will occur
- when an ADDRESSOF was made for an arglist slot. */
- else if (toplevel
- && (set = single_set (insn)) != 0
- && SET_DEST (set) == var
- /* If this represents the result of an insn group,
- don't delete the insn. */
- && find_reg_note (insn, REG_RETVAL, NULL_RTX) == 0
- && (rtx_equal_p (SET_SRC (set), var)
- || (GET_CODE (SET_SRC (set)) == REG
- && (prev = prev_nonnote_insn (insn)) != 0
- && (prev_set = single_set (prev)) != 0
- && SET_DEST (prev_set) == SET_SRC (set)
- && rtx_equal_p (SET_SRC (prev_set), var))))
- {
- /* In unoptimized compilation, we shouldn't call delete_insn
- except in jump.c doing warnings. */
- PUT_CODE (insn, NOTE);
- NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (insn) = 0;
- if (insn == last_parm_insn)
- last_parm_insn = PREV_INSN (next);
- }
- else
- {
- struct fixup_replacement *replacements = 0;
- rtx next_insn = NEXT_INSN (insn);
-
- if (SMALL_REGISTER_CLASSES)
- {
- /* If the insn that copies the results of a CALL_INSN
- into a pseudo now references VAR, we have to use an
- intermediate pseudo since we want the life of the
- return value register to be only a single insn.
-
- If we don't use an intermediate pseudo, such things as
- address computations to make the address of VAR valid
- if it is not can be placed between the CALL_INSN and INSN.
-
- To make sure this doesn't happen, we record the destination
- of the CALL_INSN and see if the next insn uses both that
- and VAR. */
-
- if (call_dest != 0 && GET_CODE (insn) == INSN
- && reg_mentioned_p (var, PATTERN (insn))
- && reg_mentioned_p (call_dest, PATTERN (insn)))
- {
- rtx temp = gen_reg_rtx (GET_MODE (call_dest));
-
- emit_insn_before (gen_move_insn (temp, call_dest), insn);
-
- PATTERN (insn) = replace_rtx (PATTERN (insn),
- call_dest, temp);
- }
-
- if (GET_CODE (insn) == CALL_INSN
- && GET_CODE (PATTERN (insn)) == SET)
- call_dest = SET_DEST (PATTERN (insn));
- else if (GET_CODE (insn) == CALL_INSN
- && GET_CODE (PATTERN (insn)) == PARALLEL
- && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET)
- call_dest = SET_DEST (XVECEXP (PATTERN (insn), 0, 0));
- else
- call_dest = 0;
- }
-
- /* See if we have to do anything to INSN now that VAR is in
- memory. If it needs to be loaded into a pseudo, use a single
- pseudo for the entire insn in case there is a MATCH_DUP
- between two operands. We pass a pointer to the head of
- a list of struct fixup_replacements. If fixup_var_refs_1
- needs to allocate pseudos or replacement MEMs (for SUBREGs),
- it will record them in this list.
-
- If it allocated a pseudo for any replacement, we copy into
- it here. */
-
- fixup_var_refs_1 (var, promoted_mode, &PATTERN (insn), insn,
- &replacements);
-
- /* If this is last_parm_insn, and any instructions were output
- after it to fix it up, then we must set last_parm_insn to
- the last such instruction emitted. */
- if (insn == last_parm_insn)
- last_parm_insn = PREV_INSN (next_insn);
-
- while (replacements)
- {
- if (GET_CODE (replacements->new) == REG)
- {
- rtx insert_before;
- rtx seq;
-
- /* OLD might be a (subreg (mem)). */
- if (GET_CODE (replacements->old) == SUBREG)
- replacements->old
- = fixup_memory_subreg (replacements->old, insn, 0);
- else
- replacements->old
- = fixup_stack_1 (replacements->old, insn);
-
- insert_before = insn;
-
- /* If we are changing the mode, do a conversion.
- This might be wasteful, but combine.c will
- eliminate much of the waste. */
-
- if (GET_MODE (replacements->new)
- != GET_MODE (replacements->old))
- {
- start_sequence ();
- convert_move (replacements->new,
- replacements->old, unsignedp);
- seq = gen_sequence ();
- end_sequence ();
- }
- else
- seq = gen_move_insn (replacements->new,
- replacements->old);
-
- emit_insn_before (seq, insert_before);
- }
-
- replacements = replacements->next;
- }
- }
-
- /* Also fix up any invalid exprs in the REG_NOTES of this insn.
- But don't touch other insns referred to by reg-notes;
- we will get them elsewhere. */
- for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
- if (GET_CODE (note) != INSN_LIST)
- XEXP (note, 0)
- = walk_fixup_memory_subreg (XEXP (note, 0), insn, 1);
- }
- insn = next;
- }
-}
-
-/* VAR is a MEM that used to be a pseudo register with mode PROMOTED_MODE.
- See if the rtx expression at *LOC in INSN needs to be changed.
-
- REPLACEMENTS is a pointer to a list head that starts out zero, but may
- contain a list of original rtx's and replacements. If we find that we need
- to modify this insn by replacing a memory reference with a pseudo or by
- making a new MEM to implement a SUBREG, we consult that list to see if
- we have already chosen a replacement. If none has already been allocated,
- we allocate it and update the list. fixup_var_refs_insns will copy VAR
- or the SUBREG, as appropriate, to the pseudo. */
-
-static void
-fixup_var_refs_1 (var, promoted_mode, loc, insn, replacements)
- register rtx var;
- enum machine_mode promoted_mode;
- register rtx *loc;
- rtx insn;
- struct fixup_replacement **replacements;
-{
- register int i;
- register rtx x = *loc;
- RTX_CODE code = GET_CODE (x);
- register char *fmt;
- register rtx tem, tem1;
- struct fixup_replacement *replacement;
-
- switch (code)
- {
- case ADDRESSOF:
- if (XEXP (x, 0) == var)
- {
- /* Prevent sharing of rtl that might lose. */
- rtx sub = copy_rtx (XEXP (var, 0));
-
- start_sequence ();
-
- if (! validate_change (insn, loc, sub, 0))
- {
- rtx y = force_operand (sub, NULL_RTX);
-
- if (! validate_change (insn, loc, y, 0))
- *loc = copy_to_reg (y);
- }
-
- emit_insn_before (gen_sequence (), insn);
- end_sequence ();
- }
- return;
-
- case MEM:
- if (var == x)
- {
- /* If we already have a replacement, use it. Otherwise,
- try to fix up this address in case it is invalid. */
-
- replacement = find_fixup_replacement (replacements, var);
- if (replacement->new)
- {
- *loc = replacement->new;
- return;
- }
-
- *loc = replacement->new = x = fixup_stack_1 (x, insn);
-
- /* Unless we are forcing memory to register or we changed the mode,
- we can leave things the way they are if the insn is valid. */
-
- INSN_CODE (insn) = -1;
- if (! flag_force_mem && GET_MODE (x) == promoted_mode
- && recog_memoized (insn) >= 0)
- return;
-
- *loc = replacement->new = gen_reg_rtx (promoted_mode);
- return;
- }
-
- /* If X contains VAR, we need to unshare it here so that we update
- each occurrence separately. But all identical MEMs in one insn
- must be replaced with the same rtx because of the possibility of
- MATCH_DUPs. */
-
- if (reg_mentioned_p (var, x))
- {
- replacement = find_fixup_replacement (replacements, x);
- if (replacement->new == 0)
- replacement->new = copy_most_rtx (x, var);
-
- *loc = x = replacement->new;
- }
- break;
-
- case REG:
- case CC0:
- case PC:
- case CONST_INT:
- case CONST:
- case SYMBOL_REF:
- case LABEL_REF:
- case CONST_DOUBLE:
- return;
-
- case SIGN_EXTRACT:
- case ZERO_EXTRACT:
- /* Note that in some cases those types of expressions are altered
- by optimize_bit_field, and do not survive to get here. */
- if (XEXP (x, 0) == var
- || (GET_CODE (XEXP (x, 0)) == SUBREG
- && SUBREG_REG (XEXP (x, 0)) == var))
- {
- /* Get TEM as a valid MEM in the mode presently in the insn.
-
- We don't worry about the possibility of MATCH_DUP here; it
- is highly unlikely and would be tricky to handle. */
-
- tem = XEXP (x, 0);
- if (GET_CODE (tem) == SUBREG)
- {
- if (GET_MODE_BITSIZE (GET_MODE (tem))
- > GET_MODE_BITSIZE (GET_MODE (var)))
- {
- replacement = find_fixup_replacement (replacements, var);
- if (replacement->new == 0)
- replacement->new = gen_reg_rtx (GET_MODE (var));
- SUBREG_REG (tem) = replacement->new;
- }
- else
- tem = fixup_memory_subreg (tem, insn, 0);
- }
- else
- tem = fixup_stack_1 (tem, insn);
-
- /* Unless we want to load from memory, get TEM into the proper mode
- for an extract from memory. This can only be done if the
- extract is at a constant position and length. */
-
- if (! flag_force_mem && GET_CODE (XEXP (x, 1)) == CONST_INT
- && GET_CODE (XEXP (x, 2)) == CONST_INT
- && ! mode_dependent_address_p (XEXP (tem, 0))
- && ! MEM_VOLATILE_P (tem))
- {
- enum machine_mode wanted_mode = VOIDmode;
- enum machine_mode is_mode = GET_MODE (tem);
- HOST_WIDE_INT pos = INTVAL (XEXP (x, 2));
-
-#ifdef HAVE_extzv
- if (GET_CODE (x) == ZERO_EXTRACT)
- {
- wanted_mode = insn_operand_mode[(int) CODE_FOR_extzv][1];
- if (wanted_mode == VOIDmode)
- wanted_mode = word_mode;
- }
-#endif
-#ifdef HAVE_extv
- if (GET_CODE (x) == SIGN_EXTRACT)
- {
- wanted_mode = insn_operand_mode[(int) CODE_FOR_extv][1];
- if (wanted_mode == VOIDmode)
- wanted_mode = word_mode;
- }
-#endif
- /* If we have a narrower mode, we can do something. */
- if (wanted_mode != VOIDmode
- && GET_MODE_SIZE (wanted_mode) < GET_MODE_SIZE (is_mode))
- {
- HOST_WIDE_INT offset = pos / BITS_PER_UNIT;
- rtx old_pos = XEXP (x, 2);
- rtx newmem;
-
- /* If the bytes and bits are counted differently, we
- must adjust the offset. */
- if (BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN)
- offset = (GET_MODE_SIZE (is_mode)
- - GET_MODE_SIZE (wanted_mode) - offset);
-
- pos %= GET_MODE_BITSIZE (wanted_mode);
-
- newmem = gen_rtx_MEM (wanted_mode,
- plus_constant (XEXP (tem, 0), offset));
- RTX_UNCHANGING_P (newmem) = RTX_UNCHANGING_P (tem);
- MEM_COPY_ATTRIBUTES (newmem, tem);
-
- /* Make the change and see if the insn remains valid. */
- INSN_CODE (insn) = -1;
- XEXP (x, 0) = newmem;
- XEXP (x, 2) = GEN_INT (pos);
-
- if (recog_memoized (insn) >= 0)
- return;
-
- /* Otherwise, restore old position. XEXP (x, 0) will be
- restored later. */
- XEXP (x, 2) = old_pos;
- }
- }
-
- /* If we get here, the bitfield extract insn can't accept a memory
- reference. Copy the input into a register. */
-
- tem1 = gen_reg_rtx (GET_MODE (tem));
- emit_insn_before (gen_move_insn (tem1, tem), insn);
- XEXP (x, 0) = tem1;
- return;
- }
- break;
-
- case SUBREG:
- if (SUBREG_REG (x) == var)
- {
- /* If this is a special SUBREG made because VAR was promoted
- from a wider mode, replace it with VAR and call ourself
- recursively, this time saying that the object previously
- had its current mode (by virtue of the SUBREG). */
-
- if (SUBREG_PROMOTED_VAR_P (x))
- {
- *loc = var;
- fixup_var_refs_1 (var, GET_MODE (var), loc, insn, replacements);
- return;
- }
-
- /* If this SUBREG makes VAR wider, it has become a paradoxical
- SUBREG with VAR in memory, but these aren't allowed at this
- stage of the compilation. So load VAR into a pseudo and take
- a SUBREG of that pseudo. */
- if (GET_MODE_SIZE (GET_MODE (x)) > GET_MODE_SIZE (GET_MODE (var)))
- {
- replacement = find_fixup_replacement (replacements, var);
- if (replacement->new == 0)
- replacement->new = gen_reg_rtx (GET_MODE (var));
- SUBREG_REG (x) = replacement->new;
- return;
- }
-
- /* See if we have already found a replacement for this SUBREG.
- If so, use it. Otherwise, make a MEM and see if the insn
- is recognized. If not, or if we should force MEM into a register,
- make a pseudo for this SUBREG. */
- replacement = find_fixup_replacement (replacements, x);
- if (replacement->new)
- {
- *loc = replacement->new;
- return;
- }
-
- replacement->new = *loc = fixup_memory_subreg (x, insn, 0);
-
- INSN_CODE (insn) = -1;
- if (! flag_force_mem && recog_memoized (insn) >= 0)
- return;
-
- *loc = replacement->new = gen_reg_rtx (GET_MODE (x));
- return;
- }
- break;
-
- case SET:
- /* First do special simplification of bit-field references. */
- if (GET_CODE (SET_DEST (x)) == SIGN_EXTRACT
- || GET_CODE (SET_DEST (x)) == ZERO_EXTRACT)
- optimize_bit_field (x, insn, 0);
- if (GET_CODE (SET_SRC (x)) == SIGN_EXTRACT
- || GET_CODE (SET_SRC (x)) == ZERO_EXTRACT)
- optimize_bit_field (x, insn, NULL_PTR);
-
- /* For a paradoxical SUBREG inside a ZERO_EXTRACT, load the object
- into a register and then store it back out. */
- if (GET_CODE (SET_DEST (x)) == ZERO_EXTRACT
- && GET_CODE (XEXP (SET_DEST (x), 0)) == SUBREG
- && SUBREG_REG (XEXP (SET_DEST (x), 0)) == var
- && (GET_MODE_SIZE (GET_MODE (XEXP (SET_DEST (x), 0)))
- > GET_MODE_SIZE (GET_MODE (var))))
- {
- replacement = find_fixup_replacement (replacements, var);
- if (replacement->new == 0)
- replacement->new = gen_reg_rtx (GET_MODE (var));
-
- SUBREG_REG (XEXP (SET_DEST (x), 0)) = replacement->new;
- emit_insn_after (gen_move_insn (var, replacement->new), insn);
- }
-
- /* If SET_DEST is now a paradoxical SUBREG, put the result of this
- insn into a pseudo and store the low part of the pseudo into VAR. */
- if (GET_CODE (SET_DEST (x)) == SUBREG
- && SUBREG_REG (SET_DEST (x)) == var
- && (GET_MODE_SIZE (GET_MODE (SET_DEST (x)))
- > GET_MODE_SIZE (GET_MODE (var))))
- {
- SET_DEST (x) = tem = gen_reg_rtx (GET_MODE (SET_DEST (x)));
- emit_insn_after (gen_move_insn (var, gen_lowpart (GET_MODE (var),
- tem)),
- insn);
- break;
- }
-
- {
- rtx dest = SET_DEST (x);
- rtx src = SET_SRC (x);
-#ifdef HAVE_insv
- rtx outerdest = dest;
-#endif
-
- while (GET_CODE (dest) == SUBREG || GET_CODE (dest) == STRICT_LOW_PART
- || GET_CODE (dest) == SIGN_EXTRACT
- || GET_CODE (dest) == ZERO_EXTRACT)
- dest = XEXP (dest, 0);
-
- if (GET_CODE (src) == SUBREG)
- src = XEXP (src, 0);
-
- /* If VAR does not appear at the top level of the SET
- just scan the lower levels of the tree. */
-
- if (src != var && dest != var)
- break;
-
- /* We will need to rerecognize this insn. */
- INSN_CODE (insn) = -1;
-
-#ifdef HAVE_insv
- if (GET_CODE (outerdest) == ZERO_EXTRACT && dest == var)
- {
- /* Since this case will return, ensure we fixup all the
- operands here. */
- fixup_var_refs_1 (var, promoted_mode, &XEXP (outerdest, 1),
- insn, replacements);
- fixup_var_refs_1 (var, promoted_mode, &XEXP (outerdest, 2),
- insn, replacements);
- fixup_var_refs_1 (var, promoted_mode, &SET_SRC (x),
- insn, replacements);
-
- tem = XEXP (outerdest, 0);
-
- /* Clean up (SUBREG:SI (MEM:mode ...) 0)
- that may appear inside a ZERO_EXTRACT.
- This was legitimate when the MEM was a REG. */
- if (GET_CODE (tem) == SUBREG
- && SUBREG_REG (tem) == var)
- tem = fixup_memory_subreg (tem, insn, 0);
- else
- tem = fixup_stack_1 (tem, insn);
-
- if (GET_CODE (XEXP (outerdest, 1)) == CONST_INT
- && GET_CODE (XEXP (outerdest, 2)) == CONST_INT
- && ! mode_dependent_address_p (XEXP (tem, 0))
- && ! MEM_VOLATILE_P (tem))
- {
- enum machine_mode wanted_mode;
- enum machine_mode is_mode = GET_MODE (tem);
- HOST_WIDE_INT pos = INTVAL (XEXP (outerdest, 2));
-
- wanted_mode = insn_operand_mode[(int) CODE_FOR_insv][0];
- if (wanted_mode == VOIDmode)
- wanted_mode = word_mode;
-
- /* If we have a narrower mode, we can do something. */
- if (GET_MODE_SIZE (wanted_mode) < GET_MODE_SIZE (is_mode))
- {
- HOST_WIDE_INT offset = pos / BITS_PER_UNIT;
- rtx old_pos = XEXP (outerdest, 2);
- rtx newmem;
-
- if (BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN)
- offset = (GET_MODE_SIZE (is_mode)
- - GET_MODE_SIZE (wanted_mode) - offset);
-
- pos %= GET_MODE_BITSIZE (wanted_mode);
-
- newmem = gen_rtx_MEM (wanted_mode,
- plus_constant (XEXP (tem, 0), offset));
- RTX_UNCHANGING_P (newmem) = RTX_UNCHANGING_P (tem);
- MEM_COPY_ATTRIBUTES (newmem, tem);
-
- /* Make the change and see if the insn remains valid. */
- INSN_CODE (insn) = -1;
- XEXP (outerdest, 0) = newmem;
- XEXP (outerdest, 2) = GEN_INT (pos);
-
- if (recog_memoized (insn) >= 0)
- return;
-
- /* Otherwise, restore old position. XEXP (x, 0) will be
- restored later. */
- XEXP (outerdest, 2) = old_pos;
- }
- }
-
- /* If we get here, the bit-field store doesn't allow memory
- or isn't located at a constant position. Load the value into
- a register, do the store, and put it back into memory. */
-
- tem1 = gen_reg_rtx (GET_MODE (tem));
- emit_insn_before (gen_move_insn (tem1, tem), insn);
- emit_insn_after (gen_move_insn (tem, tem1), insn);
- XEXP (outerdest, 0) = tem1;
- return;
- }
-#endif
-
- /* STRICT_LOW_PART is a no-op on memory references
- and it can cause combinations to be unrecognizable,
- so eliminate it. */
-
- if (dest == var && GET_CODE (SET_DEST (x)) == STRICT_LOW_PART)
- SET_DEST (x) = XEXP (SET_DEST (x), 0);
-
- /* A valid insn to copy VAR into or out of a register
- must be left alone, to avoid an infinite loop here.
- If the reference to VAR is by a subreg, fix that up,
- since SUBREG is not valid for a memref.
- Also fix up the address of the stack slot.
-
- Note that we must not try to recognize the insn until
- after we know that we have valid addresses and no
- (subreg (mem ...) ...) constructs, since these interfere
- with determining the validity of the insn. */
-
- if ((SET_SRC (x) == var
- || (GET_CODE (SET_SRC (x)) == SUBREG
- && SUBREG_REG (SET_SRC (x)) == var))
- && (GET_CODE (SET_DEST (x)) == REG
- || (GET_CODE (SET_DEST (x)) == SUBREG
- && GET_CODE (SUBREG_REG (SET_DEST (x))) == REG))
- && GET_MODE (var) == promoted_mode
- && x == single_set (insn))
- {
- rtx pat;
-
- replacement = find_fixup_replacement (replacements, SET_SRC (x));
- if (replacement->new)
- SET_SRC (x) = replacement->new;
- else if (GET_CODE (SET_SRC (x)) == SUBREG)
- SET_SRC (x) = replacement->new
- = fixup_memory_subreg (SET_SRC (x), insn, 0);
- else
- SET_SRC (x) = replacement->new
- = fixup_stack_1 (SET_SRC (x), insn);
-
- if (recog_memoized (insn) >= 0)
- return;
-
- /* INSN is not valid, but we know that we want to
- copy SET_SRC (x) to SET_DEST (x) in some way. So
- we generate the move and see whether it requires more
- than one insn. If it does, we emit those insns and
- delete INSN. Otherwise, we an just replace the pattern
- of INSN; we have already verified above that INSN has
- no other function that to do X. */
-
- pat = gen_move_insn (SET_DEST (x), SET_SRC (x));
- if (GET_CODE (pat) == SEQUENCE)
- {
- emit_insn_after (pat, insn);
- PUT_CODE (insn, NOTE);
- NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (insn) = 0;
- }
- else
- PATTERN (insn) = pat;
-
- return;
- }
-
- if ((SET_DEST (x) == var
- || (GET_CODE (SET_DEST (x)) == SUBREG
- && SUBREG_REG (SET_DEST (x)) == var))
- && (GET_CODE (SET_SRC (x)) == REG
- || (GET_CODE (SET_SRC (x)) == SUBREG
- && GET_CODE (SUBREG_REG (SET_SRC (x))) == REG))
- && GET_MODE (var) == promoted_mode
- && x == single_set (insn))
- {
- rtx pat;
-
- if (GET_CODE (SET_DEST (x)) == SUBREG)
- SET_DEST (x) = fixup_memory_subreg (SET_DEST (x), insn, 0);
- else
- SET_DEST (x) = fixup_stack_1 (SET_DEST (x), insn);
-
- if (recog_memoized (insn) >= 0)
- return;
-
- pat = gen_move_insn (SET_DEST (x), SET_SRC (x));
- if (GET_CODE (pat) == SEQUENCE)
- {
- emit_insn_after (pat, insn);
- PUT_CODE (insn, NOTE);
- NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (insn) = 0;
- }
- else
- PATTERN (insn) = pat;
-
- return;
- }
-
- /* Otherwise, storing into VAR must be handled specially
- by storing into a temporary and copying that into VAR
- with a new insn after this one. Note that this case
- will be used when storing into a promoted scalar since
- the insn will now have different modes on the input
- and output and hence will be invalid (except for the case
- of setting it to a constant, which does not need any
- change if it is valid). We generate extra code in that case,
- but combine.c will eliminate it. */
-
- if (dest == var)
- {
- rtx temp;
- rtx fixeddest = SET_DEST (x);
-
- /* STRICT_LOW_PART can be discarded, around a MEM. */
- if (GET_CODE (fixeddest) == STRICT_LOW_PART)
- fixeddest = XEXP (fixeddest, 0);
- /* Convert (SUBREG (MEM)) to a MEM in a changed mode. */
- if (GET_CODE (fixeddest) == SUBREG)
- {
- fixeddest = fixup_memory_subreg (fixeddest, insn, 0);
- promoted_mode = GET_MODE (fixeddest);
- }
- else
- fixeddest = fixup_stack_1 (fixeddest, insn);
-
- temp = gen_reg_rtx (promoted_mode);
-
- emit_insn_after (gen_move_insn (fixeddest,
- gen_lowpart (GET_MODE (fixeddest),
- temp)),
- insn);
-
- SET_DEST (x) = temp;
- }
- }
-
- default:
- break;
- }
-
- /* Nothing special about this RTX; fix its operands. */
-
- fmt = GET_RTX_FORMAT (code);
- for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
- {
- if (fmt[i] == 'e')
- fixup_var_refs_1 (var, promoted_mode, &XEXP (x, i), insn, replacements);
- if (fmt[i] == 'E')
- {
- register int j;
- for (j = 0; j < XVECLEN (x, i); j++)
- fixup_var_refs_1 (var, promoted_mode, &XVECEXP (x, i, j),
- insn, replacements);
- }
- }
-}
-
-/* Given X, an rtx of the form (SUBREG:m1 (MEM:m2 addr)),
- return an rtx (MEM:m1 newaddr) which is equivalent.
- If any insns must be emitted to compute NEWADDR, put them before INSN.
-
- UNCRITICAL nonzero means accept paradoxical subregs.
- This is used for subregs found inside REG_NOTES. */
-
-static rtx
-fixup_memory_subreg (x, insn, uncritical)
- rtx x;
- rtx insn;
- int uncritical;
-{
- int offset = SUBREG_WORD (x) * UNITS_PER_WORD;
- rtx addr = XEXP (SUBREG_REG (x), 0);
- enum machine_mode mode = GET_MODE (x);
- rtx result;
-
- /* Paradoxical SUBREGs are usually invalid during RTL generation. */
- if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))
- && ! uncritical)
- abort ();
-
- if (BYTES_BIG_ENDIAN)
- offset += (MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
- - MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode)));
- addr = plus_constant (addr, offset);
- if (!flag_force_addr && memory_address_p (mode, addr))
- /* Shortcut if no insns need be emitted. */
- return change_address (SUBREG_REG (x), mode, addr);
- start_sequence ();
- result = change_address (SUBREG_REG (x), mode, addr);
- emit_insn_before (gen_sequence (), insn);
- end_sequence ();
- return result;
-}
-
-/* Do fixup_memory_subreg on all (SUBREG (MEM ...) ...) contained in X.
- Replace subexpressions of X in place.
- If X itself is a (SUBREG (MEM ...) ...), return the replacement expression.
- Otherwise return X, with its contents possibly altered.
-
- If any insns must be emitted to compute NEWADDR, put them before INSN.
-
- UNCRITICAL is as in fixup_memory_subreg. */
-
-static rtx
-walk_fixup_memory_subreg (x, insn, uncritical)
- register rtx x;
- rtx insn;
- int uncritical;
-{
- register enum rtx_code code;
- register char *fmt;
- register int i;
-
- if (x == 0)
- return 0;
-
- code = GET_CODE (x);
-
- if (code == SUBREG && GET_CODE (SUBREG_REG (x)) == MEM)
- return fixup_memory_subreg (x, insn, uncritical);
-
- /* Nothing special about this RTX; fix its operands. */
-
- fmt = GET_RTX_FORMAT (code);
- for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
- {
- if (fmt[i] == 'e')
- XEXP (x, i) = walk_fixup_memory_subreg (XEXP (x, i), insn, uncritical);
- if (fmt[i] == 'E')
- {
- register int j;
- for (j = 0; j < XVECLEN (x, i); j++)
- XVECEXP (x, i, j)
- = walk_fixup_memory_subreg (XVECEXP (x, i, j), insn, uncritical);
- }
- }
- return x;
-}
-
-/* For each memory ref within X, if it refers to a stack slot
- with an out of range displacement, put the address in a temp register
- (emitting new insns before INSN to load these registers)
- and alter the memory ref to use that register.
- Replace each such MEM rtx with a copy, to avoid clobberage. */
-
-static rtx
-fixup_stack_1 (x, insn)
- rtx x;
- rtx insn;
-{
- register int i;
- register RTX_CODE code = GET_CODE (x);
- register char *fmt;
-
- if (code == MEM)
- {
- register rtx ad = XEXP (x, 0);
- /* If we have address of a stack slot but it's not valid
- (displacement is too large), compute the sum in a register. */
- if (GET_CODE (ad) == PLUS
- && GET_CODE (XEXP (ad, 0)) == REG
- && ((REGNO (XEXP (ad, 0)) >= FIRST_VIRTUAL_REGISTER
- && REGNO (XEXP (ad, 0)) <= LAST_VIRTUAL_REGISTER)
- || REGNO (XEXP (ad, 0)) == FRAME_POINTER_REGNUM
-#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
- || REGNO (XEXP (ad, 0)) == HARD_FRAME_POINTER_REGNUM
-#endif
- || REGNO (XEXP (ad, 0)) == STACK_POINTER_REGNUM
- || REGNO (XEXP (ad, 0)) == ARG_POINTER_REGNUM
- || XEXP (ad, 0) == current_function_internal_arg_pointer)
- && GET_CODE (XEXP (ad, 1)) == CONST_INT)
- {
- rtx temp, seq;
- if (memory_address_p (GET_MODE (x), ad))
- return x;
-
- start_sequence ();
- temp = copy_to_reg (ad);
- seq = gen_sequence ();
- end_sequence ();
- emit_insn_before (seq, insn);
- return change_address (x, VOIDmode, temp);
- }
- return x;
- }
-
- fmt = GET_RTX_FORMAT (code);
- for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
- {
- if (fmt[i] == 'e')
- XEXP (x, i) = fixup_stack_1 (XEXP (x, i), insn);
- if (fmt[i] == 'E')
- {
- register int j;
- for (j = 0; j < XVECLEN (x, i); j++)
- XVECEXP (x, i, j) = fixup_stack_1 (XVECEXP (x, i, j), insn);
- }
- }
- return x;
-}
-
-/* Optimization: a bit-field instruction whose field
- happens to be a byte or halfword in memory
- can be changed to a move instruction.
-
- We call here when INSN is an insn to examine or store into a bit-field.
- BODY is the SET-rtx to be altered.
-
- EQUIV_MEM is the table `reg_equiv_mem' if that is available; else 0.
- (Currently this is called only from function.c, and EQUIV_MEM
- is always 0.) */
-
-static void
-optimize_bit_field (body, insn, equiv_mem)
- rtx body;
- rtx insn;
- rtx *equiv_mem;
-{
- register rtx bitfield;
- int destflag;
- rtx seq = 0;
- enum machine_mode mode;
-
- if (GET_CODE (SET_DEST (body)) == SIGN_EXTRACT
- || GET_CODE (SET_DEST (body)) == ZERO_EXTRACT)
- bitfield = SET_DEST (body), destflag = 1;
- else
- bitfield = SET_SRC (body), destflag = 0;
-
- /* First check that the field being stored has constant size and position
- and is in fact a byte or halfword suitably aligned. */
-
- if (GET_CODE (XEXP (bitfield, 1)) == CONST_INT
- && GET_CODE (XEXP (bitfield, 2)) == CONST_INT
- && ((mode = mode_for_size (INTVAL (XEXP (bitfield, 1)), MODE_INT, 1))
- != BLKmode)
- && INTVAL (XEXP (bitfield, 2)) % INTVAL (XEXP (bitfield, 1)) == 0)
- {
- register rtx memref = 0;
-
- /* Now check that the containing word is memory, not a register,
- and that it is safe to change the machine mode. */
-
- if (GET_CODE (XEXP (bitfield, 0)) == MEM)
- memref = XEXP (bitfield, 0);
- else if (GET_CODE (XEXP (bitfield, 0)) == REG
- && equiv_mem != 0)
- memref = equiv_mem[REGNO (XEXP (bitfield, 0))];
- else if (GET_CODE (XEXP (bitfield, 0)) == SUBREG
- && GET_CODE (SUBREG_REG (XEXP (bitfield, 0))) == MEM)
- memref = SUBREG_REG (XEXP (bitfield, 0));
- else if (GET_CODE (XEXP (bitfield, 0)) == SUBREG
- && equiv_mem != 0
- && GET_CODE (SUBREG_REG (XEXP (bitfield, 0))) == REG)
- memref = equiv_mem[REGNO (SUBREG_REG (XEXP (bitfield, 0)))];
-
- if (memref
- && ! mode_dependent_address_p (XEXP (memref, 0))
- && ! MEM_VOLATILE_P (memref))
- {
- /* Now adjust the address, first for any subreg'ing
- that we are now getting rid of,
- and then for which byte of the word is wanted. */
-
- HOST_WIDE_INT offset = INTVAL (XEXP (bitfield, 2));
- rtx insns;
-
- /* Adjust OFFSET to count bits from low-address byte. */
- if (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN)
- offset = (GET_MODE_BITSIZE (GET_MODE (XEXP (bitfield, 0)))
- - offset - INTVAL (XEXP (bitfield, 1)));
-
- /* Adjust OFFSET to count bytes from low-address byte. */
- offset /= BITS_PER_UNIT;
- if (GET_CODE (XEXP (bitfield, 0)) == SUBREG)
- {
- offset += SUBREG_WORD (XEXP (bitfield, 0)) * UNITS_PER_WORD;
- if (BYTES_BIG_ENDIAN)
- offset -= (MIN (UNITS_PER_WORD,
- GET_MODE_SIZE (GET_MODE (XEXP (bitfield, 0))))
- - MIN (UNITS_PER_WORD,
- GET_MODE_SIZE (GET_MODE (memref))));
- }
-
- start_sequence ();
- memref = change_address (memref, mode,
- plus_constant (XEXP (memref, 0), offset));
- insns = get_insns ();
- end_sequence ();
- emit_insns_before (insns, insn);
-
- /* Store this memory reference where
- we found the bit field reference. */
-
- if (destflag)
- {
- validate_change (insn, &SET_DEST (body), memref, 1);
- if (! CONSTANT_ADDRESS_P (SET_SRC (body)))
- {
- rtx src = SET_SRC (body);
- while (GET_CODE (src) == SUBREG
- && SUBREG_WORD (src) == 0)
- src = SUBREG_REG (src);
- if (GET_MODE (src) != GET_MODE (memref))
- src = gen_lowpart (GET_MODE (memref), SET_SRC (body));
- validate_change (insn, &SET_SRC (body), src, 1);
- }
- else if (GET_MODE (SET_SRC (body)) != VOIDmode
- && GET_MODE (SET_SRC (body)) != GET_MODE (memref))
- /* This shouldn't happen because anything that didn't have
- one of these modes should have got converted explicitly
- and then referenced through a subreg.
- This is so because the original bit-field was
- handled by agg_mode and so its tree structure had
- the same mode that memref now has. */
- abort ();
- }
- else
- {
- rtx dest = SET_DEST (body);
-
- while (GET_CODE (dest) == SUBREG
- && SUBREG_WORD (dest) == 0
- && (GET_MODE_CLASS (GET_MODE (dest))
- == GET_MODE_CLASS (GET_MODE (SUBREG_REG (dest)))))
- dest = SUBREG_REG (dest);
-
- validate_change (insn, &SET_DEST (body), dest, 1);
-
- if (GET_MODE (dest) == GET_MODE (memref))
- validate_change (insn, &SET_SRC (body), memref, 1);
- else
- {
- /* Convert the mem ref to the destination mode. */
- rtx newreg = gen_reg_rtx (GET_MODE (dest));
-
- start_sequence ();
- convert_move (newreg, memref,
- GET_CODE (SET_SRC (body)) == ZERO_EXTRACT);
- seq = get_insns ();
- end_sequence ();
-
- validate_change (insn, &SET_SRC (body), newreg, 1);
- }
- }
-
- /* See if we can convert this extraction or insertion into
- a simple move insn. We might not be able to do so if this
- was, for example, part of a PARALLEL.
-
- If we succeed, write out any needed conversions. If we fail,
- it is hard to guess why we failed, so don't do anything
- special; just let the optimization be suppressed. */
-
- if (apply_change_group () && seq)
- emit_insns_before (seq, insn);
- }
- }
-}
-
-/* These routines are responsible for converting virtual register references
- to the actual hard register references once RTL generation is complete.
-
- The following four variables are used for communication between the
- routines. They contain the offsets of the virtual registers from their
- respective hard registers. */
-
-static int in_arg_offset;
-static int var_offset;
-static int dynamic_offset;
-static int out_arg_offset;
-static int cfa_offset;
-
-/* In most machines, the stack pointer register is equivalent to the bottom
- of the stack. */
-
-#ifndef STACK_POINTER_OFFSET
-#define STACK_POINTER_OFFSET 0
-#endif
-
-/* If not defined, pick an appropriate default for the offset of dynamically
- allocated memory depending on the value of ACCUMULATE_OUTGOING_ARGS,
- REG_PARM_STACK_SPACE, and OUTGOING_REG_PARM_STACK_SPACE. */
-
-#ifndef STACK_DYNAMIC_OFFSET
-
-#ifdef ACCUMULATE_OUTGOING_ARGS
-/* The bottom of the stack points to the actual arguments. If
- REG_PARM_STACK_SPACE is defined, this includes the space for the register
- parameters. However, if OUTGOING_REG_PARM_STACK space is not defined,
- stack space for register parameters is not pushed by the caller, but
- rather part of the fixed stack areas and hence not included in
- `current_function_outgoing_args_size'. Nevertheless, we must allow
- for it when allocating stack dynamic objects. */
-
-#if defined(REG_PARM_STACK_SPACE) && ! defined(OUTGOING_REG_PARM_STACK_SPACE)
-#define STACK_DYNAMIC_OFFSET(FNDECL) \
-(current_function_outgoing_args_size \
- + REG_PARM_STACK_SPACE (FNDECL) + (STACK_POINTER_OFFSET))
-
-#else
-#define STACK_DYNAMIC_OFFSET(FNDECL) \
-(current_function_outgoing_args_size + (STACK_POINTER_OFFSET))
-#endif
-
-#else
-#define STACK_DYNAMIC_OFFSET(FNDECL) STACK_POINTER_OFFSET
-#endif
-#endif
-
-/* On a few machines, the CFA coincides with the arg pointer. */
-
-#ifndef ARG_POINTER_CFA_OFFSET
-#define ARG_POINTER_CFA_OFFSET 0
-#endif
-
-
-/* Build up a (MEM (ADDRESSOF (REG))) rtx for a register REG that just had
- its address taken. DECL is the decl for the object stored in the
- register, for later use if we do need to force REG into the stack.
- REG is overwritten by the MEM like in put_reg_into_stack. */
-
-rtx
-gen_mem_addressof (reg, decl)
- rtx reg;
- tree decl;
-{
- tree type = TREE_TYPE (decl);
- rtx r = gen_rtx_ADDRESSOF (Pmode, gen_reg_rtx (GET_MODE (reg)), REGNO (reg));
- SET_ADDRESSOF_DECL (r, decl);
- /* If the original REG was a user-variable, then so is the REG whose
- address is being taken. */
- REG_USERVAR_P (XEXP (r, 0)) = REG_USERVAR_P (reg);
-
- XEXP (reg, 0) = r;
- PUT_CODE (reg, MEM);
- PUT_MODE (reg, DECL_MODE (decl));
- MEM_VOLATILE_P (reg) = TREE_SIDE_EFFECTS (decl);
- MEM_SET_IN_STRUCT_P (reg, AGGREGATE_TYPE_P (type));
- MEM_ALIAS_SET (reg) = get_alias_set (decl);
-
- if (TREE_USED (decl) || DECL_INITIAL (decl) != 0)
- fixup_var_refs (reg, GET_MODE (reg), TREE_UNSIGNED (type));
-
- return reg;
-}
-
-/* If DECL has an RTL that is an ADDRESSOF rtx, put it into the stack. */
-
-void
-flush_addressof (decl)
- tree decl;
-{
- if ((TREE_CODE (decl) == PARM_DECL || TREE_CODE (decl) == VAR_DECL)
- && DECL_RTL (decl) != 0
- && GET_CODE (DECL_RTL (decl)) == MEM
- && GET_CODE (XEXP (DECL_RTL (decl), 0)) == ADDRESSOF
- && GET_CODE (XEXP (XEXP (DECL_RTL (decl), 0), 0)) == REG)
- put_addressof_into_stack (XEXP (DECL_RTL (decl), 0));
-}
-
-/* Force the register pointed to by R, an ADDRESSOF rtx, into the stack. */
-
-static void
-put_addressof_into_stack (r)
- rtx r;
-{
- tree decl = ADDRESSOF_DECL (r);
- rtx reg = XEXP (r, 0);
-
- if (GET_CODE (reg) != REG)
- abort ();
-
- put_reg_into_stack (0, reg, TREE_TYPE (decl), GET_MODE (reg),
- DECL_MODE (decl), TREE_SIDE_EFFECTS (decl),
- ADDRESSOF_REGNO (r),
- TREE_USED (decl) || DECL_INITIAL (decl) != 0);
-}
-
-/* List of replacements made below in purge_addressof_1 when creating
- bitfield insertions. */
-static rtx purge_addressof_replacements;
-
-/* Helper function for purge_addressof. See if the rtx expression at *LOC
- in INSN needs to be changed. If FORCE, always put any ADDRESSOFs into
- the stack. */
-
-static void
-purge_addressof_1 (loc, insn, force, store)
- rtx *loc;
- rtx insn;
- int force, store;
-{
- rtx x;
- RTX_CODE code;
- int i, j;
- char *fmt;
-
- /* Re-start here to avoid recursion in common cases. */
- restart:
-
- x = *loc;
- if (x == 0)
- return;
-
- code = GET_CODE (x);
-
- if (code == ADDRESSOF && GET_CODE (XEXP (x, 0)) == MEM)
- {
- rtx insns;
- /* We must create a copy of the rtx because it was created by
- overwriting a REG rtx which is always shared. */
- rtx sub = copy_rtx (XEXP (XEXP (x, 0), 0));
-
- if (validate_change (insn, loc, sub, 0))
- return;
-
- start_sequence ();
- if (! validate_change (insn, loc,
- force_operand (sub, NULL_RTX),
- 0))
- abort ();
-
- insns = gen_sequence ();
- end_sequence ();
- emit_insn_before (insns, insn);
- return;
- }
- else if (code == MEM && GET_CODE (XEXP (x, 0)) == ADDRESSOF && ! force)
- {
- rtx sub = XEXP (XEXP (x, 0), 0);
-
- if (GET_CODE (sub) == MEM)
- sub = gen_rtx_MEM (GET_MODE (x), copy_rtx (XEXP (sub, 0)));
-
- if (GET_CODE (sub) == REG
- && (MEM_VOLATILE_P (x) || GET_MODE (x) == BLKmode))
- {
- put_addressof_into_stack (XEXP (x, 0));
- return;
- }
- else if (GET_CODE (sub) == REG && GET_MODE (x) != GET_MODE (sub))
- {
- int size_x, size_sub;
-
- if (!insn)
- {
- /* When processing REG_NOTES look at the list of
- replacements done on the insn to find the register that X
- was replaced by. */
- rtx tem;
-
- for (tem = purge_addressof_replacements; tem != NULL_RTX;
- tem = XEXP (XEXP (tem, 1), 1))
- {
- rtx y = XEXP (tem, 0);
- if (GET_CODE (y) == MEM
- && rtx_equal_p (XEXP (x, 0), XEXP (y, 0)))
- {
- /* It can happen that the note may speak of things in
- a wider (or just different) mode than the code did.
- This is especially true of REG_RETVAL. */
-
- rtx z = XEXP (XEXP (tem, 1), 0);
- if (GET_MODE (x) != GET_MODE (y))
- {
- if (GET_CODE (z) == SUBREG && SUBREG_WORD (z) == 0)
- z = SUBREG_REG (z);
-
- /* ??? If we'd gotten into any of the really complex
- cases below, I'm not sure we can do a proper
- replacement. Might we be able to delete the
- note in some cases? */
- if (GET_MODE_SIZE (GET_MODE (x))
- < GET_MODE_SIZE (GET_MODE (y)))
- abort ();
-
- if (GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD
- && (GET_MODE_SIZE (GET_MODE (x))
- > GET_MODE_SIZE (GET_MODE (z))))
- {
- /* This can occur as a result in invalid
- pointer casts, e.g. float f; ...
- *(long long int *)&f.
- ??? We could emit a warning here, but
- without a line number that wouldn't be
- very helpful. */
- z = gen_rtx_SUBREG (GET_MODE (x), z, 0);
- }
- else
- z = gen_lowpart (GET_MODE (x), z);
- }
-
- *loc = z;
- return;
- }
- }
-
- /* There should always be such a replacement. */
- abort ();
- }
-
- size_x = GET_MODE_BITSIZE (GET_MODE (x));
- size_sub = GET_MODE_BITSIZE (GET_MODE (sub));
-
- /* Don't even consider working with paradoxical subregs,
- or the moral equivalent seen here. */
- if (size_x <= size_sub
- && int_mode_for_mode (GET_MODE (sub)) != BLKmode)
- {
- /* Do a bitfield insertion to mirror what would happen
- in memory. */
-
- rtx val, seq;
-
- if (store)
- {
- rtx p;
-
- start_sequence ();
- val = gen_reg_rtx (GET_MODE (x));
- if (! validate_change (insn, loc, val, 0))
- {
- /* Discard the current sequence and put the
- ADDRESSOF on stack. */
- end_sequence ();
- goto give_up;
- }
- seq = gen_sequence ();
- end_sequence ();
- emit_insn_before (seq, insn);
-
- start_sequence ();
- store_bit_field (sub, size_x, 0, GET_MODE (x),
- val, GET_MODE_SIZE (GET_MODE (sub)),
- GET_MODE_SIZE (GET_MODE (sub)));
-
- /* Make sure to unshare any shared rtl that store_bit_field
- might have created. */
- for (p = get_insns(); p; p = NEXT_INSN (p))
- {
- reset_used_flags (PATTERN (p));
- reset_used_flags (REG_NOTES (p));
- reset_used_flags (LOG_LINKS (p));
- }
- unshare_all_rtl (get_insns ());
-
- seq = gen_sequence ();
- end_sequence ();
- emit_insn_after (seq, insn);
- }
- else
- {
- start_sequence ();
- val = extract_bit_field (sub, size_x, 0, 1, NULL_RTX,
- GET_MODE (x), GET_MODE (x),
- GET_MODE_SIZE (GET_MODE (sub)),
- GET_MODE_SIZE (GET_MODE (sub)));
-
- if (! validate_change (insn, loc, val, 0))
- {
- /* Discard the current sequence and put the
- ADDRESSOF on stack. */
- end_sequence ();
- goto give_up;
- }
-
- seq = gen_sequence ();
- end_sequence ();
- emit_insn_before (seq, insn);
- }
-
- /* Remember the replacement so that the same one can be done
- on the REG_NOTES. */
- purge_addressof_replacements
- = gen_rtx_EXPR_LIST (VOIDmode, x,
- gen_rtx_EXPR_LIST (VOIDmode, val,
- purge_addressof_replacements));
-
- /* We replaced with a reg -- all done. */
- return;
- }
- }
- else if (validate_change (insn, loc, sub, 0))
- {
- /* Remember the replacement so that the same one can be done
- on the REG_NOTES. */
- purge_addressof_replacements
- = gen_rtx_EXPR_LIST (VOIDmode, x,
- gen_rtx_EXPR_LIST (VOIDmode, sub,
- purge_addressof_replacements));
- goto restart;
- }
- give_up:;
- /* else give up and put it into the stack */
- }
- else if (code == ADDRESSOF)
- {
- put_addressof_into_stack (x);
- return;
- }
- else if (code == SET)
- {
- purge_addressof_1 (&SET_DEST (x), insn, force, 1);
- purge_addressof_1 (&SET_SRC (x), insn, force, 0);
- return;
- }
- else if (code == CALL)
- {
- purge_addressof_1 (&XEXP (x, 0), insn, 1, 0);
- purge_addressof_1 (&XEXP (x, 1), insn, force, 0);
- return;
- }
-
- /* Scan all subexpressions. */
- fmt = GET_RTX_FORMAT (code);
- for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++)
- {
- if (*fmt == 'e')
- purge_addressof_1 (&XEXP (x, i), insn, force, 0);
- else if (*fmt == 'E')
- for (j = 0; j < XVECLEN (x, i); j++)
- purge_addressof_1 (&XVECEXP (x, i, j), insn, force, 0);
- }
-}
-
-/* Eliminate all occurrences of ADDRESSOF from INSNS. Elide any remaining
- (MEM (ADDRESSOF)) patterns, and force any needed registers into the
- stack. */
-
-void
-purge_addressof (insns)
- rtx insns;
-{
- rtx insn;
- for (insn = insns; insn; insn = NEXT_INSN (insn))
- if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN
- || GET_CODE (insn) == CALL_INSN)
- {
- purge_addressof_1 (&PATTERN (insn), insn,
- asm_noperands (PATTERN (insn)) > 0, 0);
- purge_addressof_1 (®_NOTES (insn), NULL_RTX, 0, 0);
- }
- purge_addressof_replacements = 0;
-}
-
-/* Pass through the INSNS of function FNDECL and convert virtual register
- references to hard register references. */
-
-void
-instantiate_virtual_regs (fndecl, insns)
- tree fndecl;
- rtx insns;
-{
- rtx insn;
- int i;
-
- /* Compute the offsets to use for this function. */
- in_arg_offset = FIRST_PARM_OFFSET (fndecl);
- var_offset = STARTING_FRAME_OFFSET;
- dynamic_offset = STACK_DYNAMIC_OFFSET (fndecl);
- out_arg_offset = STACK_POINTER_OFFSET;
- cfa_offset = ARG_POINTER_CFA_OFFSET;
-
- /* Scan all variables and parameters of this function. For each that is
- in memory, instantiate all virtual registers if the result is a valid
- address. If not, we do it later. That will handle most uses of virtual
- regs on many machines. */
- instantiate_decls (fndecl, 1);
-
- /* Initialize recognition, indicating that volatile is OK. */
- init_recog ();
-
- /* Scan through all the insns, instantiating every virtual register still
- present. */
- for (insn = insns; insn; insn = NEXT_INSN (insn))
- if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN
- || GET_CODE (insn) == CALL_INSN)
- {
- instantiate_virtual_regs_1 (&PATTERN (insn), insn, 1);
- instantiate_virtual_regs_1 (®_NOTES (insn), NULL_RTX, 0);
- }
-
- /* Instantiate the stack slots for the parm registers, for later use in
- addressof elimination. */
- for (i = 0; i < max_parm_reg; ++i)
- if (parm_reg_stack_loc[i])
- instantiate_virtual_regs_1 (&parm_reg_stack_loc[i], NULL_RTX, 0);
-
- /* Now instantiate the remaining register equivalences for debugging info.
- These will not be valid addresses. */
- instantiate_decls (fndecl, 0);
-
- /* Indicate that, from now on, assign_stack_local should use
- frame_pointer_rtx. */
- virtuals_instantiated = 1;
-}
-
-/* Scan all decls in FNDECL (both variables and parameters) and instantiate
- all virtual registers in their DECL_RTL's.
-
- If VALID_ONLY, do this only if the resulting address is still valid.
- Otherwise, always do it. */
-
-static void
-instantiate_decls (fndecl, valid_only)
- tree fndecl;
- int valid_only;
-{
- tree decl;
-
- if (DECL_SAVED_INSNS (fndecl))
- /* When compiling an inline function, the obstack used for
- rtl allocation is the maybepermanent_obstack. Calling
- `resume_temporary_allocation' switches us back to that
- obstack while we process this function's parameters. */
- resume_temporary_allocation ();
-
- /* Process all parameters of the function. */
- for (decl = DECL_ARGUMENTS (fndecl); decl; decl = TREE_CHAIN (decl))
- {
- HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (decl));
-
- instantiate_decl (DECL_RTL (decl), size, valid_only);
-
- /* If the parameter was promoted, then the incoming RTL mode may be
- larger than the declared type size. We must use the larger of
- the two sizes. */
- size = MAX (GET_MODE_SIZE (GET_MODE (DECL_INCOMING_RTL (decl))), size);
- instantiate_decl (DECL_INCOMING_RTL (decl), size, valid_only);
- }
-
- /* Now process all variables defined in the function or its subblocks. */
- instantiate_decls_1 (DECL_INITIAL (fndecl), valid_only);
-
- if (DECL_INLINE (fndecl) || DECL_DEFER_OUTPUT (fndecl))
- {
- /* Save all rtl allocated for this function by raising the
- high-water mark on the maybepermanent_obstack. */
- preserve_data ();
- /* All further rtl allocation is now done in the current_obstack. */
- rtl_in_current_obstack ();
- }
-}
-
-/* Subroutine of instantiate_decls: Process all decls in the given
- BLOCK node and all its subblocks. */
-
-static void
-instantiate_decls_1 (let, valid_only)
- tree let;
- int valid_only;
-{
- tree t;
-
- for (t = BLOCK_VARS (let); t; t = TREE_CHAIN (t))
- instantiate_decl (DECL_RTL (t), int_size_in_bytes (TREE_TYPE (t)),
- valid_only);
-
- /* Process all subblocks. */
- for (t = BLOCK_SUBBLOCKS (let); t; t = TREE_CHAIN (t))
- instantiate_decls_1 (t, valid_only);
-}
-
-/* Subroutine of the preceding procedures: Given RTL representing a
- decl and the size of the object, do any instantiation required.
-
- If VALID_ONLY is non-zero, it means that the RTL should only be
- changed if the new address is valid. */
-
-static void
-instantiate_decl (x, size, valid_only)
- rtx x;
- int size;
- int valid_only;
-{
- enum machine_mode mode;
- rtx addr;
-
- /* If this is not a MEM, no need to do anything. Similarly if the
- address is a constant or a register that is not a virtual register. */
-
- if (x == 0 || GET_CODE (x) != MEM)
- return;
-
- addr = XEXP (x, 0);
- if (CONSTANT_P (addr)
- || (GET_CODE (addr) == ADDRESSOF && GET_CODE (XEXP (addr, 0)) == REG)
- || (GET_CODE (addr) == REG
- && (REGNO (addr) < FIRST_VIRTUAL_REGISTER
- || REGNO (addr) > LAST_VIRTUAL_REGISTER)))
- return;
-
- /* If we should only do this if the address is valid, copy the address.
- We need to do this so we can undo any changes that might make the
- address invalid. This copy is unfortunate, but probably can't be
- avoided. */
-
- if (valid_only)
- addr = copy_rtx (addr);
-
- instantiate_virtual_regs_1 (&addr, NULL_RTX, 0);
-
- if (valid_only)
- {
- /* Now verify that the resulting address is valid for every integer or
- floating-point mode up to and including SIZE bytes long. We do this
- since the object might be accessed in any mode and frame addresses
- are shared. */
-
- for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT);
- mode != VOIDmode && GET_MODE_SIZE (mode) <= size;
- mode = GET_MODE_WIDER_MODE (mode))
- if (! memory_address_p (mode, addr))
- return;
-
- for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT);
- mode != VOIDmode && GET_MODE_SIZE (mode) <= size;
- mode = GET_MODE_WIDER_MODE (mode))
- if (! memory_address_p (mode, addr))
- return;
- }
-
- /* Put back the address now that we have updated it and we either know
- it is valid or we don't care whether it is valid. */
-
- XEXP (x, 0) = addr;
-}
-
-/* Given a pointer to a piece of rtx and an optional pointer to the
- containing object, instantiate any virtual registers present in it.
-
- If EXTRA_INSNS, we always do the replacement and generate
- any extra insns before OBJECT. If it zero, we do nothing if replacement
- is not valid.
-
- Return 1 if we either had nothing to do or if we were able to do the
- needed replacement. Return 0 otherwise; we only return zero if
- EXTRA_INSNS is zero.
-
- We first try some simple transformations to avoid the creation of extra
- pseudos. */
-
-static int
-instantiate_virtual_regs_1 (loc, object, extra_insns)
- rtx *loc;
- rtx object;
- int extra_insns;
-{
- rtx x;
- RTX_CODE code;
- rtx new = 0;
- HOST_WIDE_INT offset;
- rtx temp;
- rtx seq;
- int i, j;
- char *fmt;
-
- /* Re-start here to avoid recursion in common cases. */
- restart:
-
- x = *loc;
- if (x == 0)
- return 1;
-
- code = GET_CODE (x);
-
- /* Check for some special cases. */
- switch (code)
- {
- case CONST_INT:
- case CONST_DOUBLE:
- case CONST:
- case SYMBOL_REF:
- case CODE_LABEL:
- case PC:
- case CC0:
- case ASM_INPUT:
- case ADDR_VEC:
- case ADDR_DIFF_VEC:
- case RETURN:
- return 1;
-
- case SET:
- /* We are allowed to set the virtual registers. This means that
- the actual register should receive the source minus the
- appropriate offset. This is used, for example, in the handling
- of non-local gotos. */
- if (SET_DEST (x) == virtual_incoming_args_rtx)
- new = arg_pointer_rtx, offset = - in_arg_offset;
- else if (SET_DEST (x) == virtual_stack_vars_rtx)
- new = frame_pointer_rtx, offset = - var_offset;
- else if (SET_DEST (x) == virtual_stack_dynamic_rtx)
- new = stack_pointer_rtx, offset = - dynamic_offset;
- else if (SET_DEST (x) == virtual_outgoing_args_rtx)
- new = stack_pointer_rtx, offset = - out_arg_offset;
- else if (SET_DEST (x) == virtual_cfa_rtx)
- new = arg_pointer_rtx, offset = - cfa_offset;
-
- if (new)
- {
- /* The only valid sources here are PLUS or REG. Just do
- the simplest possible thing to handle them. */
- if (GET_CODE (SET_SRC (x)) != REG
- && GET_CODE (SET_SRC (x)) != PLUS)
- abort ();
-
- start_sequence ();
- if (GET_CODE (SET_SRC (x)) != REG)
- temp = force_operand (SET_SRC (x), NULL_RTX);
- else
- temp = SET_SRC (x);
- temp = force_operand (plus_constant (temp, offset), NULL_RTX);
- seq = get_insns ();
- end_sequence ();
-
- emit_insns_before (seq, object);
- SET_DEST (x) = new;
-
- if (! validate_change (object, &SET_SRC (x), temp, 0)
- || ! extra_insns)
- abort ();
-
- return 1;
- }
-
- instantiate_virtual_regs_1 (&SET_DEST (x), object, extra_insns);
- loc = &SET_SRC (x);
- goto restart;
-
- case PLUS:
- /* Handle special case of virtual register plus constant. */
- if (CONSTANT_P (XEXP (x, 1)))
- {
- rtx old, new_offset;
-
- /* Check for (plus (plus VIRT foo) (const_int)) first. */
- if (GET_CODE (XEXP (x, 0)) == PLUS)
- {
- rtx inner = XEXP (XEXP (x, 0), 0);
-
- if (inner == virtual_incoming_args_rtx)
- new = arg_pointer_rtx, offset = in_arg_offset;
- else if (inner == virtual_stack_vars_rtx)
- new = frame_pointer_rtx, offset = var_offset;
- else if (inner == virtual_stack_dynamic_rtx)
- new = stack_pointer_rtx, offset = dynamic_offset;
- else if (inner == virtual_outgoing_args_rtx)
- new = stack_pointer_rtx, offset = out_arg_offset;
- else if (inner == virtual_cfa_rtx)
- new = arg_pointer_rtx, offset = cfa_offset;
- else
- {
- loc = &XEXP (x, 0);
- goto restart;
- }
-
- instantiate_virtual_regs_1 (&XEXP (XEXP (x, 0), 1), object,
- extra_insns);
- new = gen_rtx_PLUS (Pmode, new, XEXP (XEXP (x, 0), 1));
- }
-
- else if (XEXP (x, 0) == virtual_incoming_args_rtx)
- new = arg_pointer_rtx, offset = in_arg_offset;
- else if (XEXP (x, 0) == virtual_stack_vars_rtx)
- new = frame_pointer_rtx, offset = var_offset;
- else if (XEXP (x, 0) == virtual_stack_dynamic_rtx)
- new = stack_pointer_rtx, offset = dynamic_offset;
- else if (XEXP (x, 0) == virtual_outgoing_args_rtx)
- new = stack_pointer_rtx, offset = out_arg_offset;
- else if (XEXP (x, 0) == virtual_cfa_rtx)
- new = arg_pointer_rtx, offset = cfa_offset;
- else
- {
- /* We know the second operand is a constant. Unless the
- first operand is a REG (which has been already checked),
- it needs to be checked. */
- if (GET_CODE (XEXP (x, 0)) != REG)
- {
- loc = &XEXP (x, 0);
- goto restart;
- }
- return 1;
- }
-
- new_offset = plus_constant (XEXP (x, 1), offset);
-
- /* If the new constant is zero, try to replace the sum with just
- the register. */
- if (new_offset == const0_rtx
- && validate_change (object, loc, new, 0))
- return 1;
-
- /* Next try to replace the register and new offset.
- There are two changes to validate here and we can't assume that
- in the case of old offset equals new just changing the register
- will yield a valid insn. In the interests of a little efficiency,
- however, we only call validate change once (we don't queue up the
- changes and then call apply_change_group). */
-
- old = XEXP (x, 0);
- if (offset == 0
- ? ! validate_change (object, &XEXP (x, 0), new, 0)
- : (XEXP (x, 0) = new,
- ! validate_change (object, &XEXP (x, 1), new_offset, 0)))
- {
- if (! extra_insns)
- {
- XEXP (x, 0) = old;
- return 0;
- }
-
- /* Otherwise copy the new constant into a register and replace
- constant with that register. */
- temp = gen_reg_rtx (Pmode);
- XEXP (x, 0) = new;
- if (validate_change (object, &XEXP (x, 1), temp, 0))
- emit_insn_before (gen_move_insn (temp, new_offset), object);
- else
- {
- /* If that didn't work, replace this expression with a
- register containing the sum. */
-
- XEXP (x, 0) = old;
- new = gen_rtx_PLUS (Pmode, new, new_offset);
-
- start_sequence ();
- temp = force_operand (new, NULL_RTX);
- seq = get_insns ();
- end_sequence ();
-
- emit_insns_before (seq, object);
- if (! validate_change (object, loc, temp, 0)
- && ! validate_replace_rtx (x, temp, object))
- abort ();
- }
- }
-
- return 1;
- }
-
- /* Fall through to generic two-operand expression case. */
- case EXPR_LIST:
- case CALL:
- case COMPARE:
- case MINUS:
- case MULT:
- case DIV: case UDIV:
- case MOD: case UMOD:
- case AND: case IOR: case XOR:
- case ROTATERT: case ROTATE:
- case ASHIFTRT: case LSHIFTRT: case ASHIFT:
- case NE: case EQ:
- case GE: case GT: case GEU: case GTU:
- case LE: case LT: case LEU: case LTU:
- if (XEXP (x, 1) && ! CONSTANT_P (XEXP (x, 1)))
- instantiate_virtual_regs_1 (&XEXP (x, 1), object, extra_insns);
- loc = &XEXP (x, 0);
- goto restart;
-
- case MEM:
- /* Most cases of MEM that convert to valid addresses have already been
- handled by our scan of decls. The only special handling we
- need here is to make a copy of the rtx to ensure it isn't being
- shared if we have to change it to a pseudo.
-
- If the rtx is a simple reference to an address via a virtual register,
- it can potentially be shared. In such cases, first try to make it
- a valid address, which can also be shared. Otherwise, copy it and
- proceed normally.
-
- First check for common cases that need no processing. These are
- usually due to instantiation already being done on a previous instance
- of a shared rtx. */
-
- temp = XEXP (x, 0);
- if (CONSTANT_ADDRESS_P (temp)
-#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
- || temp == arg_pointer_rtx
-#endif
-#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
- || temp == hard_frame_pointer_rtx
-#endif
- || temp == frame_pointer_rtx)
- return 1;
-
- if (GET_CODE (temp) == PLUS
- && CONSTANT_ADDRESS_P (XEXP (temp, 1))
- && (XEXP (temp, 0) == frame_pointer_rtx
-#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
- || XEXP (temp, 0) == hard_frame_pointer_rtx
-#endif
-#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
- || XEXP (temp, 0) == arg_pointer_rtx
-#endif
- ))
- return 1;
-
- if (temp == virtual_stack_vars_rtx
- || temp == virtual_incoming_args_rtx
- || (GET_CODE (temp) == PLUS
- && CONSTANT_ADDRESS_P (XEXP (temp, 1))
- && (XEXP (temp, 0) == virtual_stack_vars_rtx
- || XEXP (temp, 0) == virtual_incoming_args_rtx)))
- {
- /* This MEM may be shared. If the substitution can be done without
- the need to generate new pseudos, we want to do it in place
- so all copies of the shared rtx benefit. The call below will
- only make substitutions if the resulting address is still
- valid.
-
- Note that we cannot pass X as the object in the recursive call
- since the insn being processed may not allow all valid
- addresses. However, if we were not passed on object, we can
- only modify X without copying it if X will have a valid
- address.
-
- ??? Also note that this can still lose if OBJECT is an insn that
- has less restrictions on an address that some other insn.
- In that case, we will modify the shared address. This case
- doesn't seem very likely, though. One case where this could
- happen is in the case of a USE or CLOBBER reference, but we
- take care of that below. */
-
- if (instantiate_virtual_regs_1 (&XEXP (x, 0),
- object ? object : x, 0))
- return 1;
-
- /* Otherwise make a copy and process that copy. We copy the entire
- RTL expression since it might be a PLUS which could also be
- shared. */
- *loc = x = copy_rtx (x);
- }
-
- /* Fall through to generic unary operation case. */
- case SUBREG:
- case STRICT_LOW_PART:
- case NEG: case NOT:
- case PRE_DEC: case PRE_INC: case POST_DEC: case POST_INC:
- case SIGN_EXTEND: case ZERO_EXTEND:
- case TRUNCATE: case FLOAT_EXTEND: case FLOAT_TRUNCATE:
- case FLOAT: case FIX:
- case UNSIGNED_FIX: case UNSIGNED_FLOAT:
- case ABS:
- case SQRT:
- case FFS:
- /* These case either have just one operand or we know that we need not
- check the rest of the operands. */
- loc = &XEXP (x, 0);
- goto restart;
-
- case USE:
- case CLOBBER:
- /* If the operand is a MEM, see if the change is a valid MEM. If not,
- go ahead and make the invalid one, but do it to a copy. For a REG,
- just make the recursive call, since there's no chance of a problem. */
-
- if ((GET_CODE (XEXP (x, 0)) == MEM
- && instantiate_virtual_regs_1 (&XEXP (XEXP (x, 0), 0), XEXP (x, 0),
- 0))
- || (GET_CODE (XEXP (x, 0)) == REG
- && instantiate_virtual_regs_1 (&XEXP (x, 0), object, 0)))
- return 1;
-
- XEXP (x, 0) = copy_rtx (XEXP (x, 0));
- loc = &XEXP (x, 0);
- goto restart;
-
- case REG:
- /* Try to replace with a PLUS. If that doesn't work, compute the sum
- in front of this insn and substitute the temporary. */
- if (x == virtual_incoming_args_rtx)
- new = arg_pointer_rtx, offset = in_arg_offset;
- else if (x == virtual_stack_vars_rtx)
- new = frame_pointer_rtx, offset = var_offset;
- else if (x == virtual_stack_dynamic_rtx)
- new = stack_pointer_rtx, offset = dynamic_offset;
- else if (x == virtual_outgoing_args_rtx)
- new = stack_pointer_rtx, offset = out_arg_offset;
- else if (x == virtual_cfa_rtx)
- new = arg_pointer_rtx, offset = cfa_offset;
-
- if (new)
- {
- temp = plus_constant (new, offset);
- if (!validate_change (object, loc, temp, 0))
- {
- if (! extra_insns)
- return 0;
-
- start_sequence ();
- temp = force_operand (temp, NULL_RTX);
- seq = get_insns ();
- end_sequence ();
-
- emit_insns_before (seq, object);
- if (! validate_change (object, loc, temp, 0)
- && ! validate_replace_rtx (x, temp, object))
- abort ();
- }
- }
-
- return 1;
-
- case ADDRESSOF:
- if (GET_CODE (XEXP (x, 0)) == REG)
- return 1;
-
- else if (GET_CODE (XEXP (x, 0)) == MEM)
- {
- /* If we have a (addressof (mem ..)), do any instantiation inside
- since we know we'll be making the inside valid when we finally
- remove the ADDRESSOF. */
- instantiate_virtual_regs_1 (&XEXP (XEXP (x, 0), 0), NULL_RTX, 0);
- return 1;
- }
- break;
-
- default:
- break;
- }
-
- /* Scan all subexpressions. */
- fmt = GET_RTX_FORMAT (code);
- for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++)
- if (*fmt == 'e')
- {
- if (!instantiate_virtual_regs_1 (&XEXP (x, i), object, extra_insns))
- return 0;
- }
- else if (*fmt == 'E')
- for (j = 0; j < XVECLEN (x, i); j++)
- if (! instantiate_virtual_regs_1 (&XVECEXP (x, i, j), object,
- extra_insns))
- return 0;
-
- return 1;
-}
-
-/* Optimization: assuming this function does not receive nonlocal gotos,
- delete the handlers for such, as well as the insns to establish
- and disestablish them. */
-
-static void
-delete_handlers ()
-{
- rtx insn;
- for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
- {
- /* Delete the handler by turning off the flag that would
- prevent jump_optimize from deleting it.
- Also permit deletion of the nonlocal labels themselves
- if nothing local refers to them. */
- if (GET_CODE (insn) == CODE_LABEL)
- {
- tree t, last_t;
-
- LABEL_PRESERVE_P (insn) = 0;
-
- /* Remove it from the nonlocal_label list, to avoid confusing
- flow. */
- for (t = nonlocal_labels, last_t = 0; t;
- last_t = t, t = TREE_CHAIN (t))
- if (DECL_RTL (TREE_VALUE (t)) == insn)
- break;
- if (t)
- {
- if (! last_t)
- nonlocal_labels = TREE_CHAIN (nonlocal_labels);
- else
- TREE_CHAIN (last_t) = TREE_CHAIN (t);
- }
- }
- if (GET_CODE (insn) == INSN)
- {
- int can_delete = 0;
- rtx t;
- for (t = nonlocal_goto_handler_slots; t != 0; t = XEXP (t, 1))
- if (reg_mentioned_p (t, PATTERN (insn)))
- {
- can_delete = 1;
- break;
- }
- if (can_delete
- || (nonlocal_goto_stack_level != 0
- && reg_mentioned_p (nonlocal_goto_stack_level,
- PATTERN (insn))))
- delete_insn (insn);
- }
- }
-}
-
-/* Return a list (chain of EXPR_LIST nodes) for the nonlocal labels
- of the current function. */
-
-rtx
-nonlocal_label_rtx_list ()
-{
- tree t;
- rtx x = 0;
-
- for (t = nonlocal_labels; t; t = TREE_CHAIN (t))
- x = gen_rtx_EXPR_LIST (VOIDmode, label_rtx (TREE_VALUE (t)), x);
-
- return x;
-}
-
-/* Output a USE for any register use in RTL.
- This is used with -noreg to mark the extent of lifespan
- of any registers used in a user-visible variable's DECL_RTL. */
-
-void
-use_variable (rtl)
- rtx rtl;
-{
- if (GET_CODE (rtl) == REG)
- /* This is a register variable. */
- emit_insn (gen_rtx_USE (VOIDmode, rtl));
- else if (GET_CODE (rtl) == MEM
- && GET_CODE (XEXP (rtl, 0)) == REG
- && (REGNO (XEXP (rtl, 0)) < FIRST_VIRTUAL_REGISTER
- || REGNO (XEXP (rtl, 0)) > LAST_VIRTUAL_REGISTER)
- && XEXP (rtl, 0) != current_function_internal_arg_pointer)
- /* This is a variable-sized structure. */
- emit_insn (gen_rtx_USE (VOIDmode, XEXP (rtl, 0)));
-}
-
-/* Like use_variable except that it outputs the USEs after INSN
- instead of at the end of the insn-chain. */
-
-void
-use_variable_after (rtl, insn)
- rtx rtl, insn;
-{
- if (GET_CODE (rtl) == REG)
- /* This is a register variable. */
- emit_insn_after (gen_rtx_USE (VOIDmode, rtl), insn);
- else if (GET_CODE (rtl) == MEM
- && GET_CODE (XEXP (rtl, 0)) == REG
- && (REGNO (XEXP (rtl, 0)) < FIRST_VIRTUAL_REGISTER
- || REGNO (XEXP (rtl, 0)) > LAST_VIRTUAL_REGISTER)
- && XEXP (rtl, 0) != current_function_internal_arg_pointer)
- /* This is a variable-sized structure. */
- emit_insn_after (gen_rtx_USE (VOIDmode, XEXP (rtl, 0)), insn);
-}
-
-int
-max_parm_reg_num ()
-{
- return max_parm_reg;
-}
-
-/* Return the first insn following those generated by `assign_parms'. */
-
-rtx
-get_first_nonparm_insn ()
-{
- if (last_parm_insn)
- return NEXT_INSN (last_parm_insn);
- return get_insns ();
-}
-
-/* Return the first NOTE_INSN_BLOCK_BEG note in the function.
- Crash if there is none. */
-
-rtx
-get_first_block_beg ()
-{
- register rtx searcher;
- register rtx insn = get_first_nonparm_insn ();
-
- for (searcher = insn; searcher; searcher = NEXT_INSN (searcher))
- if (GET_CODE (searcher) == NOTE
- && NOTE_LINE_NUMBER (searcher) == NOTE_INSN_BLOCK_BEG)
- return searcher;
-
- abort (); /* Invalid call to this function. (See comments above.) */
- return NULL_RTX;
-}
-
-/* Return 1 if EXP is an aggregate type (or a value with aggregate type).
- This means a type for which function calls must pass an address to the
- function or get an address back from the function.
- EXP may be a type node or an expression (whose type is tested). */
-
-int
-aggregate_value_p (exp)
- tree exp;
-{
- int i, regno, nregs;
- rtx reg;
- tree type;
- if (TREE_CODE_CLASS (TREE_CODE (exp)) == 't')
- type = exp;
- else
- type = TREE_TYPE (exp);
-
- if (RETURN_IN_MEMORY (type))
- return 1;
- /* Types that are TREE_ADDRESSABLE must be constructed in memory,
- and thus can't be returned in registers. */
- if (TREE_ADDRESSABLE (type))
- return 1;
- if (flag_pcc_struct_return && AGGREGATE_TYPE_P (type))
- return 1;
- /* Make sure we have suitable call-clobbered regs to return
- the value in; if not, we must return it in memory. */
- reg = hard_function_value (type, 0);
-
- /* If we have something other than a REG (e.g. a PARALLEL), then assume
- it is OK. */
- if (GET_CODE (reg) != REG)
- return 0;
-
- regno = REGNO (reg);
- nregs = HARD_REGNO_NREGS (regno, TYPE_MODE (type));
- for (i = 0; i < nregs; i++)
- if (! call_used_regs[regno + i])
- return 1;
- return 0;
-}
-
-/* Assign RTL expressions to the function's parameters.
- This may involve copying them into registers and using
- those registers as the RTL for them.
-
- If SECOND_TIME is non-zero it means that this function is being
- called a second time. This is done by integrate.c when a function's
- compilation is deferred. We need to come back here in case the
- FUNCTION_ARG macro computes items needed for the rest of the compilation
- (such as changing which registers are fixed or caller-saved). But suppress
- writing any insns or setting DECL_RTL of anything in this case. */
-
-void
-assign_parms (fndecl, second_time)
- tree fndecl;
- int second_time;
-{
- register tree parm;
- register rtx entry_parm = 0;
- register rtx stack_parm = 0;
- CUMULATIVE_ARGS args_so_far;
- enum machine_mode promoted_mode, passed_mode;
- enum machine_mode nominal_mode, promoted_nominal_mode;
- int unsignedp;
- /* Total space needed so far for args on the stack,
- given as a constant and a tree-expression. */
- struct args_size stack_args_size;
- tree fntype = TREE_TYPE (fndecl);
- tree fnargs = DECL_ARGUMENTS (fndecl);
- /* This is used for the arg pointer when referring to stack args. */
- rtx internal_arg_pointer;
- /* This is a dummy PARM_DECL that we used for the function result if
- the function returns a structure. */
- tree function_result_decl = 0;
- int varargs_setup = 0;
- rtx conversion_insns = 0;
-
- /* Nonzero if the last arg is named `__builtin_va_alist',
- which is used on some machines for old-fashioned non-ANSI varargs.h;
- this should be stuck onto the stack as if it had arrived there. */
- int hide_last_arg
- = (current_function_varargs
- && fnargs
- && (parm = tree_last (fnargs)) != 0
- && DECL_NAME (parm)
- && (! strcmp (IDENTIFIER_POINTER (DECL_NAME (parm)),
- "__builtin_va_alist")));
-
- /* Nonzero if function takes extra anonymous args.
- This means the last named arg must be on the stack
- right before the anonymous ones. */
- int stdarg
- = (TYPE_ARG_TYPES (fntype) != 0
- && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
- != void_type_node));
-
- current_function_stdarg = stdarg;
-
- /* If the reg that the virtual arg pointer will be translated into is
- not a fixed reg or is the stack pointer, make a copy of the virtual
- arg pointer, and address parms via the copy. The frame pointer is
- considered fixed even though it is not marked as such.
-
- The second time through, simply use ap to avoid generating rtx. */
-
- if ((ARG_POINTER_REGNUM == STACK_POINTER_REGNUM
- || ! (fixed_regs[ARG_POINTER_REGNUM]
- || ARG_POINTER_REGNUM == FRAME_POINTER_REGNUM))
- && ! second_time)
- internal_arg_pointer = copy_to_reg (virtual_incoming_args_rtx);
- else
- internal_arg_pointer = virtual_incoming_args_rtx;
- current_function_internal_arg_pointer = internal_arg_pointer;
-
- stack_args_size.constant = 0;
- stack_args_size.var = 0;
-
- /* If struct value address is treated as the first argument, make it so. */
- if (aggregate_value_p (DECL_RESULT (fndecl))
- && ! current_function_returns_pcc_struct
- && struct_value_incoming_rtx == 0)
- {
- tree type = build_pointer_type (TREE_TYPE (fntype));
-
- function_result_decl = build_decl (PARM_DECL, NULL_TREE, type);
-
- DECL_ARG_TYPE (function_result_decl) = type;
- TREE_CHAIN (function_result_decl) = fnargs;
- fnargs = function_result_decl;
- }
-
- max_parm_reg = LAST_VIRTUAL_REGISTER + 1;
- parm_reg_stack_loc = (rtx *) savealloc (max_parm_reg * sizeof (rtx));
- bzero ((char *) parm_reg_stack_loc, max_parm_reg * sizeof (rtx));
-
-#ifdef INIT_CUMULATIVE_INCOMING_ARGS
- INIT_CUMULATIVE_INCOMING_ARGS (args_so_far, fntype, NULL_RTX);
-#else
- INIT_CUMULATIVE_ARGS (args_so_far, fntype, NULL_RTX, 0);
-#endif
-
- /* We haven't yet found an argument that we must push and pretend the
- caller did. */
- current_function_pretend_args_size = 0;
-
- for (parm = fnargs; parm; parm = TREE_CHAIN (parm))
- {
- int aggregate = AGGREGATE_TYPE_P (TREE_TYPE (parm));
- struct args_size stack_offset;
- struct args_size arg_size;
- int passed_pointer = 0;
- int did_conversion = 0;
- tree passed_type = DECL_ARG_TYPE (parm);
- tree nominal_type = TREE_TYPE (parm);
-
- /* Set LAST_NAMED if this is last named arg before some
- anonymous args. */
- int last_named = ((TREE_CHAIN (parm) == 0
- || DECL_NAME (TREE_CHAIN (parm)) == 0)
- && (stdarg || current_function_varargs));
- /* Set NAMED_ARG if this arg should be treated as a named arg. For
- most machines, if this is a varargs/stdarg function, then we treat
- the last named arg as if it were anonymous too. */
- int named_arg = STRICT_ARGUMENT_NAMING ? 1 : ! last_named;
-
- if (TREE_TYPE (parm) == error_mark_node
- /* This can happen after weird syntax errors
- or if an enum type is defined among the parms. */
- || TREE_CODE (parm) != PARM_DECL
- || passed_type == NULL)
- {
- DECL_INCOMING_RTL (parm) = DECL_RTL (parm)
- = gen_rtx_MEM (BLKmode, const0_rtx);
- TREE_USED (parm) = 1;
- continue;
- }
-
- /* For varargs.h function, save info about regs and stack space
- used by the individual args, not including the va_alist arg. */
- if (hide_last_arg && last_named)
- current_function_args_info = args_so_far;
-
- /* Find mode of arg as it is passed, and mode of arg
- as it should be during execution of this function. */
- passed_mode = TYPE_MODE (passed_type);
- nominal_mode = TYPE_MODE (nominal_type);
-
- /* If the parm's mode is VOID, its value doesn't matter,
- and avoid the usual things like emit_move_insn that could crash. */
- if (nominal_mode == VOIDmode)
- {
- DECL_INCOMING_RTL (parm) = DECL_RTL (parm) = const0_rtx;
- continue;
- }
-
- /* If the parm is to be passed as a transparent union, use the
- type of the first field for the tests below. We have already
- verified that the modes are the same. */
- if (DECL_TRANSPARENT_UNION (parm)
- || TYPE_TRANSPARENT_UNION (passed_type))
- passed_type = TREE_TYPE (TYPE_FIELDS (passed_type));
-
- /* See if this arg was passed by invisible reference. It is if
- it is an object whose size depends on the contents of the
- object itself or if the machine requires these objects be passed
- that way. */
-
- if ((TREE_CODE (TYPE_SIZE (passed_type)) != INTEGER_CST
- && contains_placeholder_p (TYPE_SIZE (passed_type)))
- || TREE_ADDRESSABLE (passed_type)
-#ifdef FUNCTION_ARG_PASS_BY_REFERENCE
- || FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, passed_mode,
- passed_type, named_arg)
-#endif
- )
- {
- passed_type = nominal_type = build_pointer_type (passed_type);
- passed_pointer = 1;
- passed_mode = nominal_mode = Pmode;
- }
-
- promoted_mode = passed_mode;
-
-#ifdef PROMOTE_FUNCTION_ARGS
- /* Compute the mode in which the arg is actually extended to. */
- unsignedp = TREE_UNSIGNED (passed_type);
- promoted_mode = promote_mode (passed_type, promoted_mode, &unsignedp, 1);
-#endif
-
- /* Let machine desc say which reg (if any) the parm arrives in.
- 0 means it arrives on the stack. */
-#ifdef FUNCTION_INCOMING_ARG
- entry_parm = FUNCTION_INCOMING_ARG (args_so_far, promoted_mode,
- passed_type, named_arg);
-#else
- entry_parm = FUNCTION_ARG (args_so_far, promoted_mode,
- passed_type, named_arg);
-#endif
-
- if (entry_parm == 0)
- promoted_mode = passed_mode;
-
-#ifdef SETUP_INCOMING_VARARGS
- /* If this is the last named parameter, do any required setup for
- varargs or stdargs. We need to know about the case of this being an
- addressable type, in which case we skip the registers it
- would have arrived in.
-
- For stdargs, LAST_NAMED will be set for two parameters, the one that
- is actually the last named, and the dummy parameter. We only
- want to do this action once.
-
- Also, indicate when RTL generation is to be suppressed. */
- if (last_named && !varargs_setup)
- {
- SETUP_INCOMING_VARARGS (args_so_far, promoted_mode, passed_type,
- current_function_pretend_args_size,
- second_time);
- varargs_setup = 1;
- }
-#endif
-
- /* Determine parm's home in the stack,
- in case it arrives in the stack or we should pretend it did.
-
- Compute the stack position and rtx where the argument arrives
- and its size.
-
- There is one complexity here: If this was a parameter that would
- have been passed in registers, but wasn't only because it is
- __builtin_va_alist, we want locate_and_pad_parm to treat it as if
- it came in a register so that REG_PARM_STACK_SPACE isn't skipped.
- In this case, we call FUNCTION_ARG with NAMED set to 1 instead of
- 0 as it was the previous time. */
-
- locate_and_pad_parm (promoted_mode, passed_type,
-#ifdef STACK_PARMS_IN_REG_PARM_AREA
- 1,
-#else
-#ifdef FUNCTION_INCOMING_ARG
- FUNCTION_INCOMING_ARG (args_so_far, promoted_mode,
- passed_type,
- (named_arg
- || varargs_setup)) != 0,
-#else
- FUNCTION_ARG (args_so_far, promoted_mode,
- passed_type,
- named_arg || varargs_setup) != 0,
-#endif
-#endif
- fndecl, &stack_args_size, &stack_offset, &arg_size);
-
- if (! second_time)
- {
- rtx offset_rtx = ARGS_SIZE_RTX (stack_offset);
-
- if (offset_rtx == const0_rtx)
- stack_parm = gen_rtx_MEM (promoted_mode, internal_arg_pointer);
- else
- stack_parm = gen_rtx_MEM (promoted_mode,
- gen_rtx_PLUS (Pmode,
- internal_arg_pointer,
- offset_rtx));
-
- /* If this is a memory ref that contains aggregate components,
- mark it as such for cse and loop optimize. Likewise if it
- is readonly. */
- MEM_SET_IN_STRUCT_P (stack_parm, aggregate);
- RTX_UNCHANGING_P (stack_parm) = TREE_READONLY (parm);
- MEM_ALIAS_SET (stack_parm) = get_alias_set (parm);
- }
-
- /* If this parameter was passed both in registers and in the stack,
- use the copy on the stack. */
- if (MUST_PASS_IN_STACK (promoted_mode, passed_type))
- entry_parm = 0;
-
-#ifdef FUNCTION_ARG_PARTIAL_NREGS
- /* If this parm was passed part in regs and part in memory,
- pretend it arrived entirely in memory
- by pushing the register-part onto the stack.
-
- In the special case of a DImode or DFmode that is split,
- we could put it together in a pseudoreg directly,
- but for now that's not worth bothering with. */
-
- if (entry_parm)
- {
- int nregs = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, promoted_mode,
- passed_type, named_arg);
-
- if (nregs > 0)
- {
- current_function_pretend_args_size
- = (((nregs * UNITS_PER_WORD) + (PARM_BOUNDARY / BITS_PER_UNIT) - 1)
- / (PARM_BOUNDARY / BITS_PER_UNIT)
- * (PARM_BOUNDARY / BITS_PER_UNIT));
-
- if (! second_time)
- {
- /* Handle calls that pass values in multiple non-contiguous
- locations. The Irix 6 ABI has examples of this. */
- if (GET_CODE (entry_parm) == PARALLEL)
- emit_group_store (validize_mem (stack_parm), entry_parm,
- int_size_in_bytes (TREE_TYPE (parm)),
- (TYPE_ALIGN (TREE_TYPE (parm))
- / BITS_PER_UNIT));
- else
- move_block_from_reg (REGNO (entry_parm),
- validize_mem (stack_parm), nregs,
- int_size_in_bytes (TREE_TYPE (parm)));
- }
- entry_parm = stack_parm;
- }
- }
-#endif
-
- /* If we didn't decide this parm came in a register,
- by default it came on the stack. */
- if (entry_parm == 0)
- entry_parm = stack_parm;
-
- /* Record permanently how this parm was passed. */
- if (! second_time)
- DECL_INCOMING_RTL (parm) = entry_parm;
-
- /* If there is actually space on the stack for this parm,
- count it in stack_args_size; otherwise set stack_parm to 0
- to indicate there is no preallocated stack slot for the parm. */
-
- if (entry_parm == stack_parm
-#if defined (REG_PARM_STACK_SPACE) && ! defined (MAYBE_REG_PARM_STACK_SPACE)
- /* On some machines, even if a parm value arrives in a register
- there is still an (uninitialized) stack slot allocated for it.
-
- ??? When MAYBE_REG_PARM_STACK_SPACE is defined, we can't tell
- whether this parameter already has a stack slot allocated,
- because an arg block exists only if current_function_args_size
- is larger than some threshold, and we haven't calculated that
- yet. So, for now, we just assume that stack slots never exist
- in this case. */
- || REG_PARM_STACK_SPACE (fndecl) > 0
-#endif
- )
- {
- stack_args_size.constant += arg_size.constant;
- if (arg_size.var)
- ADD_PARM_SIZE (stack_args_size, arg_size.var);
- }
- else
- /* No stack slot was pushed for this parm. */
- stack_parm = 0;
-
- /* Update info on where next arg arrives in registers. */
-
- FUNCTION_ARG_ADVANCE (args_so_far, promoted_mode,
- passed_type, named_arg);
-
- /* If this is our second time through, we are done with this parm. */
- if (second_time)
- continue;
-
- /* If we can't trust the parm stack slot to be aligned enough
- for its ultimate type, don't use that slot after entry.
- We'll make another stack slot, if we need one. */
- {
- int thisparm_boundary
- = FUNCTION_ARG_BOUNDARY (promoted_mode, passed_type);
-
- if (GET_MODE_ALIGNMENT (nominal_mode) > thisparm_boundary)
- stack_parm = 0;
- }
-
- /* If parm was passed in memory, and we need to convert it on entry,
- don't store it back in that same slot. */
- if (entry_parm != 0
- && nominal_mode != BLKmode && nominal_mode != passed_mode)
- stack_parm = 0;
-
-#if 0
- /* Now adjust STACK_PARM to the mode and precise location
- where this parameter should live during execution,
- if we discover that it must live in the stack during execution.
- To make debuggers happier on big-endian machines, we store
- the value in the last bytes of the space available. */
-
- if (nominal_mode != BLKmode && nominal_mode != passed_mode
- && stack_parm != 0)
- {
- rtx offset_rtx;
-
- if (BYTES_BIG_ENDIAN
- && GET_MODE_SIZE (nominal_mode) < UNITS_PER_WORD)
- stack_offset.constant += (GET_MODE_SIZE (passed_mode)
- - GET_MODE_SIZE (nominal_mode));
-
- offset_rtx = ARGS_SIZE_RTX (stack_offset);
- if (offset_rtx == const0_rtx)
- stack_parm = gen_rtx_MEM (nominal_mode, internal_arg_pointer);
- else
- stack_parm = gen_rtx_MEM (nominal_mode,
- gen_rtx_PLUS (Pmode,
- internal_arg_pointer,
- offset_rtx));
-
- /* If this is a memory ref that contains aggregate components,
- mark it as such for cse and loop optimize. */
- MEM_SET_IN_STRUCT_P (stack_parm, aggregate);
- }
-#endif /* 0 */
-
-#ifdef STACK_REGS
- /* We need this "use" info, because the gcc-register->stack-register
- converter in reg-stack.c needs to know which registers are active
- at the start of the function call. The actual parameter loading
- instructions are not always available then anymore, since they might
- have been optimised away. */
-
- if (GET_CODE (entry_parm) == REG && !(hide_last_arg && last_named))
- emit_insn (gen_rtx_USE (GET_MODE (entry_parm), entry_parm));
-#endif
-
- /* ENTRY_PARM is an RTX for the parameter as it arrives,
- in the mode in which it arrives.
- STACK_PARM is an RTX for a stack slot where the parameter can live
- during the function (in case we want to put it there).
- STACK_PARM is 0 if no stack slot was pushed for it.
-
- Now output code if necessary to convert ENTRY_PARM to
- the type in which this function declares it,
- and store that result in an appropriate place,
- which may be a pseudo reg, may be STACK_PARM,
- or may be a local stack slot if STACK_PARM is 0.
-
- Set DECL_RTL to that place. */
-
- if (nominal_mode == BLKmode || GET_CODE (entry_parm) == PARALLEL)
- {
- /* If a BLKmode arrives in registers, copy it to a stack slot.
- Handle calls that pass values in multiple non-contiguous
- locations. The Irix 6 ABI has examples of this. */
- if (GET_CODE (entry_parm) == REG
- || GET_CODE (entry_parm) == PARALLEL)
- {
- int size_stored
- = CEIL_ROUND (int_size_in_bytes (TREE_TYPE (parm)),
- UNITS_PER_WORD);
-
- /* Note that we will be storing an integral number of words.
- So we have to be careful to ensure that we allocate an
- integral number of words. We do this below in the
- assign_stack_local if space was not allocated in the argument
- list. If it was, this will not work if PARM_BOUNDARY is not
- a multiple of BITS_PER_WORD. It isn't clear how to fix this
- if it becomes a problem. */
-
- if (stack_parm == 0)
- {
- stack_parm
- = assign_stack_local (GET_MODE (entry_parm),
- size_stored, 0);
-
- /* If this is a memory ref that contains aggregate
- components, mark it as such for cse and loop optimize. */
- MEM_SET_IN_STRUCT_P (stack_parm, aggregate);
- }
-
- else if (PARM_BOUNDARY % BITS_PER_WORD != 0)
- abort ();
-
- if (TREE_READONLY (parm))
- RTX_UNCHANGING_P (stack_parm) = 1;
-
- /* Handle calls that pass values in multiple non-contiguous
- locations. The Irix 6 ABI has examples of this. */
- if (GET_CODE (entry_parm) == PARALLEL)
- emit_group_store (validize_mem (stack_parm), entry_parm,
- int_size_in_bytes (TREE_TYPE (parm)),
- (TYPE_ALIGN (TREE_TYPE (parm))
- / BITS_PER_UNIT));
- else
- move_block_from_reg (REGNO (entry_parm),
- validize_mem (stack_parm),
- size_stored / UNITS_PER_WORD,
- int_size_in_bytes (TREE_TYPE (parm)));
- }
- DECL_RTL (parm) = stack_parm;
- }
- else if (! ((obey_regdecls && ! DECL_REGISTER (parm)
- && ! DECL_INLINE (fndecl))
- /* layout_decl may set this. */
- || TREE_ADDRESSABLE (parm)
- || TREE_SIDE_EFFECTS (parm)
- /* If -ffloat-store specified, don't put explicit
- float variables into registers. */
- || (flag_float_store
- && TREE_CODE (TREE_TYPE (parm)) == REAL_TYPE))
- /* Always assign pseudo to structure return or item passed
- by invisible reference. */
- || passed_pointer || parm == function_result_decl)
- {
- /* Store the parm in a pseudoregister during the function, but we
- may need to do it in a wider mode. */
-
- register rtx parmreg;
- int regno, regnoi = 0, regnor = 0;
-
- unsignedp = TREE_UNSIGNED (TREE_TYPE (parm));
-
- promoted_nominal_mode
- = promote_mode (TREE_TYPE (parm), nominal_mode, &unsignedp, 0);
-
- parmreg = gen_reg_rtx (promoted_nominal_mode);
- mark_user_reg (parmreg);
-
- /* If this was an item that we received a pointer to, set DECL_RTL
- appropriately. */
- if (passed_pointer)
- {
- DECL_RTL (parm)
- = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (passed_type)), parmreg);
- MEM_SET_IN_STRUCT_P (DECL_RTL (parm), aggregate);
- }
- else
- DECL_RTL (parm) = parmreg;
-
- /* Copy the value into the register. */
- if (nominal_mode != passed_mode
- || promoted_nominal_mode != promoted_mode)
- {
- int save_tree_used;
- /* ENTRY_PARM has been converted to PROMOTED_MODE, its
- mode, by the caller. We now have to convert it to
- NOMINAL_MODE, if different. However, PARMREG may be in
- a different mode than NOMINAL_MODE if it is being stored
- promoted.
-
- If ENTRY_PARM is a hard register, it might be in a register
- not valid for operating in its mode (e.g., an odd-numbered
- register for a DFmode). In that case, moves are the only
- thing valid, so we can't do a convert from there. This
- occurs when the calling sequence allow such misaligned
- usages.
-
- In addition, the conversion may involve a call, which could
- clobber parameters which haven't been copied to pseudo
- registers yet. Therefore, we must first copy the parm to
- a pseudo reg here, and save the conversion until after all
- parameters have been moved. */
-
- rtx tempreg = gen_reg_rtx (GET_MODE (entry_parm));
-
- emit_move_insn (tempreg, validize_mem (entry_parm));
-
- push_to_sequence (conversion_insns);
- tempreg = convert_to_mode (nominal_mode, tempreg, unsignedp);
-
- /* TREE_USED gets set erroneously during expand_assignment. */
- save_tree_used = TREE_USED (parm);
- expand_assignment (parm,
- make_tree (nominal_type, tempreg), 0, 0);
- TREE_USED (parm) = save_tree_used;
- conversion_insns = get_insns ();
- did_conversion = 1;
- end_sequence ();
- }
- else
- emit_move_insn (parmreg, validize_mem (entry_parm));
-
- /* If we were passed a pointer but the actual value
- can safely live in a register, put it in one. */
- if (passed_pointer && TYPE_MODE (TREE_TYPE (parm)) != BLKmode
- /* CYGNUS LOCAL -- FUNCTION_ARG_KEEP_AS_REFERENCE/meissner */
-#ifdef FUNCTION_ARG_KEEP_AS_REFERENCE
- && !FUNCTION_ARG_KEEP_AS_REFERENCE (args_so_far, passed_mode,
- passed_type, ! last_named)
-#endif
- /* END CYGNUS LOCAL */
- && ! ((obey_regdecls && ! DECL_REGISTER (parm)
- && ! DECL_INLINE (fndecl))
- /* layout_decl may set this. */
- || TREE_ADDRESSABLE (parm)
- || TREE_SIDE_EFFECTS (parm)
- /* If -ffloat-store specified, don't put explicit
- float variables into registers. */
- || (flag_float_store
- && TREE_CODE (TREE_TYPE (parm)) == REAL_TYPE)))
- {
- /* We can't use nominal_mode, because it will have been set to
- Pmode above. We must use the actual mode of the parm. */
- parmreg = gen_reg_rtx (TYPE_MODE (TREE_TYPE (parm)));
- mark_user_reg (parmreg);
- emit_move_insn (parmreg, DECL_RTL (parm));
- DECL_RTL (parm) = parmreg;
- /* STACK_PARM is the pointer, not the parm, and PARMREG is
- now the parm. */
- stack_parm = 0;
- }
-#ifdef FUNCTION_ARG_CALLEE_COPIES
- /* If we are passed an arg by reference and it is our responsibility
- to make a copy, do it now.
- PASSED_TYPE and PASSED mode now refer to the pointer, not the
- original argument, so we must recreate them in the call to
- FUNCTION_ARG_CALLEE_COPIES. */
- /* ??? Later add code to handle the case that if the argument isn't
- modified, don't do the copy. */
-
- else if (passed_pointer
- && FUNCTION_ARG_CALLEE_COPIES (args_so_far,
- TYPE_MODE (DECL_ARG_TYPE (parm)),
- DECL_ARG_TYPE (parm),
- named_arg)
- && ! TREE_ADDRESSABLE (DECL_ARG_TYPE (parm)))
- {
- rtx copy;
- tree type = DECL_ARG_TYPE (parm);
-
- /* This sequence may involve a library call perhaps clobbering
- registers that haven't been copied to pseudos yet. */
-
- push_to_sequence (conversion_insns);
-
- if (TYPE_SIZE (type) == 0
- || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
- /* This is a variable sized object. */
- copy = gen_rtx_MEM (BLKmode,
- allocate_dynamic_stack_space
- (expr_size (parm), NULL_RTX,
- TYPE_ALIGN (type)));
- else
- copy = assign_stack_temp (TYPE_MODE (type),
- int_size_in_bytes (type), 1);
- MEM_SET_IN_STRUCT_P (copy, AGGREGATE_TYPE_P (type));
- RTX_UNCHANGING_P (copy) = TREE_READONLY (parm);
-
- store_expr (parm, copy, 0);
- emit_move_insn (parmreg, XEXP (copy, 0));
- if (current_function_check_memory_usage)
- emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
- XEXP (copy, 0), ptr_mode,
- GEN_INT (int_size_in_bytes (type)),
- TYPE_MODE (sizetype),
- GEN_INT (MEMORY_USE_RW),
- TYPE_MODE (integer_type_node));
- conversion_insns = get_insns ();
- did_conversion = 1;
- end_sequence ();
- }
-#endif /* FUNCTION_ARG_CALLEE_COPIES */
-
- /* In any case, record the parm's desired stack location
- in case we later discover it must live in the stack.
-
- If it is a COMPLEX value, store the stack location for both
- halves. */
-
- if (GET_CODE (parmreg) == CONCAT)
- regno = MAX (REGNO (XEXP (parmreg, 0)), REGNO (XEXP (parmreg, 1)));
- else
- regno = REGNO (parmreg);
-
- if (regno >= max_parm_reg)
- {
- rtx *new;
- int old_max_parm_reg = max_parm_reg;
-
- /* It's slow to expand this one register at a time,
- but it's also rare and we need max_parm_reg to be
- precisely correct. */
- max_parm_reg = regno + 1;
- new = (rtx *) savealloc (max_parm_reg * sizeof (rtx));
- bcopy ((char *) parm_reg_stack_loc, (char *) new,
- old_max_parm_reg * sizeof (rtx));
- bzero ((char *) (new + old_max_parm_reg),
- (max_parm_reg - old_max_parm_reg) * sizeof (rtx));
- parm_reg_stack_loc = new;
- }
-
- if (GET_CODE (parmreg) == CONCAT)
- {
- enum machine_mode submode = GET_MODE (XEXP (parmreg, 0));
-
- regnor = REGNO (gen_realpart (submode, parmreg));
- regnoi = REGNO (gen_imagpart (submode, parmreg));
-
- if (stack_parm != 0)
- {
- parm_reg_stack_loc[regnor]
- = gen_realpart (submode, stack_parm);
- parm_reg_stack_loc[regnoi]
- = gen_imagpart (submode, stack_parm);
- }
- else
- {
- parm_reg_stack_loc[regnor] = 0;
- parm_reg_stack_loc[regnoi] = 0;
- }
- }
- else
- parm_reg_stack_loc[REGNO (parmreg)] = stack_parm;
-
- /* Mark the register as eliminable if we did no conversion
- and it was copied from memory at a fixed offset,
- and the arg pointer was not copied to a pseudo-reg.
- If the arg pointer is a pseudo reg or the offset formed
- an invalid address, such memory-equivalences
- as we make here would screw up life analysis for it. */
- if (nominal_mode == passed_mode
- && ! did_conversion
- && stack_parm != 0
- && GET_CODE (stack_parm) == MEM
- && stack_offset.var == 0
- && reg_mentioned_p (virtual_incoming_args_rtx,
- XEXP (stack_parm, 0)))
- {
- rtx linsn = get_last_insn ();
- rtx sinsn, set;
-
- /* Mark complex types separately. */
- if (GET_CODE (parmreg) == CONCAT)
- /* Scan backwards for the set of the real and
- imaginary parts. */
- for (sinsn = linsn; sinsn != 0;
- sinsn = prev_nonnote_insn (sinsn))
- {
- set = single_set (sinsn);
- if (set != 0
- && SET_DEST (set) == regno_reg_rtx [regnoi])
- REG_NOTES (sinsn)
- = gen_rtx_EXPR_LIST (REG_EQUIV,
- parm_reg_stack_loc[regnoi],
- REG_NOTES (sinsn));
- else if (set != 0
- && SET_DEST (set) == regno_reg_rtx [regnor])
- REG_NOTES (sinsn)
- = gen_rtx_EXPR_LIST (REG_EQUIV,
- parm_reg_stack_loc[regnor],
- REG_NOTES (sinsn));
- }
- else if ((set = single_set (linsn)) != 0
- && SET_DEST (set) == parmreg)
- REG_NOTES (linsn)
- = gen_rtx_EXPR_LIST (REG_EQUIV,
- stack_parm, REG_NOTES (linsn));
- }
-
- /* For pointer data type, suggest pointer register. */
- if (POINTER_TYPE_P (TREE_TYPE (parm)))
- mark_reg_pointer (parmreg,
- (TYPE_ALIGN (TREE_TYPE (TREE_TYPE (parm)))
- / BITS_PER_UNIT));
- }
- else
- {
- /* Value must be stored in the stack slot STACK_PARM
- during function execution. */
-
- if (promoted_mode != nominal_mode)
- {
- /* Conversion is required. */
- rtx tempreg = gen_reg_rtx (GET_MODE (entry_parm));
-
- emit_move_insn (tempreg, validize_mem (entry_parm));
-
- push_to_sequence (conversion_insns);
- entry_parm = convert_to_mode (nominal_mode, tempreg,
- TREE_UNSIGNED (TREE_TYPE (parm)));
- if (stack_parm)
- {
- /* ??? This may need a big-endian conversion on sparc64. */
- stack_parm = change_address (stack_parm, nominal_mode,
- NULL_RTX);
- }
- conversion_insns = get_insns ();
- did_conversion = 1;
- end_sequence ();
- }
-
- if (entry_parm != stack_parm)
- {
- if (stack_parm == 0)
- {
- stack_parm
- = assign_stack_local (GET_MODE (entry_parm),
- GET_MODE_SIZE (GET_MODE (entry_parm)), 0);
- /* If this is a memory ref that contains aggregate components,
- mark it as such for cse and loop optimize. */
- MEM_SET_IN_STRUCT_P (stack_parm, aggregate);
- }
-
- if (promoted_mode != nominal_mode)
- {
- push_to_sequence (conversion_insns);
- emit_move_insn (validize_mem (stack_parm),
- validize_mem (entry_parm));
- conversion_insns = get_insns ();
- end_sequence ();
- }
- else
- emit_move_insn (validize_mem (stack_parm),
- validize_mem (entry_parm));
- }
- if (current_function_check_memory_usage)
- {
- push_to_sequence (conversion_insns);
- emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
- XEXP (stack_parm, 0), ptr_mode,
- GEN_INT (GET_MODE_SIZE (GET_MODE
- (entry_parm))),
- TYPE_MODE (sizetype),
- GEN_INT (MEMORY_USE_RW),
- TYPE_MODE (integer_type_node));
-
- conversion_insns = get_insns ();
- end_sequence ();
- }
- DECL_RTL (parm) = stack_parm;
- }
-
- /* If this "parameter" was the place where we are receiving the
- function's incoming structure pointer, set up the result. */
- if (parm == function_result_decl)
- {
- tree result = DECL_RESULT (fndecl);
- tree restype = TREE_TYPE (result);
-
- DECL_RTL (result)
- = gen_rtx_MEM (DECL_MODE (result), DECL_RTL (parm));
-
- MEM_SET_IN_STRUCT_P (DECL_RTL (result),
- AGGREGATE_TYPE_P (restype));
- }
-
- if (TREE_THIS_VOLATILE (parm))
- MEM_VOLATILE_P (DECL_RTL (parm)) = 1;
- if (TREE_READONLY (parm))
- RTX_UNCHANGING_P (DECL_RTL (parm)) = 1;
- }
-
- /* Output all parameter conversion instructions (possibly including calls)
- now that all parameters have been copied out of hard registers. */
- emit_insns (conversion_insns);
-
- last_parm_insn = get_last_insn ();
-
- current_function_args_size = stack_args_size.constant;
-
- /* Adjust function incoming argument size for alignment and
- minimum length. */
-
-#ifdef REG_PARM_STACK_SPACE
-#ifndef MAYBE_REG_PARM_STACK_SPACE
- current_function_args_size = MAX (current_function_args_size,
- REG_PARM_STACK_SPACE (fndecl));
-#endif
-#endif
-
-#ifdef PREFERRED_STACK_BOUNDARY
-#define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
-
- current_function_args_size
- = ((current_function_args_size + STACK_BYTES - 1)
- / STACK_BYTES) * STACK_BYTES;
-#endif
-
-#ifdef ARGS_GROW_DOWNWARD
- current_function_arg_offset_rtx
- = (stack_args_size.var == 0 ? GEN_INT (-stack_args_size.constant)
- : expand_expr (size_binop (MINUS_EXPR, stack_args_size.var,
- size_int (-stack_args_size.constant)),
- NULL_RTX, VOIDmode, EXPAND_MEMORY_USE_BAD));
-#else
- current_function_arg_offset_rtx = ARGS_SIZE_RTX (stack_args_size);
-#endif
-
- /* See how many bytes, if any, of its args a function should try to pop
- on return. */
-
- current_function_pops_args = RETURN_POPS_ARGS (fndecl, TREE_TYPE (fndecl),
- current_function_args_size);
-
- /* For stdarg.h function, save info about
- regs and stack space used by the named args. */
-
- if (!hide_last_arg)
- current_function_args_info = args_so_far;
-
- /* Set the rtx used for the function return value. Put this in its
- own variable so any optimizers that need this information don't have
- to include tree.h. Do this here so it gets done when an inlined
- function gets output. */
-
- current_function_return_rtx = DECL_RTL (DECL_RESULT (fndecl));
-}
-
-/* Indicate whether REGNO is an incoming argument to the current function
- that was promoted to a wider mode. If so, return the RTX for the
- register (to get its mode). PMODE and PUNSIGNEDP are set to the mode
- that REGNO is promoted from and whether the promotion was signed or
- unsigned. */
-
-#ifdef PROMOTE_FUNCTION_ARGS
-
-rtx
-promoted_input_arg (regno, pmode, punsignedp)
- int regno;
- enum machine_mode *pmode;
- int *punsignedp;
-{
- tree arg;
-
- for (arg = DECL_ARGUMENTS (current_function_decl); arg;
- arg = TREE_CHAIN (arg))
- if (GET_CODE (DECL_INCOMING_RTL (arg)) == REG
- && REGNO (DECL_INCOMING_RTL (arg)) == regno
- && TYPE_MODE (DECL_ARG_TYPE (arg)) == TYPE_MODE (TREE_TYPE (arg)))
- {
- enum machine_mode mode = TYPE_MODE (TREE_TYPE (arg));
- int unsignedp = TREE_UNSIGNED (TREE_TYPE (arg));
-
- mode = promote_mode (TREE_TYPE (arg), mode, &unsignedp, 1);
- if (mode == GET_MODE (DECL_INCOMING_RTL (arg))
- && mode != DECL_MODE (arg))
- {
- *pmode = DECL_MODE (arg);
- *punsignedp = unsignedp;
- return DECL_INCOMING_RTL (arg);
- }
- }
-
- return 0;
-}
-
-#endif
-
-/* Compute the size and offset from the start of the stacked arguments for a
- parm passed in mode PASSED_MODE and with type TYPE.
-
- INITIAL_OFFSET_PTR points to the current offset into the stacked
- arguments.
-
- The starting offset and size for this parm are returned in *OFFSET_PTR
- and *ARG_SIZE_PTR, respectively.
-
- IN_REGS is non-zero if the argument will be passed in registers. It will
- never be set if REG_PARM_STACK_SPACE is not defined.
-
- FNDECL is the function in which the argument was defined.
-
- There are two types of rounding that are done. The first, controlled by
- FUNCTION_ARG_BOUNDARY, forces the offset from the start of the argument
- list to be aligned to the specific boundary (in bits). This rounding
- affects the initial and starting offsets, but not the argument size.
-
- The second, controlled by FUNCTION_ARG_PADDING and PARM_BOUNDARY,
- optionally rounds the size of the parm to PARM_BOUNDARY. The
- initial offset is not affected by this rounding, while the size always
- is and the starting offset may be. */
-
-/* offset_ptr will be negative for ARGS_GROW_DOWNWARD case;
- initial_offset_ptr is positive because locate_and_pad_parm's
- callers pass in the total size of args so far as
- initial_offset_ptr. arg_size_ptr is always positive.*/
-
-void
-locate_and_pad_parm (passed_mode, type, in_regs, fndecl,
- initial_offset_ptr, offset_ptr, arg_size_ptr)
- enum machine_mode passed_mode;
- tree type;
- int in_regs;
- tree fndecl;
- struct args_size *initial_offset_ptr;
- struct args_size *offset_ptr;
- struct args_size *arg_size_ptr;
-{
- tree sizetree
- = type ? size_in_bytes (type) : size_int (GET_MODE_SIZE (passed_mode));
- enum direction where_pad = FUNCTION_ARG_PADDING (passed_mode, type);
- int boundary = FUNCTION_ARG_BOUNDARY (passed_mode, type);
-
-#ifdef REG_PARM_STACK_SPACE
- /* If we have found a stack parm before we reach the end of the
- area reserved for registers, skip that area. */
- if (! in_regs)
- {
- int reg_parm_stack_space = 0;
-
-#ifdef MAYBE_REG_PARM_STACK_SPACE
- reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE;
-#else
- reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl);
-#endif
- if (reg_parm_stack_space > 0)
- {
- if (initial_offset_ptr->var)
- {
- initial_offset_ptr->var
- = size_binop (MAX_EXPR, ARGS_SIZE_TREE (*initial_offset_ptr),
- size_int (reg_parm_stack_space));
- initial_offset_ptr->constant = 0;
- }
- else if (initial_offset_ptr->constant < reg_parm_stack_space)
- initial_offset_ptr->constant = reg_parm_stack_space;
- }
- }
-#endif /* REG_PARM_STACK_SPACE */
-
- arg_size_ptr->var = 0;
- arg_size_ptr->constant = 0;
-
-#ifdef ARGS_GROW_DOWNWARD
- if (initial_offset_ptr->var)
- {
- offset_ptr->constant = 0;
- offset_ptr->var = size_binop (MINUS_EXPR, integer_zero_node,
- initial_offset_ptr->var);
- }
- else
- {
- offset_ptr->constant = - initial_offset_ptr->constant;
- offset_ptr->var = 0;
- }
- if (where_pad != none
- && (TREE_CODE (sizetree) != INTEGER_CST
- || ((TREE_INT_CST_LOW (sizetree) * BITS_PER_UNIT) % PARM_BOUNDARY)))
- sizetree = round_up (sizetree, PARM_BOUNDARY / BITS_PER_UNIT);
- SUB_PARM_SIZE (*offset_ptr, sizetree);
- if (where_pad != downward)
- pad_to_arg_alignment (offset_ptr, boundary);
- if (initial_offset_ptr->var)
- {
- arg_size_ptr->var = size_binop (MINUS_EXPR,
- size_binop (MINUS_EXPR,
- integer_zero_node,
- initial_offset_ptr->var),
- offset_ptr->var);
- }
- else
- {
- arg_size_ptr->constant = (- initial_offset_ptr->constant
- - offset_ptr->constant);
- }
-#else /* !ARGS_GROW_DOWNWARD */
- pad_to_arg_alignment (initial_offset_ptr, boundary);
- *offset_ptr = *initial_offset_ptr;
-
-#ifdef PUSH_ROUNDING
- if (passed_mode != BLKmode)
- sizetree = size_int (PUSH_ROUNDING (TREE_INT_CST_LOW (sizetree)));
-#endif
-
- /* Pad_below needs the pre-rounded size to know how much to pad below
- so this must be done before rounding up. */
- if (where_pad == downward
- /* However, BLKmode args passed in regs have their padding done elsewhere.
- The stack slot must be able to hold the entire register. */
- && !(in_regs && passed_mode == BLKmode))
- pad_below (offset_ptr, passed_mode, sizetree);
-
- if (where_pad != none
- && (TREE_CODE (sizetree) != INTEGER_CST
- || ((TREE_INT_CST_LOW (sizetree) * BITS_PER_UNIT) % PARM_BOUNDARY)))
- sizetree = round_up (sizetree, PARM_BOUNDARY / BITS_PER_UNIT);
-
- ADD_PARM_SIZE (*arg_size_ptr, sizetree);
-#endif /* ARGS_GROW_DOWNWARD */
-}
-
-/* Round the stack offset in *OFFSET_PTR up to a multiple of BOUNDARY.
- BOUNDARY is measured in bits, but must be a multiple of a storage unit. */
-
-static void
-pad_to_arg_alignment (offset_ptr, boundary)
- struct args_size *offset_ptr;
- int boundary;
-{
- int boundary_in_bytes = boundary / BITS_PER_UNIT;
-
- if (boundary > BITS_PER_UNIT)
- {
- if (offset_ptr->var)
- {
- offset_ptr->var =
-#ifdef ARGS_GROW_DOWNWARD
- round_down
-#else
- round_up
-#endif
- (ARGS_SIZE_TREE (*offset_ptr),
- boundary / BITS_PER_UNIT);
- offset_ptr->constant = 0; /*?*/
- }
- else
- offset_ptr->constant =
-#ifdef ARGS_GROW_DOWNWARD
- FLOOR_ROUND (offset_ptr->constant, boundary_in_bytes);
-#else
- CEIL_ROUND (offset_ptr->constant, boundary_in_bytes);
-#endif
- }
-}
-
-#ifndef ARGS_GROW_DOWNWARD
-static void
-pad_below (offset_ptr, passed_mode, sizetree)
- struct args_size *offset_ptr;
- enum machine_mode passed_mode;
- tree sizetree;
-{
- if (passed_mode != BLKmode)
- {
- if (GET_MODE_BITSIZE (passed_mode) % PARM_BOUNDARY)
- offset_ptr->constant
- += (((GET_MODE_BITSIZE (passed_mode) + PARM_BOUNDARY - 1)
- / PARM_BOUNDARY * PARM_BOUNDARY / BITS_PER_UNIT)
- - GET_MODE_SIZE (passed_mode));
- }
- else
- {
- if (TREE_CODE (sizetree) != INTEGER_CST
- || (TREE_INT_CST_LOW (sizetree) * BITS_PER_UNIT) % PARM_BOUNDARY)
- {
- /* Round the size up to multiple of PARM_BOUNDARY bits. */
- tree s2 = round_up (sizetree, PARM_BOUNDARY / BITS_PER_UNIT);
- /* Add it in. */
- ADD_PARM_SIZE (*offset_ptr, s2);
- SUB_PARM_SIZE (*offset_ptr, sizetree);
- }
- }
-}
-#endif
-
-#ifdef ARGS_GROW_DOWNWARD
-static tree
-round_down (value, divisor)
- tree value;
- int divisor;
-{
- return size_binop (MULT_EXPR,
- size_binop (FLOOR_DIV_EXPR, value, size_int (divisor)),
- size_int (divisor));
-}
-#endif
-
-/* Walk the tree of blocks describing the binding levels within a function
- and warn about uninitialized variables.
- This is done after calling flow_analysis and before global_alloc
- clobbers the pseudo-regs to hard regs. */
-
-void
-uninitialized_vars_warning (block)
- tree block;
-{
- register tree decl, sub;
- for (decl = BLOCK_VARS (block); decl; decl = TREE_CHAIN (decl))
- {
- if (TREE_CODE (decl) == VAR_DECL
- /* These warnings are unreliable for and aggregates
- because assigning the fields one by one can fail to convince
- flow.c that the entire aggregate was initialized.
- Unions are troublesome because members may be shorter. */
- && ! AGGREGATE_TYPE_P (TREE_TYPE (decl))
- && DECL_RTL (decl) != 0
- && GET_CODE (DECL_RTL (decl)) == REG
- /* Global optimizations can make it difficult to determine if a
- particular variable has been initialized. However, a VAR_DECL
- with a nonzero DECL_INITIAL had an initializer, so do not
- claim it is potentially uninitialized.
-
- We do not care about the actual value in DECL_INITIAL, so we do
- not worry that it may be a dangling pointer. */
- && DECL_INITIAL (decl) == NULL_TREE
- && regno_uninitialized (REGNO (DECL_RTL (decl))))
- warning_with_decl (decl,
- "`%s' might be used uninitialized in this function");
- if (TREE_CODE (decl) == VAR_DECL
- && DECL_RTL (decl) != 0
- && GET_CODE (DECL_RTL (decl)) == REG
- && regno_clobbered_at_setjmp (REGNO (DECL_RTL (decl))))
- warning_with_decl (decl,
- "variable `%s' might be clobbered by `longjmp' or `vfork'");
- }
- for (sub = BLOCK_SUBBLOCKS (block); sub; sub = TREE_CHAIN (sub))
- uninitialized_vars_warning (sub);
-}
-
-/* Do the appropriate part of uninitialized_vars_warning
- but for arguments instead of local variables. */
-
-void
-setjmp_args_warning ()
-{
- register tree decl;
- for (decl = DECL_ARGUMENTS (current_function_decl);
- decl; decl = TREE_CHAIN (decl))
- if (DECL_RTL (decl) != 0
- && GET_CODE (DECL_RTL (decl)) == REG
- && regno_clobbered_at_setjmp (REGNO (DECL_RTL (decl))))
- warning_with_decl (decl, "argument `%s' might be clobbered by `longjmp' or `vfork'");
-}
-
-/* If this function call setjmp, put all vars into the stack
- unless they were declared `register'. */
-
-void
-setjmp_protect (block)
- tree block;
-{
- register tree decl, sub;
- for (decl = BLOCK_VARS (block); decl; decl = TREE_CHAIN (decl))
- if ((TREE_CODE (decl) == VAR_DECL
- || TREE_CODE (decl) == PARM_DECL)
- && DECL_RTL (decl) != 0
- && (GET_CODE (DECL_RTL (decl)) == REG
- || (GET_CODE (DECL_RTL (decl)) == MEM
- && GET_CODE (XEXP (DECL_RTL (decl), 0)) == ADDRESSOF))
- /* If this variable came from an inline function, it must be
- that its life doesn't overlap the setjmp. If there was a
- setjmp in the function, it would already be in memory. We
- must exclude such variable because their DECL_RTL might be
- set to strange things such as virtual_stack_vars_rtx. */
- && ! DECL_FROM_INLINE (decl)
- && (
-#ifdef NON_SAVING_SETJMP
- /* If longjmp doesn't restore the registers,
- don't put anything in them. */
- NON_SAVING_SETJMP
- ||
-#endif
- ! DECL_REGISTER (decl)))
- put_var_into_stack (decl);
- for (sub = BLOCK_SUBBLOCKS (block); sub; sub = TREE_CHAIN (sub))
- setjmp_protect (sub);
-}
-
-/* Like the previous function, but for args instead of local variables. */
-
-void
-setjmp_protect_args ()
-{
- register tree decl;
- for (decl = DECL_ARGUMENTS (current_function_decl);
- decl; decl = TREE_CHAIN (decl))
- if ((TREE_CODE (decl) == VAR_DECL
- || TREE_CODE (decl) == PARM_DECL)
- && DECL_RTL (decl) != 0
- && (GET_CODE (DECL_RTL (decl)) == REG
- || (GET_CODE (DECL_RTL (decl)) == MEM
- && GET_CODE (XEXP (DECL_RTL (decl), 0)) == ADDRESSOF))
- && (
- /* If longjmp doesn't restore the registers,
- don't put anything in them. */
-#ifdef NON_SAVING_SETJMP
- NON_SAVING_SETJMP
- ||
-#endif
- ! DECL_REGISTER (decl)))
- put_var_into_stack (decl);
-}
-
-/* Return the context-pointer register corresponding to DECL,
- or 0 if it does not need one. */
-
-rtx
-lookup_static_chain (decl)
- tree decl;
-{
- tree context = decl_function_context (decl);
- tree link;
-
- if (context == 0
- || (TREE_CODE (decl) == FUNCTION_DECL && DECL_NO_STATIC_CHAIN (decl)))
- return 0;
-
- /* We treat inline_function_decl as an alias for the current function
- because that is the inline function whose vars, types, etc.
- are being merged into the current function.
- See expand_inline_function. */
- if (context == current_function_decl || context == inline_function_decl)
- return virtual_stack_vars_rtx;
-
- for (link = context_display; link; link = TREE_CHAIN (link))
- if (TREE_PURPOSE (link) == context)
- return RTL_EXPR_RTL (TREE_VALUE (link));
-
- abort ();
-}
-
-/* Convert a stack slot address ADDR for variable VAR
- (from a containing function)
- into an address valid in this function (using a static chain). */
-
-rtx
-fix_lexical_addr (addr, var)
- rtx addr;
- tree var;
-{
- rtx basereg;
- HOST_WIDE_INT displacement;
- tree context = decl_function_context (var);
- struct function *fp;
- rtx base = 0;
-
- /* If this is the present function, we need not do anything. */
- if (context == current_function_decl || context == inline_function_decl)
- return addr;
-
- for (fp = outer_function_chain; fp; fp = fp->next)
- if (fp->decl == context)
- break;
-
- if (fp == 0)
- abort ();
-
- if (GET_CODE (addr) == ADDRESSOF && GET_CODE (XEXP (addr, 0)) == MEM)
- addr = XEXP (XEXP (addr, 0), 0);
-
- /* Decode given address as base reg plus displacement. */
- if (GET_CODE (addr) == REG)
- basereg = addr, displacement = 0;
- else if (GET_CODE (addr) == PLUS && GET_CODE (XEXP (addr, 1)) == CONST_INT)
- basereg = XEXP (addr, 0), displacement = INTVAL (XEXP (addr, 1));
- else
- abort ();
-
- /* We accept vars reached via the containing function's
- incoming arg pointer and via its stack variables pointer. */
- if (basereg == fp->internal_arg_pointer)
- {
- /* If reached via arg pointer, get the arg pointer value
- out of that function's stack frame.
-
- There are two cases: If a separate ap is needed, allocate a
- slot in the outer function for it and dereference it that way.
- This is correct even if the real ap is actually a pseudo.
- Otherwise, just adjust the offset from the frame pointer to
- compensate. */
-
-#ifdef NEED_SEPARATE_AP
- rtx addr;
-
- if (fp->arg_pointer_save_area == 0)
- fp->arg_pointer_save_area
- = assign_outer_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0, fp);
-
- addr = fix_lexical_addr (XEXP (fp->arg_pointer_save_area, 0), var);
- addr = memory_address (Pmode, addr);
-
- base = copy_to_reg (gen_rtx_MEM (Pmode, addr));
-#else
- displacement += (FIRST_PARM_OFFSET (context) - STARTING_FRAME_OFFSET);
- base = lookup_static_chain (var);
-#endif
- }
-
- else if (basereg == virtual_stack_vars_rtx)
- {
- /* This is the same code as lookup_static_chain, duplicated here to
- avoid an extra call to decl_function_context. */
- tree link;
-
- for (link = context_display; link; link = TREE_CHAIN (link))
- if (TREE_PURPOSE (link) == context)
- {
- base = RTL_EXPR_RTL (TREE_VALUE (link));
- break;
- }
- }
-
- if (base == 0)
- abort ();
-
- /* Use same offset, relative to appropriate static chain or argument
- pointer. */
- return plus_constant (base, displacement);
-}
-
-/* Return the address of the trampoline for entering nested fn FUNCTION.
- If necessary, allocate a trampoline (in the stack frame)
- and emit rtl to initialize its contents (at entry to this function). */
-
-rtx
-trampoline_address (function)
- tree function;
-{
- tree link;
- tree rtlexp;
- rtx tramp;
- struct function *fp;
- tree fn_context;
-
- /* Find an existing trampoline and return it. */
- for (link = trampoline_list; link; link = TREE_CHAIN (link))
- if (TREE_PURPOSE (link) == function)
- return
- round_trampoline_addr (XEXP (RTL_EXPR_RTL (TREE_VALUE (link)), 0));
-
- for (fp = outer_function_chain; fp; fp = fp->next)
- for (link = fp->trampoline_list; link; link = TREE_CHAIN (link))
- if (TREE_PURPOSE (link) == function)
- {
- tramp = fix_lexical_addr (XEXP (RTL_EXPR_RTL (TREE_VALUE (link)), 0),
- function);
- return round_trampoline_addr (tramp);
- }
-
- /* None exists; we must make one. */
-
- /* Find the `struct function' for the function containing FUNCTION. */
- fp = 0;
- fn_context = decl_function_context (function);
- if (fn_context != current_function_decl
- && fn_context != inline_function_decl)
- for (fp = outer_function_chain; fp; fp = fp->next)
- if (fp->decl == fn_context)
- break;
-
- /* Allocate run-time space for this trampoline
- (usually in the defining function's stack frame). */
-#ifdef ALLOCATE_TRAMPOLINE
- tramp = ALLOCATE_TRAMPOLINE (fp);
-#else
- /* If rounding needed, allocate extra space
- to ensure we have TRAMPOLINE_SIZE bytes left after rounding up. */
-#ifdef TRAMPOLINE_ALIGNMENT
-#define TRAMPOLINE_REAL_SIZE \
- (TRAMPOLINE_SIZE + (TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT) - 1)
-#else
-#define TRAMPOLINE_REAL_SIZE (TRAMPOLINE_SIZE)
-#endif
- if (fp != 0)
- tramp = assign_outer_stack_local (BLKmode, TRAMPOLINE_REAL_SIZE, 0, fp);
- else
- tramp = assign_stack_local (BLKmode, TRAMPOLINE_REAL_SIZE, 0);
-#endif
-
- /* Record the trampoline for reuse and note it for later initialization
- by expand_function_end. */
- if (fp != 0)
- {
- push_obstacks (fp->function_maybepermanent_obstack,
- fp->function_maybepermanent_obstack);
- rtlexp = make_node (RTL_EXPR);
- RTL_EXPR_RTL (rtlexp) = tramp;
- fp->trampoline_list = tree_cons (function, rtlexp, fp->trampoline_list);
- pop_obstacks ();
- }
- else
- {
- /* Make the RTL_EXPR node temporary, not momentary, so that the
- trampoline_list doesn't become garbage. */
- int momentary = suspend_momentary ();
- rtlexp = make_node (RTL_EXPR);
- resume_momentary (momentary);
-
- RTL_EXPR_RTL (rtlexp) = tramp;
- trampoline_list = tree_cons (function, rtlexp, trampoline_list);
- }
-
- tramp = fix_lexical_addr (XEXP (tramp, 0), function);
- return round_trampoline_addr (tramp);
-}
-
-/* Given a trampoline address,
- round it to multiple of TRAMPOLINE_ALIGNMENT. */
-
-static rtx
-round_trampoline_addr (tramp)
- rtx tramp;
-{
-#ifdef TRAMPOLINE_ALIGNMENT
- /* Round address up to desired boundary. */
- rtx temp = gen_reg_rtx (Pmode);
- temp = expand_binop (Pmode, add_optab, tramp,
- GEN_INT (TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT - 1),
- temp, 0, OPTAB_LIB_WIDEN);
- tramp = expand_binop (Pmode, and_optab, temp,
- GEN_INT (- TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT),
- temp, 0, OPTAB_LIB_WIDEN);
-#endif
- return tramp;
-}
-
-/* The functions identify_blocks and reorder_blocks provide a way to
- reorder the tree of BLOCK nodes, for optimizers that reshuffle or
- duplicate portions of the RTL code. Call identify_blocks before
- changing the RTL, and call reorder_blocks after. */
-
-/* Put all this function's BLOCK nodes including those that are chained
- onto the first block into a vector, and return it.
- Also store in each NOTE for the beginning or end of a block
- the index of that block in the vector.
- The arguments are BLOCK, the chain of top-level blocks of the function,
- and INSNS, the insn chain of the function. */
-
-tree *
-identify_blocks (block, insns)
- tree block;
- rtx insns;
-{
- int n_blocks;
- tree *block_vector;
- int *block_stack;
- int depth = 0;
- int next_block_number = 1;
- int current_block_number = 1;
- rtx insn;
-
- if (block == 0)
- return 0;
-
- n_blocks = all_blocks (block, 0);
- block_vector = (tree *) xmalloc (n_blocks * sizeof (tree));
- block_stack = (int *) alloca (n_blocks * sizeof (int));
-
- all_blocks (block, block_vector);
-
- for (insn = insns; insn; insn = NEXT_INSN (insn))
- if (GET_CODE (insn) == NOTE)
- {
- if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG)
- {
- block_stack[depth++] = current_block_number;
- current_block_number = next_block_number;
- NOTE_BLOCK_NUMBER (insn) = next_block_number++;
- }
- if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END)
- {
- NOTE_BLOCK_NUMBER (insn) = current_block_number;
- current_block_number = block_stack[--depth];
- }
- }
-
- if (n_blocks != next_block_number)
- abort ();
-
- return block_vector;
-}
-
-/* Given BLOCK_VECTOR which was returned by identify_blocks,
- and a revised instruction chain, rebuild the tree structure
- of BLOCK nodes to correspond to the new order of RTL.
- The new block tree is inserted below TOP_BLOCK.
- Returns the current top-level block. */
-
-tree
-reorder_blocks (block_vector, block, insns)
- tree *block_vector;
- tree block;
- rtx insns;
-{
- tree current_block = block;
- rtx insn;
-
- if (block_vector == 0)
- return block;
-
- /* Prune the old trees away, so that it doesn't get in the way. */
- BLOCK_SUBBLOCKS (current_block) = 0;
- BLOCK_CHAIN (current_block) = 0;
-
- /* CYGNUS LOCAL LRS */
- for (insn = insns; insn; insn = NEXT_INSN (insn))
- {
- tree block, range_start_block = NULL_TREE;
-
- if (GET_CODE (insn) == NOTE)
- switch (NOTE_LINE_NUMBER (insn))
- {
- /* Block beginning, link into block chain */
- case NOTE_INSN_BLOCK_BEG:
- if (NOTE_BLOCK_NUMBER (insn) == NOTE_BLOCK_LIVE_RANGE_BLOCK)
- {
- range_start_block = block = make_node (BLOCK);
- BLOCK_LIVE_RANGE_FLAG (block) = TRUE;
- TREE_USED (block) = TRUE;
- }
- else if (NOTE_BLOCK_NUMBER (insn) <= 0)
- abort ();
- else
- {
- block = block_vector[NOTE_BLOCK_NUMBER (insn)];
- range_start_block = NULL_TREE;
-
- /* If we have seen this block before, copy it. */
- if (TREE_ASM_WRITTEN (block))
- block = copy_node (block);
- }
-
- BLOCK_SUBBLOCKS (block) = 0;
- TREE_ASM_WRITTEN (block) = 1;
- BLOCK_SUPERCONTEXT (block) = current_block;
- BLOCK_CHAIN (block) = BLOCK_SUBBLOCKS (current_block);
- BLOCK_SUBBLOCKS (current_block) = block;
- NOTE_SOURCE_FILE (insn) = 0;
- current_block = block;
- break;
-
- /* Block ending, restore current block, reset block number. */
- case NOTE_INSN_BLOCK_END:
- BLOCK_SUBBLOCKS (current_block)
- = blocks_nreverse (BLOCK_SUBBLOCKS (current_block));
- current_block = BLOCK_SUPERCONTEXT (current_block);
- NOTE_BLOCK_NUMBER (insn) = 0;
- break;
-
- /* Range start, if we created a new block for the range, link
- any new copies into the range. */
- case NOTE_INSN_RANGE_START:
- if (range_start_block)
- {
- rtx ri = NOTE_RANGE_INFO (insn);
- int i;
- for (i = 0; i < (int)RANGE_INFO_NUM_REGS (ri); i++)
- if (RANGE_REG_SYMBOL_NODE (ri, i))
- {
- tree new_sym = copy_node (RANGE_REG_SYMBOL_NODE (ri, i));
- DECL_RTL (new_sym) = regno_reg_rtx[RANGE_REG_COPY (ri, i)];
- TREE_CHAIN (new_sym) = BLOCK_VARS (range_start_block);
- BLOCK_VARS (range_start_block) = new_sym;
- RANGE_REG_SYMBOL_NODE (ri, i) = new_sym;
- RANGE_REG_BLOCK_NODE (ri, i) = range_start_block;
- }
- }
- break;
- }
- }
- /* END CYGNUS LOCAL */
-
- BLOCK_SUBBLOCKS (current_block)
- = blocks_nreverse (BLOCK_SUBBLOCKS (current_block));
- return current_block;
-}
-
-/* Reverse the order of elements in the chain T of blocks,
- and return the new head of the chain (old last element). */
-
-static tree
-blocks_nreverse (t)
- tree t;
-{
- register tree prev = 0, decl, next;
- for (decl = t; decl; decl = next)
- {
- next = BLOCK_CHAIN (decl);
- BLOCK_CHAIN (decl) = prev;
- prev = decl;
- }
- return prev;
-}
-
-/* Count the subblocks of the list starting with BLOCK, and list them
- all into the vector VECTOR. Also clear TREE_ASM_WRITTEN in all
- blocks. */
-
-static int
-all_blocks (block, vector)
- tree block;
- tree *vector;
-{
- int n_blocks = 0;
-
- while (block)
- {
- TREE_ASM_WRITTEN (block) = 0;
-
- /* Record this block. */
- if (vector)
- vector[n_blocks] = block;
-
- ++n_blocks;
-
- /* Record the subblocks, and their subblocks... */
- n_blocks += all_blocks (BLOCK_SUBBLOCKS (block),
- vector ? vector + n_blocks : 0);
- block = BLOCK_CHAIN (block);
- }
-
- return n_blocks;
-}
-
-/* Generate RTL for the start of the function SUBR (a FUNCTION_DECL tree node)
- and initialize static variables for generating RTL for the statements
- of the function. */
-
-void
-init_function_start (subr, filename, line)
- tree subr;
- char *filename;
- int line;
-{
- init_stmt_for_function ();
-
- cse_not_expected = ! optimize;
-
- /* Caller save not needed yet. */
- caller_save_needed = 0;
-
- /* No stack slots have been made yet. */
- stack_slot_list = 0;
-
- /* There is no stack slot for handling nonlocal gotos. */
- nonlocal_goto_handler_slots = 0;
- nonlocal_goto_stack_level = 0;
-
- /* No labels have been declared for nonlocal use. */
- nonlocal_labels = 0;
-
- /* No function calls so far in this function. */
- function_call_count = 0;
-
- /* No parm regs have been allocated.
- (This is important for output_inline_function.) */
- max_parm_reg = LAST_VIRTUAL_REGISTER + 1;
-
- /* Initialize the RTL mechanism. */
- init_emit ();
-
- /* Initialize the queue of pending postincrement and postdecrements,
- and some other info in expr.c. */
- init_expr ();
-
- /* We haven't done register allocation yet. */
- reg_renumber = 0;
-
- init_const_rtx_hash_table ();
-
- current_function_name = (*decl_printable_name) (subr, 2);
-
- /* Nonzero if this is a nested function that uses a static chain. */
-
- current_function_needs_context
- = (decl_function_context (current_function_decl) != 0
- && ! DECL_NO_STATIC_CHAIN (current_function_decl));
-
- /* Set if a call to setjmp is seen. */
- current_function_calls_setjmp = 0;
-
- /* Set if a call to longjmp is seen. */
- current_function_calls_longjmp = 0;
-
- current_function_calls_alloca = 0;
- current_function_has_nonlocal_label = 0;
- current_function_has_nonlocal_goto = 0;
- current_function_contains_functions = 0;
- current_function_sp_is_unchanging = 0;
- current_function_is_thunk = 0;
-
- current_function_returns_pcc_struct = 0;
- current_function_returns_struct = 0;
- current_function_epilogue_delay_list = 0;
- current_function_uses_const_pool = 0;
- current_function_uses_pic_offset_table = 0;
- current_function_cannot_inline = 0;
- /* CYGNUS LOCAL -- Branch Prediction */
- current_function_uses_expect = 0;
- current_function_processing_expect = 0;
- /* END CYGNUS LOCAL -- Branch Prediction */
-
- /* We have not yet needed to make a label to jump to for tail-recursion. */
- tail_recursion_label = 0;
-
- /* We haven't had a need to make a save area for ap yet. */
-
- arg_pointer_save_area = 0;
-
- /* No stack slots allocated yet. */
- frame_offset = 0;
-
- /* No SAVE_EXPRs in this function yet. */
- save_expr_regs = 0;
-
- /* No RTL_EXPRs in this function yet. */
- rtl_expr_chain = 0;
-
- /* Set up to allocate temporaries. */
- init_temp_slots ();
-
- /* Within function body, compute a type's size as soon it is laid out. */
- immediate_size_expand++;
-
- /* We haven't made any trampolines for this function yet. */
- trampoline_list = 0;
-
- init_pending_stack_adjust ();
- inhibit_defer_pop = 0;
-
- current_function_outgoing_args_size = 0;
-
- /* Prevent ever trying to delete the first instruction of a function.
- Also tell final how to output a linenum before the function prologue.
- Note linenums could be missing, e.g. when compiling a Java .class file. */
- if (line > 0)
- emit_line_note (filename, line);
-
- /* Make sure first insn is a note even if we don't want linenums.
- This makes sure the first insn will never be deleted.
- Also, final expects a note to appear there. */
- emit_note (NULL_PTR, NOTE_INSN_DELETED);
-
- /* Set flags used by final.c. */
- if (aggregate_value_p (DECL_RESULT (subr)))
- {
-#ifdef PCC_STATIC_STRUCT_RETURN
- current_function_returns_pcc_struct = 1;
-#endif
- current_function_returns_struct = 1;
- }
-
- /* Warn if this value is an aggregate type,
- regardless of which calling convention we are using for it. */
- if (warn_aggregate_return
- && AGGREGATE_TYPE_P (TREE_TYPE (DECL_RESULT (subr))))
- warning ("function returns an aggregate");
-
- current_function_returns_pointer
- = POINTER_TYPE_P (TREE_TYPE (DECL_RESULT (subr)));
-
- /* Indicate that we need to distinguish between the return value of the
- present function and the return value of a function being called. */
- rtx_equal_function_value_matters = 1;
-
- /* Indicate that we have not instantiated virtual registers yet. */
- virtuals_instantiated = 0;
-
- /* Indicate we have no need of a frame pointer yet. */
- frame_pointer_needed = 0;
-
- /* By default assume not varargs or stdarg. */
- current_function_varargs = 0;
- current_function_stdarg = 0;
-}
-
-/* Indicate that the current function uses extra args
- not explicitly mentioned in the argument list in any fashion. */
-
-void
-mark_varargs ()
-{
- current_function_varargs = 1;
-}
-
-/* Expand a call to __main at the beginning of a possible main function. */
-
-#if defined(INIT_SECTION_ASM_OP) && !defined(INVOKE__main)
-#undef HAS_INIT_SECTION
-#define HAS_INIT_SECTION
-#endif
-
-void
-expand_main_function ()
-{
-#if !defined (HAS_INIT_SECTION)
- emit_library_call (gen_rtx_SYMBOL_REF (Pmode, NAME__MAIN), 0,
- VOIDmode, 0);
-#endif /* not HAS_INIT_SECTION */
-}
-
-extern struct obstack permanent_obstack;
-
-/* Start the RTL for a new function, and set variables used for
- emitting RTL.
- SUBR is the FUNCTION_DECL node.
- PARMS_HAVE_CLEANUPS is nonzero if there are cleanups associated with
- the function's parameters, which must be run at any return statement. */
-
-void
-expand_function_start (subr, parms_have_cleanups)
- tree subr;
- int parms_have_cleanups;
-{
- register int i;
- tree tem;
- rtx last_ptr = NULL_RTX;
-
- /* Make sure volatile mem refs aren't considered
- valid operands of arithmetic insns. */
- init_recog_no_volatile ();
-
- /* Set this before generating any memory accesses. */
- current_function_check_memory_usage
- = (flag_check_memory_usage
- && ! DECL_NO_CHECK_MEMORY_USAGE (current_function_decl));
-
- current_function_instrument_entry_exit
- = (flag_instrument_function_entry_exit
- && ! DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (subr));
-
- /* If function gets a static chain arg, store it in the stack frame.
- Do this first, so it gets the first stack slot offset. */
- if (current_function_needs_context)
- {
- last_ptr = assign_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0);
-
- /* Delay copying static chain if it is not a register to avoid
- conflicts with regs used for parameters. */
- if (! SMALL_REGISTER_CLASSES
- || GET_CODE (static_chain_incoming_rtx) == REG)
- emit_move_insn (last_ptr, static_chain_incoming_rtx);
- }
-
- /* If the parameters of this function need cleaning up, get a label
- for the beginning of the code which executes those cleanups. This must
- be done before doing anything with return_label. */
- if (parms_have_cleanups)
- cleanup_label = gen_label_rtx ();
- else
- cleanup_label = 0;
-
- /* Make the label for return statements to jump to, if this machine
- does not have a one-instruction return and uses an epilogue,
- or if it returns a structure, or if it has parm cleanups. */
-#ifdef HAVE_return
- if (cleanup_label == 0 && HAVE_return
- && ! current_function_instrument_entry_exit
- && ! current_function_returns_pcc_struct
- && ! (current_function_returns_struct && ! optimize))
- return_label = 0;
- else
- return_label = gen_label_rtx ();
-#else
- return_label = gen_label_rtx ();
-#endif
-
- /* Initialize rtx used to return the value. */
- /* Do this before assign_parms so that we copy the struct value address
- before any library calls that assign parms might generate. */
-
- /* Decide whether to return the value in memory or in a register. */
- if (aggregate_value_p (DECL_RESULT (subr)))
- {
- /* Returning something that won't go in a register. */
- register rtx value_address = 0;
-
-#ifdef PCC_STATIC_STRUCT_RETURN
- if (current_function_returns_pcc_struct)
- {
- int size = int_size_in_bytes (TREE_TYPE (DECL_RESULT (subr)));
- value_address = assemble_static_space (size);
- }
- else
-#endif
- {
- /* Expect to be passed the address of a place to store the value.
- If it is passed as an argument, assign_parms will take care of
- it. */
- if (struct_value_incoming_rtx)
- {
- value_address = gen_reg_rtx (Pmode);
- emit_move_insn (value_address, struct_value_incoming_rtx);
- }
- }
- if (value_address)
- {
- DECL_RTL (DECL_RESULT (subr))
- = gen_rtx_MEM (DECL_MODE (DECL_RESULT (subr)), value_address);
- MEM_SET_IN_STRUCT_P (DECL_RTL (DECL_RESULT (subr)),
- AGGREGATE_TYPE_P (TREE_TYPE
- (DECL_RESULT
- (subr))));
- }
- }
- else if (DECL_MODE (DECL_RESULT (subr)) == VOIDmode)
- /* If return mode is void, this decl rtl should not be used. */
- DECL_RTL (DECL_RESULT (subr)) = 0;
- else if (parms_have_cleanups || current_function_instrument_entry_exit)
- {
- /* If function will end with cleanup code for parms,
- compute the return values into a pseudo reg,
- which we will copy into the true return register
- after the cleanups are done. */
-
- enum machine_mode mode = DECL_MODE (DECL_RESULT (subr));
-
-#ifdef PROMOTE_FUNCTION_RETURN
- tree type = TREE_TYPE (DECL_RESULT (subr));
- int unsignedp = TREE_UNSIGNED (type);
-
- mode = promote_mode (type, mode, &unsignedp, 1);
-#endif
-
- DECL_RTL (DECL_RESULT (subr)) = gen_reg_rtx (mode);
- }
- else
- /* Scalar, returned in a register. */
- {
-#ifdef FUNCTION_OUTGOING_VALUE
- DECL_RTL (DECL_RESULT (subr))
- = FUNCTION_OUTGOING_VALUE (TREE_TYPE (DECL_RESULT (subr)), subr);
-#else
- DECL_RTL (DECL_RESULT (subr))
- = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (subr)), subr);
-#endif
-
- /* Mark this reg as the function's return value. */
- if (GET_CODE (DECL_RTL (DECL_RESULT (subr))) == REG)
- {
- REG_FUNCTION_VALUE_P (DECL_RTL (DECL_RESULT (subr))) = 1;
- /* Needed because we may need to move this to memory
- in case it's a named return value whose address is taken. */
- DECL_REGISTER (DECL_RESULT (subr)) = 1;
- }
- }
-
- /* Initialize rtx for parameters and local variables.
- In some cases this requires emitting insns. */
-
- assign_parms (subr, 0);
-
- /* Copy the static chain now if it wasn't a register. The delay is to
- avoid conflicts with the parameter passing registers. */
-
- if (SMALL_REGISTER_CLASSES && current_function_needs_context)
- if (GET_CODE (static_chain_incoming_rtx) != REG)
- emit_move_insn (last_ptr, static_chain_incoming_rtx);
-
- /* The following was moved from init_function_start.
- The move is supposed to make sdb output more accurate. */
- /* Indicate the beginning of the function body,
- as opposed to parm setup. */
- emit_note (NULL_PTR, NOTE_INSN_FUNCTION_BEG);
-
- /* If doing stupid allocation, mark parms as born here. */
-
- if (GET_CODE (get_last_insn ()) != NOTE)
- emit_note (NULL_PTR, NOTE_INSN_DELETED);
- parm_birth_insn = get_last_insn ();
-
- if (obey_regdecls)
- {
- for (i = LAST_VIRTUAL_REGISTER + 1; i < max_parm_reg; i++)
- use_variable (regno_reg_rtx[i]);
-
- if (current_function_internal_arg_pointer != virtual_incoming_args_rtx)
- use_variable (current_function_internal_arg_pointer);
- }
-
- context_display = 0;
- if (current_function_needs_context)
- {
- /* Fetch static chain values for containing functions. */
- tem = decl_function_context (current_function_decl);
- /* If not doing stupid register allocation copy the static chain
- pointer into a pseudo. If we have small register classes, copy
- the value from memory if static_chain_incoming_rtx is a REG. If
- we do stupid register allocation, we use the stack address
- generated above. */
- if (tem && ! obey_regdecls)
- {
- /* If the static chain originally came in a register, put it back
- there, then move it out in the next insn. The reason for
- this peculiar code is to satisfy function integration. */
- if (SMALL_REGISTER_CLASSES
- && GET_CODE (static_chain_incoming_rtx) == REG)
- emit_move_insn (static_chain_incoming_rtx, last_ptr);
- last_ptr = copy_to_reg (static_chain_incoming_rtx);
- }
-
- while (tem)
- {
- tree rtlexp = make_node (RTL_EXPR);
-
- RTL_EXPR_RTL (rtlexp) = last_ptr;
- context_display = tree_cons (tem, rtlexp, context_display);
- tem = decl_function_context (tem);
- if (tem == 0)
- break;
- /* Chain thru stack frames, assuming pointer to next lexical frame
- is found at the place we always store it. */
-#ifdef FRAME_GROWS_DOWNWARD
- last_ptr = plus_constant (last_ptr, - GET_MODE_SIZE (Pmode));
-#endif
- last_ptr = copy_to_reg (gen_rtx_MEM (Pmode,
- memory_address (Pmode, last_ptr)));
-
- /* If we are not optimizing, ensure that we know that this
- piece of context is live over the entire function. */
- if (! optimize)
- save_expr_regs = gen_rtx_EXPR_LIST (VOIDmode, last_ptr,
- save_expr_regs);
- }
- }
-
- if (current_function_instrument_entry_exit)
- {
- rtx fun = DECL_RTL (current_function_decl);
- if (GET_CODE (fun) == MEM)
- fun = XEXP (fun, 0);
- else
- abort ();
- emit_library_call (profile_function_entry_libfunc, 0, VOIDmode, 2,
- fun, Pmode,
- expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS,
- 0,
- hard_frame_pointer_rtx),
- Pmode);
- }
-
- /* After the display initializations is where the tail-recursion label
- should go, if we end up needing one. Ensure we have a NOTE here
- since some things (like trampolines) get placed before this. */
- tail_recursion_reentry = emit_note (NULL_PTR, NOTE_INSN_DELETED);
-
- /* Evaluate now the sizes of any types declared among the arguments. */
- for (tem = nreverse (get_pending_sizes ()); tem; tem = TREE_CHAIN (tem))
- {
- expand_expr (TREE_VALUE (tem), const0_rtx, VOIDmode,
- EXPAND_MEMORY_USE_BAD);
- /* Flush the queue in case this parameter declaration has
- side-effects. */
- emit_queue ();
- }
-
- /* Make sure there is a line number after the function entry setup code. */
- force_next_line_note ();
-}
-
-/* Call DOIT for each hard register used as a return value from
- the current function. */
-
-static void
-diddle_return_value (doit, arg)
- void (*doit) PARAMS ((rtx, void *));
- void *arg;
-{
- rtx outgoing = current_function_return_rtx;
- int pcc;
-
- if (! outgoing)
- return;
-
- pcc = (current_function_returns_struct
- || current_function_returns_pcc_struct);
-
- if ((GET_CODE (outgoing) == REG
- && REGNO (outgoing) >= FIRST_PSEUDO_REGISTER)
- || pcc)
- {
- tree type = TREE_TYPE (DECL_RESULT (current_function_decl));
-
- /* A PCC-style return returns a pointer to the memory in which
- the structure is stored. */
- if (pcc)
- type = build_pointer_type (type);
-
-#ifdef FUNCTION_OUTGOING_VALUE
- outgoing = FUNCTION_OUTGOING_VALUE (type, current_function_decl);
-#else
- outgoing = FUNCTION_VALUE (type, current_function_decl);
-#endif
- /* If this is a BLKmode structure being returned in registers, then use
- the mode computed in expand_return. */
- if (GET_MODE (outgoing) == BLKmode)
- PUT_MODE (outgoing, GET_MODE (current_function_return_rtx));
- REG_FUNCTION_VALUE_P (outgoing) = 1;
- }
-
- if (GET_CODE (outgoing) == REG)
- (*doit) (outgoing, arg);
- else if (GET_CODE (outgoing) == PARALLEL)
- {
- int i;
-
- for (i = 0; i < XVECLEN (outgoing, 0); i++)
- {
- rtx x = XEXP (XVECEXP (outgoing, 0, i), 0);
-
- if (GET_CODE (x) == REG && REGNO (x) < FIRST_PSEUDO_REGISTER)
- (*doit) (x, arg);
- }
- }
-}
-
-static void
-do_use_return_reg (reg, arg)
- rtx reg;
- void *arg ATTRIBUTE_UNUSED;
-{
- emit_insn (gen_rtx_USE (VOIDmode, reg));
-}
-
-static void
-use_return_register ()
-{
- diddle_return_value (do_use_return_reg, NULL);
-}
-
-/* Generate RTL for the end of the current function.
- FILENAME and LINE are the current position in the source file.
-
- It is up to language-specific callers to do cleanups for parameters--
- or else, supply 1 for END_BINDINGS and we will call expand_end_bindings. */
-
-void
-expand_function_end (filename, line, end_bindings)
- char *filename;
- int line;
- int end_bindings;
-{
- register int i;
- tree link;
-
-#ifdef TRAMPOLINE_TEMPLATE
- static rtx initial_trampoline;
-#endif
-
-#ifdef NON_SAVING_SETJMP
- /* Don't put any variables in registers if we call setjmp
- on a machine that fails to restore the registers. */
- if (NON_SAVING_SETJMP && current_function_calls_setjmp)
- {
- if (DECL_INITIAL (current_function_decl) != error_mark_node)
- setjmp_protect (DECL_INITIAL (current_function_decl));
-
- setjmp_protect_args ();
- }
-#endif
-
- /* Save the argument pointer if a save area was made for it. */
- if (arg_pointer_save_area)
- {
- /* arg_pointer_save_area may not be a valid memory address, so we
- have to check it and fix it if necessary. */
- rtx seq;
- start_sequence ();
- emit_move_insn (validize_mem (arg_pointer_save_area),
- virtual_incoming_args_rtx);
- seq = gen_sequence ();
- end_sequence ();
- emit_insn_before (seq, tail_recursion_reentry);
- }
-
- /* Initialize any trampolines required by this function. */
- for (link = trampoline_list; link; link = TREE_CHAIN (link))
- {
- tree function = TREE_PURPOSE (link);
- rtx context = lookup_static_chain (function);
- rtx tramp = RTL_EXPR_RTL (TREE_VALUE (link));
-#ifdef TRAMPOLINE_TEMPLATE
- rtx blktramp;
-#endif
- rtx seq;
-
-#ifdef TRAMPOLINE_TEMPLATE
- /* First make sure this compilation has a template for
- initializing trampolines. */
- if (initial_trampoline == 0)
- {
- end_temporary_allocation ();
- initial_trampoline
- = gen_rtx_MEM (BLKmode, assemble_trampoline_template ());
- resume_temporary_allocation ();
- }
-#endif
-
- /* Generate insns to initialize the trampoline. */
- start_sequence ();
- tramp = round_trampoline_addr (XEXP (tramp, 0));
-#ifdef TRAMPOLINE_TEMPLATE
- blktramp = change_address (initial_trampoline, BLKmode, tramp);
- emit_block_move (blktramp, initial_trampoline,
- GEN_INT (TRAMPOLINE_SIZE),
- TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT);
-#endif
- INITIALIZE_TRAMPOLINE (tramp, XEXP (DECL_RTL (function), 0), context);
- seq = get_insns ();
- end_sequence ();
-
- /* Put those insns at entry to the containing function (this one). */
- emit_insns_before (seq, tail_recursion_reentry);
- }
-
- /* If we are doing stack checking and this function makes calls,
- do a stack probe at the start of the function to ensure we have enough
- space for another stack frame. */
- if (flag_stack_check && ! STACK_CHECK_BUILTIN)
- {
- rtx insn, seq;
-
- for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
- if (GET_CODE (insn) == CALL_INSN)
- {
- start_sequence ();
- probe_stack_range (STACK_CHECK_PROTECT,
- GEN_INT (STACK_CHECK_MAX_FRAME_SIZE));
- seq = get_insns ();
- end_sequence ();
- emit_insns_before (seq, tail_recursion_reentry);
- break;
- }
- }
-
- /* Warn about unused parms if extra warnings were specified. */
- if (warn_unused && extra_warnings)
- {
- tree decl;
-
- for (decl = DECL_ARGUMENTS (current_function_decl);
- decl; decl = TREE_CHAIN (decl))
- if (! TREE_USED (decl) && TREE_CODE (decl) == PARM_DECL
- && DECL_NAME (decl) && ! DECL_ARTIFICIAL (decl))
- warning_with_decl (decl, "unused parameter `%s'");
- }
-
- /* Delete handlers for nonlocal gotos if nothing uses them. */
- if (nonlocal_goto_handler_slots != 0
- && ! current_function_has_nonlocal_label)
- delete_handlers ();
-
- /* End any sequences that failed to be closed due to syntax errors. */
- while (in_sequence_p ())
- end_sequence ();
-
- /* Outside function body, can't compute type's actual size
- until next function's body starts. */
- immediate_size_expand--;
-
- /* If doing stupid register allocation,
- mark register parms as dying here. */
-
- if (obey_regdecls)
- {
- rtx tem;
- for (i = LAST_VIRTUAL_REGISTER + 1; i < max_parm_reg; i++)
- use_variable (regno_reg_rtx[i]);
-
- /* Likewise for the regs of all the SAVE_EXPRs in the function. */
-
- for (tem = save_expr_regs; tem; tem = XEXP (tem, 1))
- {
- use_variable (XEXP (tem, 0));
- use_variable_after (XEXP (tem, 0), parm_birth_insn);
- }
-
- if (current_function_internal_arg_pointer != virtual_incoming_args_rtx)
- use_variable (current_function_internal_arg_pointer);
- }
-
- clear_pending_stack_adjust ();
- do_pending_stack_adjust ();
-
- /* Mark the end of the function body.
- If control reaches this insn, the function can drop through
- without returning a value. */
- emit_note (NULL_PTR, NOTE_INSN_FUNCTION_END);
-
- /* Output a linenumber for the end of the function.
- SDB depends on this. */
- emit_line_note_force (filename, line);
-
- /* Output the label for the actual return from the function,
- if one is expected. This happens either because a function epilogue
- is used instead of a return instruction, or because a return was done
- with a goto in order to run local cleanups, or because of pcc-style
- structure returning. */
-
- if (return_label)
- emit_label (return_label);
-
- /* C++ uses this. */
- if (end_bindings)
- expand_end_bindings (0, 0, 0);
-
- /* Now handle any leftover exception regions that may have been
- created for the parameters. */
- {
- rtx last = get_last_insn ();
- rtx label;
-
- expand_leftover_cleanups ();
-
- /* If the above emitted any code, may sure we jump around it. */
- if (last != get_last_insn ())
- {
- label = gen_label_rtx ();
- last = emit_jump_insn_after (gen_jump (label), last);
- last = emit_barrier_after (last);
- emit_label (label);
- }
- }
-
- if (current_function_instrument_entry_exit)
- {
- rtx fun = DECL_RTL (current_function_decl);
- if (GET_CODE (fun) == MEM)
- fun = XEXP (fun, 0);
- else
- abort ();
- emit_library_call (profile_function_exit_libfunc, 0, VOIDmode, 2,
- fun, Pmode,
- expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS,
- 0,
- hard_frame_pointer_rtx),
- Pmode);
- }
-
- /* If we had calls to alloca, and this machine needs
- an accurate stack pointer to exit the function,
- insert some code to save and restore the stack pointer. */
-#ifdef EXIT_IGNORE_STACK
- if (! EXIT_IGNORE_STACK)
-#endif
- if (current_function_calls_alloca)
- {
- rtx tem = 0;
-
- emit_stack_save (SAVE_FUNCTION, &tem, parm_birth_insn);
- emit_stack_restore (SAVE_FUNCTION, tem, NULL_RTX);
- }
-
- /* If scalar return value was computed in a pseudo-reg,
- copy that to the hard return register. */
- if (DECL_RTL (DECL_RESULT (current_function_decl)) != 0
- && GET_CODE (DECL_RTL (DECL_RESULT (current_function_decl))) == REG
- && (REGNO (DECL_RTL (DECL_RESULT (current_function_decl)))
- >= FIRST_PSEUDO_REGISTER))
- {
- rtx real_decl_result;
-
-#ifdef FUNCTION_OUTGOING_VALUE
- real_decl_result
- = FUNCTION_OUTGOING_VALUE (TREE_TYPE (DECL_RESULT (current_function_decl)),
- current_function_decl);
-#else
- real_decl_result
- = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (current_function_decl)),
- current_function_decl);
-#endif
- REG_FUNCTION_VALUE_P (real_decl_result) = 1;
- /* If this is a BLKmode structure being returned in registers, then use
- the mode computed in expand_return. */
- if (GET_MODE (real_decl_result) == BLKmode)
- PUT_MODE (real_decl_result,
- GET_MODE (DECL_RTL (DECL_RESULT (current_function_decl))));
- emit_move_insn (real_decl_result,
- DECL_RTL (DECL_RESULT (current_function_decl)));
- emit_insn (gen_rtx_USE (VOIDmode, real_decl_result));
-
- /* The delay slot scheduler assumes that current_function_return_rtx
- holds the hard register containing the return value, not a temporary
- pseudo. */
- current_function_return_rtx = real_decl_result;
- }
-
- /* If returning a structure, arrange to return the address of the value
- in a place where debuggers expect to find it.
-
- If returning a structure PCC style,
- the caller also depends on this value.
- And current_function_returns_pcc_struct is not necessarily set. */
- if (current_function_returns_struct
- || current_function_returns_pcc_struct)
- {
- rtx value_address = XEXP (DECL_RTL (DECL_RESULT (current_function_decl)), 0);
- tree type = TREE_TYPE (DECL_RESULT (current_function_decl));
-#ifdef FUNCTION_OUTGOING_VALUE
- rtx outgoing
- = FUNCTION_OUTGOING_VALUE (build_pointer_type (type),
- current_function_decl);
-#else
- rtx outgoing
- = FUNCTION_VALUE (build_pointer_type (type),
- current_function_decl);
-#endif
-
- /* Mark this as a function return value so integrate will delete the
- assignment and USE below when inlining this function. */
- REG_FUNCTION_VALUE_P (outgoing) = 1;
-
- emit_move_insn (outgoing, value_address);
- use_variable (outgoing);
- }
-
- use_return_register ();
-
- /* If this is an implementation of __throw, do what's necessary to
- communicate between __builtin_eh_return and the epilogue. */
- expand_eh_return ();
-
- /* Output a return insn if we are using one.
- Otherwise, let the rtl chain end here, to drop through
- into the epilogue. */
-
-#ifdef HAVE_return
- if (HAVE_return)
- {
- emit_jump_insn (gen_return ());
- emit_barrier ();
- }
-#endif
-
- /* Fix up any gotos that jumped out to the outermost
- binding level of the function.
- Must follow emitting RETURN_LABEL. */
-
- /* If you have any cleanups to do at this point,
- and they need to create temporary variables,
- then you will lose. */
- expand_fixups (get_insns ());
-}
-
-/* These arrays record the INSN_UIDs of the prologue and epilogue insns. */
-
-static int *prologue;
-static int *epilogue;
-
-/* Create an array that records the INSN_UIDs of INSNS (either a sequence
- or a single insn). */
-
-#if defined (HAVE_prologue) || defined (HAVE_epilogue)
-static int *
-record_insns (insns)
- rtx insns;
-{
- int *vec;
-
- if (GET_CODE (insns) == SEQUENCE)
- {
- int len = XVECLEN (insns, 0);
- vec = (int *) oballoc ((len + 1) * sizeof (int));
- vec[len] = 0;
- while (--len >= 0)
- vec[len] = INSN_UID (XVECEXP (insns, 0, len));
- }
- else
- {
- vec = (int *) oballoc (2 * sizeof (int));
- vec[0] = INSN_UID (insns);
- vec[1] = 0;
- }
- return vec;
-}
-
-/* Determine how many INSN_UIDs in VEC are part of INSN. */
-
-static int
-contains (insn, vec)
- rtx insn;
- int *vec;
-{
- register int i, j;
-
- if (GET_CODE (insn) == INSN
- && GET_CODE (PATTERN (insn)) == SEQUENCE)
- {
- int count = 0;
- for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
- for (j = 0; vec[j]; j++)
- if (INSN_UID (XVECEXP (PATTERN (insn), 0, i)) == vec[j])
- count++;
- return count;
- }
- else
- {
- for (j = 0; vec[j]; j++)
- if (INSN_UID (insn) == vec[j])
- return 1;
- }
- return 0;
-}
-#endif /* HAVE_prologue || HAVE_epilogue */
-
-/* Generate the prologue and epilogue RTL if the machine supports it. Thread
- this into place with notes indicating where the prologue ends and where
- the epilogue begins. Update the basic block information when possible. */
-
-void
-thread_prologue_and_epilogue_insns (f)
- rtx f ATTRIBUTE_UNUSED;
-{
-#ifdef HAVE_prologue
- if (HAVE_prologue)
- {
- rtx head, seq;
-
- /* The first insn (a NOTE_INSN_DELETED) is followed by zero or more
- prologue insns and a NOTE_INSN_PROLOGUE_END. */
- emit_note_after (NOTE_INSN_PROLOGUE_END, f);
- seq = gen_prologue ();
- head = emit_insn_after (seq, f);
-
- /* Include the new prologue insns in the first block. Ignore them
- if they form a basic block unto themselves. */
- if (x_basic_block_head && n_basic_blocks
- && GET_CODE (BLOCK_HEAD (0)) != CODE_LABEL)
- BLOCK_HEAD (0) = NEXT_INSN (f);
-
- /* Retain a map of the prologue insns. */
- prologue = record_insns (GET_CODE (seq) == SEQUENCE ? seq : head);
- }
- else
-#endif
- prologue = 0;
-
-#ifdef HAVE_epilogue
- if (HAVE_epilogue)
- {
- rtx insn = get_last_insn ();
- rtx prev = prev_nonnote_insn (insn);
-
- /* If we end with a BARRIER, we don't need an epilogue. */
- if (! (prev && GET_CODE (prev) == BARRIER))
- {
- rtx tail, seq, tem;
- rtx first_use = 0;
- rtx last_use = 0;
-
- /* The last basic block ends with a NOTE_INSN_EPILOGUE_BEG, the
- epilogue insns, the USE insns at the end of a function,
- the jump insn that returns, and then a BARRIER. */
-
- /* Move the USE insns at the end of a function onto a list. */
- while (prev
- && GET_CODE (prev) == INSN
- && GET_CODE (PATTERN (prev)) == USE)
- {
- tem = prev;
- prev = prev_nonnote_insn (prev);
-
- NEXT_INSN (PREV_INSN (tem)) = NEXT_INSN (tem);
- PREV_INSN (NEXT_INSN (tem)) = PREV_INSN (tem);
- if (first_use)
- {
- NEXT_INSN (tem) = first_use;
- PREV_INSN (first_use) = tem;
- }
- first_use = tem;
- if (!last_use)
- last_use = tem;
- }
-
- emit_barrier_after (insn);
-
- seq = gen_epilogue ();
- tail = emit_jump_insn_after (seq, insn);
-
- /* Insert the USE insns immediately before the return insn, which
- must be the first instruction before the final barrier. */
- if (first_use)
- {
- tem = prev_nonnote_insn (get_last_insn ());
- NEXT_INSN (PREV_INSN (tem)) = first_use;
- PREV_INSN (first_use) = PREV_INSN (tem);
- PREV_INSN (tem) = last_use;
- NEXT_INSN (last_use) = tem;
- }
-
- emit_note_after (NOTE_INSN_EPILOGUE_BEG, insn);
-
- /* Include the new epilogue insns in the last block. Ignore
- them if they form a basic block unto themselves. */
- if (x_basic_block_end && n_basic_blocks
- && GET_CODE (BLOCK_END (n_basic_blocks - 1)) != JUMP_INSN)
- BLOCK_END (n_basic_blocks - 1) = tail;
-
- /* Retain a map of the epilogue insns. */
- epilogue = record_insns (GET_CODE (seq) == SEQUENCE ? seq : tail);
- return;
- }
- }
-#endif
- epilogue = 0;
-}
-
-/* Reposition the prologue-end and epilogue-begin notes after instruction
- scheduling and delayed branch scheduling. */
-
-void
-reposition_prologue_and_epilogue_notes (f)
- rtx f ATTRIBUTE_UNUSED;
-{
-#if defined (HAVE_prologue) || defined (HAVE_epilogue)
- /* Reposition the prologue and epilogue notes. */
- if (n_basic_blocks)
- {
- int len;
-
- if (prologue)
- {
- register rtx insn, note = 0;
-
- /* Scan from the beginning until we reach the last prologue insn.
- We apparently can't depend on basic_block_{head,end} after
- reorg has run. */
- for (len = 0; prologue[len]; len++)
- ;
- for (insn = f; len && insn; insn = NEXT_INSN (insn))
- {
- if (GET_CODE (insn) == NOTE)
- {
- if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_PROLOGUE_END)
- note = insn;
- }
- else if ((len -= contains (insn, prologue)) == 0)
- {
- rtx next;
- /* Find the prologue-end note if we haven't already, and
- move it to just after the last prologue insn. */
- if (note == 0)
- {
- for (note = insn; (note = NEXT_INSN (note));)
- if (GET_CODE (note) == NOTE
- && NOTE_LINE_NUMBER (note) == NOTE_INSN_PROLOGUE_END)
- break;
- }
-
- next = NEXT_INSN (note);
-
- /* Whether or not we can depend on BLOCK_HEAD,
- attempt to keep it up-to-date. */
- if (BLOCK_HEAD (0) == note)
- BLOCK_HEAD (0) = next;
-
- remove_insn (note);
- add_insn_after (note, insn);
- }
- }
- }
-
- if (epilogue)
- {
- register rtx insn, note = 0;
-
- /* Scan from the end until we reach the first epilogue insn.
- We apparently can't depend on basic_block_{head,end} after
- reorg has run. */
- for (len = 0; epilogue[len]; len++)
- ;
- for (insn = get_last_insn (); len && insn; insn = PREV_INSN (insn))
- {
- if (GET_CODE (insn) == NOTE)
- {
- if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EPILOGUE_BEG)
- note = insn;
- }
- else if ((len -= contains (insn, epilogue)) == 0)
- {
- /* Find the epilogue-begin note if we haven't already, and
- move it to just before the first epilogue insn. */
- if (note == 0)
- {
- for (note = insn; (note = PREV_INSN (note));)
- if (GET_CODE (note) == NOTE
- && NOTE_LINE_NUMBER (note) == NOTE_INSN_EPILOGUE_BEG)
- break;
- }
-
- /* Whether or not we can depend on BLOCK_HEAD,
- attempt to keep it up-to-date. */
- if (n_basic_blocks
- && BLOCK_HEAD (n_basic_blocks-1) == insn)
- BLOCK_HEAD (n_basic_blocks-1) = note;
-
- remove_insn (note);
- add_insn_before (note, insn);
- }
- }
- }
- }
-#endif /* HAVE_prologue or HAVE_epilogue */
-}
diff --git a/gcc/function_990206.c b/gcc/function_990206.c deleted file mode 100755 index 064ea3d..0000000 --- a/gcc/function_990206.c +++ /dev/null @@ -1,6578 +0,0 @@ -/* Expands front end tree to back end RTL for GNU C-Compiler - Copyright (C) 1987, 88, 89, 91-98, 1999 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC 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 2, or (at your option) -any later version. - -GNU CC 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 GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ - - -/* This file handles the generation of rtl code from tree structure - at the level of the function as a whole. - It creates the rtl expressions for parameters and auto variables - and has full responsibility for allocating stack slots. - - `expand_function_start' is called at the beginning of a function, - before the function body is parsed, and `expand_function_end' is - called after parsing the body. - - Call `assign_stack_local' to allocate a stack slot for a local variable. - This is usually done during the RTL generation for the function body, - but it can also be done in the reload pass when a pseudo-register does - not get a hard register. - - Call `put_var_into_stack' when you learn, belatedly, that a variable - previously given a pseudo-register must in fact go in the stack. - This function changes the DECL_RTL to be a stack slot instead of a reg - then scans all the RTL instructions so far generated to correct them. */ - -#include "config.h" -#include "system.h" -#include "rtl.h" -#include "tree.h" -#include "flags.h" -#include "except.h" -#include "function.h" -#include "insn-flags.h" -#include "expr.h" -#include "insn-codes.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "insn-config.h" -#include "recog.h" -#include "output.h" -#include "basic-block.h" -#include "obstack.h" -#include "toplev.h" - -#if !defined PREFERRED_STACK_BOUNDARY && defined STACK_BOUNDARY -#define PREFERRED_STACK_BOUNDARY STACK_BOUNDARY -#endif - -#ifndef TRAMPOLINE_ALIGNMENT -#define TRAMPOLINE_ALIGNMENT FUNCTION_BOUNDARY -#endif - -/* Some systems use __main in a way incompatible with its use in gcc, in these - cases use the macros NAME__MAIN to give a quoted symbol and SYMBOL__MAIN to - give the same symbol without quotes for an alternative entry point. You - must define both, or neither. */ -#ifndef NAME__MAIN -#define NAME__MAIN "__main" -#define SYMBOL__MAIN __main -#endif - -/* Round a value to the lowest integer less than it that is a multiple of - the required alignment. Avoid using division in case the value is - negative. Assume the alignment is a power of two. */ -#define FLOOR_ROUND(VALUE,ALIGN) ((VALUE) & ~((ALIGN) - 1)) - -/* Similar, but round to the next highest integer that meets the - alignment. */ -#define CEIL_ROUND(VALUE,ALIGN) (((VALUE) + (ALIGN) - 1) & ~((ALIGN)- 1)) - -/* NEED_SEPARATE_AP means that we cannot derive ap from the value of fp - during rtl generation. If they are different register numbers, this is - always true. It may also be true if - FIRST_PARM_OFFSET - STARTING_FRAME_OFFSET is not a constant during rtl - generation. See fix_lexical_addr for details. */ - -#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM -#define NEED_SEPARATE_AP -#endif - -/* Number of bytes of args popped by function being compiled on its return. - Zero if no bytes are to be popped. - May affect compilation of return insn or of function epilogue. */ - -int current_function_pops_args; - -/* Nonzero if function being compiled needs to be given an address - where the value should be stored. */ - -int current_function_returns_struct; - -/* Nonzero if function being compiled needs to - return the address of where it has put a structure value. */ - -int current_function_returns_pcc_struct; - -/* Nonzero if function being compiled needs to be passed a static chain. */ - -int current_function_needs_context; - -/* Nonzero if function being compiled can call setjmp. */ - -int current_function_calls_setjmp; - -/* Nonzero if function being compiled can call longjmp. */ - -int current_function_calls_longjmp; - -/* Nonzero if function being compiled receives nonlocal gotos - from nested functions. */ - -int current_function_has_nonlocal_label; - -/* Nonzero if function being compiled has nonlocal gotos to parent - function. */ - -int current_function_has_nonlocal_goto; - -/* Nonzero if this function has a computed goto. - - It is computed during find_basic_blocks or during stupid life - analysis. */ - -int current_function_has_computed_jump; - -/* Nonzero if function being compiled contains nested functions. */ - -int current_function_contains_functions; - -/* Nonzero if function being compiled doesn't modify the stack pointer - (ignoring the prologue and epilogue). This is only valid after - life_analysis has run. */ - -int current_function_sp_is_unchanging; - -/* Nonzero if the current function is a thunk (a lightweight function that - just adjusts one of its arguments and forwards to another function), so - we should try to cut corners where we can. */ -int current_function_is_thunk; - -/* Nonzero if function being compiled can call alloca, - either as a subroutine or builtin. */ - -int current_function_calls_alloca; - -/* Nonzero if the current function returns a pointer type */ - -int current_function_returns_pointer; - -/* If some insns can be deferred to the delay slots of the epilogue, the - delay list for them is recorded here. */ - -rtx current_function_epilogue_delay_list; - -/* If function's args have a fixed size, this is that size, in bytes. - Otherwise, it is -1. - May affect compilation of return insn or of function epilogue. */ - -int current_function_args_size; - -/* # bytes the prologue should push and pretend that the caller pushed them. - The prologue must do this, but only if parms can be passed in registers. */ - -int current_function_pretend_args_size; - -/* # of bytes of outgoing arguments. If ACCUMULATE_OUTGOING_ARGS is - defined, the needed space is pushed by the prologue. */ - -int current_function_outgoing_args_size; - -/* This is the offset from the arg pointer to the place where the first - anonymous arg can be found, if there is one. */ - -rtx current_function_arg_offset_rtx; - -/* Nonzero if current function uses varargs.h or equivalent. - Zero for functions that use stdarg.h. */ - -int current_function_varargs; - -/* Nonzero if current function uses stdarg.h or equivalent. - Zero for functions that use varargs.h. */ - -int current_function_stdarg; - -/* Quantities of various kinds of registers - used for the current function's args. */ - -CUMULATIVE_ARGS current_function_args_info; - -/* Name of function now being compiled. */ - -char *current_function_name; - -/* If non-zero, an RTL expression for the location at which the current - function returns its result. If the current function returns its - result in a register, current_function_return_rtx will always be - the hard register containing the result. */ - -rtx current_function_return_rtx; - -/* Nonzero if the current function uses the constant pool. */ - -int current_function_uses_const_pool; - -/* Nonzero if the current function uses pic_offset_table_rtx. */ -int current_function_uses_pic_offset_table; - -/* The arg pointer hard register, or the pseudo into which it was copied. */ -rtx current_function_internal_arg_pointer; - -/* CYGNUS LOCAL -- Branch Prediction */ -/* The current function uses __builtin_expect for branch prediction. */ -int current_function_uses_expect; - -/* The current function is currently expanding the first argument to - __builtin_expect. */ -int current_function_processing_expect; -/* END CYGNUS LOCAL -- Branch Prediction */ - -/* Language-specific reason why the current function cannot be made inline. */ -char *current_function_cannot_inline; - -/* Nonzero if instrumentation calls for function entry and exit should be - generated. */ -int current_function_instrument_entry_exit; - -/* Nonzero if memory access checking be enabled in the current function. */ -int current_function_check_memory_usage; - -/* The FUNCTION_DECL for an inline function currently being expanded. */ -tree inline_function_decl; - -/* Number of function calls seen so far in current function. */ - -int function_call_count; - -/* List (chain of TREE_LIST) of LABEL_DECLs for all nonlocal labels - (labels to which there can be nonlocal gotos from nested functions) - in this function. */ - -tree nonlocal_labels; - -/* List (chain of EXPR_LIST) of stack slots that hold the current handlers - for nonlocal gotos. There is one for every nonlocal label in the function; - this list matches the one in nonlocal_labels. - Zero when function does not have nonlocal labels. */ - -rtx nonlocal_goto_handler_slots; - -/* RTX for stack slot that holds the stack pointer value to restore - for a nonlocal goto. - Zero when function does not have nonlocal labels. */ - -rtx nonlocal_goto_stack_level; - -/* Label that will go on parm cleanup code, if any. - Jumping to this label runs cleanup code for parameters, if - such code must be run. Following this code is the logical return label. */ - -rtx cleanup_label; - -/* Label that will go on function epilogue. - Jumping to this label serves as a "return" instruction - on machines which require execution of the epilogue on all returns. */ - -rtx return_label; - -/* List (chain of EXPR_LISTs) of pseudo-regs of SAVE_EXPRs. - So we can mark them all live at the end of the function, if nonopt. */ -rtx save_expr_regs; - -/* List (chain of EXPR_LISTs) of all stack slots in this function. - Made for the sake of unshare_all_rtl. */ -rtx stack_slot_list; - -/* Chain of all RTL_EXPRs that have insns in them. */ -tree rtl_expr_chain; - -/* Label to jump back to for tail recursion, or 0 if we have - not yet needed one for this function. */ -rtx tail_recursion_label; - -/* Place after which to insert the tail_recursion_label if we need one. */ -rtx tail_recursion_reentry; - -/* Location at which to save the argument pointer if it will need to be - referenced. There are two cases where this is done: if nonlocal gotos - exist, or if vars stored at an offset from the argument pointer will be - needed by inner routines. */ - -rtx arg_pointer_save_area; - -/* Offset to end of allocated area of stack frame. - If stack grows down, this is the address of the last stack slot allocated. - If stack grows up, this is the address for the next slot. */ -HOST_WIDE_INT frame_offset; - -/* List (chain of TREE_LISTs) of static chains for containing functions. - Each link has a FUNCTION_DECL in the TREE_PURPOSE and a reg rtx - in an RTL_EXPR in the TREE_VALUE. */ -static tree context_display; - -/* List (chain of TREE_LISTs) of trampolines for nested functions. - The trampoline sets up the static chain and jumps to the function. - We supply the trampoline's address when the function's address is requested. - - Each link has a FUNCTION_DECL in the TREE_PURPOSE and a reg rtx - in an RTL_EXPR in the TREE_VALUE. */ -static tree trampoline_list; - -/* Insn after which register parms and SAVE_EXPRs are born, if nonopt. */ -static rtx parm_birth_insn; - -#if 0 -/* Nonzero if a stack slot has been generated whose address is not - actually valid. It means that the generated rtl must all be scanned - to detect and correct the invalid addresses where they occur. */ -static int invalid_stack_slot; -#endif - -/* Last insn of those whose job was to put parms into their nominal homes. */ -static rtx last_parm_insn; - -/* 1 + last pseudo register number possibly used for loading a copy - of a parameter of this function. */ -int max_parm_reg; - -/* Vector indexed by REGNO, containing location on stack in which - to put the parm which is nominally in pseudo register REGNO, - if we discover that that parm must go in the stack. The highest - element in this vector is one less than MAX_PARM_REG, above. */ -rtx *parm_reg_stack_loc; - -/* Nonzero once virtual register instantiation has been done. - assign_stack_local uses frame_pointer_rtx when this is nonzero. */ -static int virtuals_instantiated; - -/* These variables hold pointers to functions to - save and restore machine-specific data, - in push_function_context and pop_function_context. */ -void (*save_machine_status) PROTO((struct function *)); -void (*restore_machine_status) PROTO((struct function *)); - -/* Nonzero if we need to distinguish between the return value of this function - and the return value of a function called by this function. This helps - integrate.c */ - -extern int rtx_equal_function_value_matters; -extern tree sequence_rtl_expr; - -/* In order to evaluate some expressions, such as function calls returning - structures in memory, we need to temporarily allocate stack locations. - We record each allocated temporary in the following structure. - - Associated with each temporary slot is a nesting level. When we pop up - one level, all temporaries associated with the previous level are freed. - Normally, all temporaries are freed after the execution of the statement - in which they were created. However, if we are inside a ({...}) grouping, - the result may be in a temporary and hence must be preserved. If the - result could be in a temporary, we preserve it if we can determine which - one it is in. If we cannot determine which temporary may contain the - result, all temporaries are preserved. A temporary is preserved by - pretending it was allocated at the previous nesting level. - - Automatic variables are also assigned temporary slots, at the nesting - level where they are defined. They are marked a "kept" so that - free_temp_slots will not free them. */ - -struct temp_slot -{ - /* Points to next temporary slot. */ - struct temp_slot *next; - /* The rtx to used to reference the slot. */ - rtx slot; - /* The rtx used to represent the address if not the address of the - slot above. May be an EXPR_LIST if multiple addresses exist. */ - rtx address; - /* The size, in units, of the slot. */ - HOST_WIDE_INT size; - /* The value of `sequence_rtl_expr' when this temporary is allocated. */ - tree rtl_expr; - /* Non-zero if this temporary is currently in use. */ - char in_use; - /* Non-zero if this temporary has its address taken. */ - char addr_taken; - /* Nesting level at which this slot is being used. */ - int level; - /* Non-zero if this should survive a call to free_temp_slots. */ - int keep; - /* The offset of the slot from the frame_pointer, including extra space - for alignment. This info is for combine_temp_slots. */ - HOST_WIDE_INT base_offset; - /* The size of the slot, including extra space for alignment. This - info is for combine_temp_slots. */ - HOST_WIDE_INT full_size; -}; - -/* List of all temporaries allocated, both available and in use. */ - -struct temp_slot *temp_slots; - -/* Current nesting level for temporaries. */ - -int temp_slot_level; - -/* Current nesting level for variables in a block. */ - -int var_temp_slot_level; - -/* When temporaries are created by TARGET_EXPRs, they are created at - this level of temp_slot_level, so that they can remain allocated - until no longer needed. CLEANUP_POINT_EXPRs define the lifetime - of TARGET_EXPRs. */ -int target_temp_slot_level; - -/* This structure is used to record MEMs or pseudos used to replace VAR, any - SUBREGs of VAR, and any MEMs containing VAR as an address. We need to - maintain this list in case two operands of an insn were required to match; - in that case we must ensure we use the same replacement. */ - -struct fixup_replacement -{ - rtx old; - rtx new; - struct fixup_replacement *next; -}; - -/* Forward declarations. */ - -static rtx assign_outer_stack_local PROTO ((enum machine_mode, HOST_WIDE_INT, - int, struct function *)); -static struct temp_slot *find_temp_slot_from_address PROTO((rtx)); -static void put_reg_into_stack PROTO((struct function *, rtx, tree, - enum machine_mode, enum machine_mode, - int, int, int)); -static void fixup_var_refs PROTO((rtx, enum machine_mode, int)); -static struct fixup_replacement - *find_fixup_replacement PROTO((struct fixup_replacement **, rtx)); -static void fixup_var_refs_insns PROTO((rtx, enum machine_mode, int, - rtx, int)); -static void fixup_var_refs_1 PROTO((rtx, enum machine_mode, rtx *, rtx, - struct fixup_replacement **)); -static rtx fixup_memory_subreg PROTO((rtx, rtx, int)); -static rtx walk_fixup_memory_subreg PROTO((rtx, rtx, int)); -static rtx fixup_stack_1 PROTO((rtx, rtx)); -static void optimize_bit_field PROTO((rtx, rtx, rtx *)); -static void instantiate_decls PROTO((tree, int)); -static void instantiate_decls_1 PROTO((tree, int)); -static void instantiate_decl PROTO((rtx, int, int)); -static int instantiate_virtual_regs_1 PROTO((rtx *, rtx, int)); -static void delete_handlers PROTO((void)); -static void pad_to_arg_alignment PROTO((struct args_size *, int)); -#ifndef ARGS_GROW_DOWNWARD -static void pad_below PROTO((struct args_size *, enum machine_mode, - tree)); -#endif -#ifdef ARGS_GROW_DOWNWARD -static tree round_down PROTO((tree, int)); -#endif -static rtx round_trampoline_addr PROTO((rtx)); -static tree blocks_nreverse PROTO((tree)); -static int all_blocks PROTO((tree, tree *)); -#if defined (HAVE_prologue) || defined (HAVE_epilogue) -static int *record_insns PROTO((rtx)); -static int contains PROTO((rtx, int *)); -#endif /* HAVE_prologue || HAVE_epilogue */ -static void put_addressof_into_stack PROTO((rtx)); -static void purge_addressof_1 PROTO((rtx *, rtx, int, int)); - -/* Pointer to chain of `struct function' for containing functions. */ -struct function *outer_function_chain; - -/* Given a function decl for a containing function, - return the `struct function' for it. */ - -struct function * -find_function_data (decl) - tree decl; -{ - struct function *p; - - for (p = outer_function_chain; p; p = p->next) - if (p->decl == decl) - return p; - - abort (); -} - -/* Save the current context for compilation of a nested function. - This is called from language-specific code. - The caller is responsible for saving any language-specific status, - since this function knows only about language-independent variables. */ - -void -push_function_context_to (context) - tree context; -{ - struct function *p = (struct function *) xmalloc (sizeof (struct function)); - - p->next = outer_function_chain; - outer_function_chain = p; - - p->name = current_function_name; - p->decl = current_function_decl; - p->pops_args = current_function_pops_args; - p->returns_struct = current_function_returns_struct; - p->returns_pcc_struct = current_function_returns_pcc_struct; - p->returns_pointer = current_function_returns_pointer; - p->needs_context = current_function_needs_context; - p->calls_setjmp = current_function_calls_setjmp; - p->calls_longjmp = current_function_calls_longjmp; - p->calls_alloca = current_function_calls_alloca; - p->has_nonlocal_label = current_function_has_nonlocal_label; - p->has_nonlocal_goto = current_function_has_nonlocal_goto; - p->contains_functions = current_function_contains_functions; - p->is_thunk = current_function_is_thunk; - p->args_size = current_function_args_size; - p->pretend_args_size = current_function_pretend_args_size; - p->arg_offset_rtx = current_function_arg_offset_rtx; - p->varargs = current_function_varargs; - p->stdarg = current_function_stdarg; - p->uses_const_pool = current_function_uses_const_pool; - p->uses_pic_offset_table = current_function_uses_pic_offset_table; - p->internal_arg_pointer = current_function_internal_arg_pointer; - p->cannot_inline = current_function_cannot_inline; - p->max_parm_reg = max_parm_reg; - p->parm_reg_stack_loc = parm_reg_stack_loc; - p->outgoing_args_size = current_function_outgoing_args_size; - p->return_rtx = current_function_return_rtx; - p->nonlocal_goto_handler_slots = nonlocal_goto_handler_slots; - p->nonlocal_goto_stack_level = nonlocal_goto_stack_level; - p->nonlocal_labels = nonlocal_labels; - p->cleanup_label = cleanup_label; - p->return_label = return_label; - p->save_expr_regs = save_expr_regs; - p->stack_slot_list = stack_slot_list; - p->parm_birth_insn = parm_birth_insn; - p->frame_offset = frame_offset; - p->tail_recursion_label = tail_recursion_label; - p->tail_recursion_reentry = tail_recursion_reentry; - p->arg_pointer_save_area = arg_pointer_save_area; - p->rtl_expr_chain = rtl_expr_chain; - p->last_parm_insn = last_parm_insn; - p->context_display = context_display; - p->trampoline_list = trampoline_list; - p->function_call_count = function_call_count; - p->temp_slots = temp_slots; - p->temp_slot_level = temp_slot_level; - p->target_temp_slot_level = target_temp_slot_level; - p->var_temp_slot_level = var_temp_slot_level; - p->fixup_var_refs_queue = 0; - p->epilogue_delay_list = current_function_epilogue_delay_list; - p->args_info = current_function_args_info; - p->check_memory_usage = current_function_check_memory_usage; - p->instrument_entry_exit = current_function_instrument_entry_exit; - /* CYGNUS LOCAL -- Branch Prediction */ - p->uses_expect = current_function_uses_expect; - /* END CYGNUS LOCAL -- Branch Prediction */ - - save_tree_status (p, context); - save_storage_status (p); - save_emit_status (p); - save_expr_status (p); - save_stmt_status (p); - save_varasm_status (p, context); - if (save_machine_status) - (*save_machine_status) (p); -} - -void -push_function_context () -{ - push_function_context_to (current_function_decl); -} - -/* Restore the last saved context, at the end of a nested function. - This function is called from language-specific code. */ - -void -pop_function_context_from (context) - tree context; -{ - struct function *p = outer_function_chain; - struct var_refs_queue *queue; - - outer_function_chain = p->next; - - current_function_contains_functions - = p->contains_functions || p->inline_obstacks - || context == current_function_decl; - current_function_name = p->name; - current_function_decl = p->decl; - current_function_pops_args = p->pops_args; - current_function_returns_struct = p->returns_struct; - current_function_returns_pcc_struct = p->returns_pcc_struct; - current_function_returns_pointer = p->returns_pointer; - current_function_needs_context = p->needs_context; - current_function_calls_setjmp = p->calls_setjmp; - current_function_calls_longjmp = p->calls_longjmp; - current_function_calls_alloca = p->calls_alloca; - current_function_has_nonlocal_label = p->has_nonlocal_label; - current_function_has_nonlocal_goto = p->has_nonlocal_goto; - current_function_is_thunk = p->is_thunk; - current_function_args_size = p->args_size; - current_function_pretend_args_size = p->pretend_args_size; - current_function_arg_offset_rtx = p->arg_offset_rtx; - current_function_varargs = p->varargs; - current_function_stdarg = p->stdarg; - current_function_uses_const_pool = p->uses_const_pool; - current_function_uses_pic_offset_table = p->uses_pic_offset_table; - current_function_internal_arg_pointer = p->internal_arg_pointer; - current_function_cannot_inline = p->cannot_inline; - max_parm_reg = p->max_parm_reg; - parm_reg_stack_loc = p->parm_reg_stack_loc; - current_function_outgoing_args_size = p->outgoing_args_size; - current_function_return_rtx = p->return_rtx; - nonlocal_goto_handler_slots = p->nonlocal_goto_handler_slots; - nonlocal_goto_stack_level = p->nonlocal_goto_stack_level; - nonlocal_labels = p->nonlocal_labels; - cleanup_label = p->cleanup_label; - return_label = p->return_label; - save_expr_regs = p->save_expr_regs; - stack_slot_list = p->stack_slot_list; - parm_birth_insn = p->parm_birth_insn; - frame_offset = p->frame_offset; - tail_recursion_label = p->tail_recursion_label; - tail_recursion_reentry = p->tail_recursion_reentry; - arg_pointer_save_area = p->arg_pointer_save_area; - rtl_expr_chain = p->rtl_expr_chain; - last_parm_insn = p->last_parm_insn; - context_display = p->context_display; - trampoline_list = p->trampoline_list; - function_call_count = p->function_call_count; - temp_slots = p->temp_slots; - temp_slot_level = p->temp_slot_level; - target_temp_slot_level = p->target_temp_slot_level; - var_temp_slot_level = p->var_temp_slot_level; - current_function_epilogue_delay_list = p->epilogue_delay_list; - reg_renumber = 0; - current_function_args_info = p->args_info; - current_function_check_memory_usage = p->check_memory_usage; - current_function_instrument_entry_exit = p->instrument_entry_exit; - /* CYGNUS LOCAL -- Branch Prediction */ - current_function_uses_expect = p->uses_expect; - /* END CYGNUS LOCAL -- Branch Prediction */ - - restore_tree_status (p, context); - restore_storage_status (p); - restore_expr_status (p); - restore_emit_status (p); - restore_stmt_status (p); - restore_varasm_status (p); - - if (restore_machine_status) - (*restore_machine_status) (p); - - /* Finish doing put_var_into_stack for any of our variables - which became addressable during the nested function. */ - for (queue = p->fixup_var_refs_queue; queue; queue = queue->next) - fixup_var_refs (queue->modified, queue->promoted_mode, queue->unsignedp); - - free (p); - - /* Reset variables that have known state during rtx generation. */ - rtx_equal_function_value_matters = 1; - virtuals_instantiated = 0; -} - -void pop_function_context () -{ - pop_function_context_from (current_function_decl); -} - -/* Allocate fixed slots in the stack frame of the current function. */ - -/* Return size needed for stack frame based on slots so far allocated. - This size counts from zero. It is not rounded to PREFERRED_STACK_BOUNDARY; - the caller may have to do that. */ - -HOST_WIDE_INT -get_frame_size () -{ -#ifdef FRAME_GROWS_DOWNWARD - return -frame_offset; -#else - return frame_offset; -#endif -} - -/* Allocate a stack slot of SIZE bytes and return a MEM rtx for it - with machine mode MODE. - - ALIGN controls the amount of alignment for the address of the slot: - 0 means according to MODE, - -1 means use BIGGEST_ALIGNMENT and round size to multiple of that, - positive specifies alignment boundary in bits. - - We do not round to stack_boundary here. */ - -rtx -assign_stack_local (mode, size, align) - enum machine_mode mode; - HOST_WIDE_INT size; - int align; -{ - register rtx x, addr; - int bigend_correction = 0; - int alignment; - - if (align == 0) - { - alignment = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; - if (mode == BLKmode) - alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT; - } - else if (align == -1) - { - alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT; - size = CEIL_ROUND (size, alignment); - } - else - alignment = align / BITS_PER_UNIT; - - /* Round frame offset to that alignment. - We must be careful here, since FRAME_OFFSET might be negative and - division with a negative dividend isn't as well defined as we might - like. So we instead assume that ALIGNMENT is a power of two and - use logical operations which are unambiguous. */ -#ifdef FRAME_GROWS_DOWNWARD - frame_offset = FLOOR_ROUND (frame_offset, alignment); -#else - frame_offset = CEIL_ROUND (frame_offset, alignment); -#endif - - /* On a big-endian machine, if we are allocating more space than we will use, - use the least significant bytes of those that are allocated. */ - if (BYTES_BIG_ENDIAN && mode != BLKmode) - bigend_correction = size - GET_MODE_SIZE (mode); - -#ifdef FRAME_GROWS_DOWNWARD - frame_offset -= size; -#endif - - /* If we have already instantiated virtual registers, return the actual - address relative to the frame pointer. */ - if (virtuals_instantiated) - addr = plus_constant (frame_pointer_rtx, - (frame_offset + bigend_correction - + STARTING_FRAME_OFFSET)); - else - addr = plus_constant (virtual_stack_vars_rtx, - frame_offset + bigend_correction); - -#ifndef FRAME_GROWS_DOWNWARD - frame_offset += size; -#endif - - x = gen_rtx_MEM (mode, addr); - - stack_slot_list = gen_rtx_EXPR_LIST (VOIDmode, x, stack_slot_list); - - return x; -} - -/* Assign a stack slot in a containing function. - First three arguments are same as in preceding function. - The last argument specifies the function to allocate in. */ - -static rtx -assign_outer_stack_local (mode, size, align, function) - enum machine_mode mode; - HOST_WIDE_INT size; - int align; - struct function *function; -{ - register rtx x, addr; - int bigend_correction = 0; - int alignment; - - /* Allocate in the memory associated with the function in whose frame - we are assigning. */ - push_obstacks (function->function_obstack, - function->function_maybepermanent_obstack); - - if (align == 0) - { - alignment = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; - if (mode == BLKmode) - alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT; - } - else if (align == -1) - { - alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT; - size = CEIL_ROUND (size, alignment); - } - else - alignment = align / BITS_PER_UNIT; - - /* Round frame offset to that alignment. */ -#ifdef FRAME_GROWS_DOWNWARD - function->frame_offset = FLOOR_ROUND (function->frame_offset, alignment); -#else - function->frame_offset = CEIL_ROUND (function->frame_offset, alignment); -#endif - - /* On a big-endian machine, if we are allocating more space than we will use, - use the least significant bytes of those that are allocated. */ - if (BYTES_BIG_ENDIAN && mode != BLKmode) - bigend_correction = size - GET_MODE_SIZE (mode); - -#ifdef FRAME_GROWS_DOWNWARD - function->frame_offset -= size; -#endif - addr = plus_constant (virtual_stack_vars_rtx, - function->frame_offset + bigend_correction); -#ifndef FRAME_GROWS_DOWNWARD - function->frame_offset += size; -#endif - - x = gen_rtx_MEM (mode, addr); - - function->stack_slot_list - = gen_rtx_EXPR_LIST (VOIDmode, x, function->stack_slot_list); - - pop_obstacks (); - - return x; -} - -/* Allocate a temporary stack slot and record it for possible later - reuse. - - MODE is the machine mode to be given to the returned rtx. - - SIZE is the size in units of the space required. We do no rounding here - since assign_stack_local will do any required rounding. - - KEEP is 1 if this slot is to be retained after a call to - free_temp_slots. Automatic variables for a block are allocated - with this flag. KEEP is 2 if we allocate a longer term temporary, - whose lifetime is controlled by CLEANUP_POINT_EXPRs. KEEP is 3 - if we are to allocate something at an inner level to be treated as - a variable in the block (e.g., a SAVE_EXPR). */ - -rtx -assign_stack_temp (mode, size, keep) - enum machine_mode mode; - HOST_WIDE_INT size; - int keep; -{ - struct temp_slot *p, *best_p = 0; - - /* If SIZE is -1 it means that somebody tried to allocate a temporary - of a variable size. */ - if (size == -1) - abort (); - - /* First try to find an available, already-allocated temporary that is the - exact size we require. */ - for (p = temp_slots; p; p = p->next) - if (p->size == size && GET_MODE (p->slot) == mode && ! p->in_use) - break; - - /* If we didn't find, one, try one that is larger than what we want. We - find the smallest such. */ - if (p == 0) - for (p = temp_slots; p; p = p->next) - if (p->size > size && GET_MODE (p->slot) == mode && ! p->in_use - && (best_p == 0 || best_p->size > p->size)) - best_p = p; - - /* Make our best, if any, the one to use. */ - if (best_p) - { - /* If there are enough aligned bytes left over, make them into a new - temp_slot so that the extra bytes don't get wasted. Do this only - for BLKmode slots, so that we can be sure of the alignment. */ - if (GET_MODE (best_p->slot) == BLKmode) - { - int alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT; - HOST_WIDE_INT rounded_size = CEIL_ROUND (size, alignment); - - if (best_p->size - rounded_size >= alignment) - { - p = (struct temp_slot *) oballoc (sizeof (struct temp_slot)); - p->in_use = p->addr_taken = 0; - p->size = best_p->size - rounded_size; - p->base_offset = best_p->base_offset + rounded_size; - p->full_size = best_p->full_size - rounded_size; - p->slot = gen_rtx_MEM (BLKmode, - plus_constant (XEXP (best_p->slot, 0), - rounded_size)); - p->address = 0; - p->rtl_expr = 0; - p->next = temp_slots; - temp_slots = p; - - stack_slot_list = gen_rtx_EXPR_LIST (VOIDmode, p->slot, - stack_slot_list); - - best_p->size = rounded_size; - best_p->full_size = rounded_size; - } - } - - p = best_p; - } - - /* If we still didn't find one, make a new temporary. */ - if (p == 0) - { - HOST_WIDE_INT frame_offset_old = frame_offset; - - p = (struct temp_slot *) oballoc (sizeof (struct temp_slot)); - - /* If the temp slot mode doesn't indicate the alignment, - use the largest possible, so no one will be disappointed. */ - p->slot = assign_stack_local (mode, size, mode == BLKmode ? -1 : 0); - - /* The following slot size computation is necessary because we don't - know the actual size of the temporary slot until assign_stack_local - has performed all the frame alignment and size rounding for the - requested temporary. Note that extra space added for alignment - can be either above or below this stack slot depending on which - way the frame grows. We include the extra space if and only if it - is above this slot. */ -#ifdef FRAME_GROWS_DOWNWARD - p->size = frame_offset_old - frame_offset; -#else - p->size = size; -#endif - - /* Now define the fields used by combine_temp_slots. */ -#ifdef FRAME_GROWS_DOWNWARD - p->base_offset = frame_offset; - p->full_size = frame_offset_old - frame_offset; -#else - p->base_offset = frame_offset_old; - p->full_size = frame_offset - frame_offset_old; -#endif - p->address = 0; - p->next = temp_slots; - temp_slots = p; - } - - p->in_use = 1; - p->addr_taken = 0; - p->rtl_expr = sequence_rtl_expr; - - if (keep == 2) - { - p->level = target_temp_slot_level; - p->keep = 0; - } - else if (keep == 3) - { - p->level = var_temp_slot_level; - p->keep = 0; - } - else - { - p->level = temp_slot_level; - p->keep = keep; - } - - /* We may be reusing an old slot, so clear any MEM flags that may have been - set from before. */ - RTX_UNCHANGING_P (p->slot) = 0; - MEM_IN_STRUCT_P (p->slot) = 0; - MEM_SCALAR_P (p->slot) = 0; - MEM_ALIAS_SET (p->slot) = 0; - return p->slot; -} - -/* Assign a temporary of given TYPE. - KEEP is as for assign_stack_temp. - MEMORY_REQUIRED is 1 if the result must be addressable stack memory; - it is 0 if a register is OK. - DONT_PROMOTE is 1 if we should not promote values in register - to wider modes. */ - -rtx -assign_temp (type, keep, memory_required, dont_promote) - tree type; - int keep; - int memory_required; - int dont_promote; -{ - enum machine_mode mode = TYPE_MODE (type); - int unsignedp = TREE_UNSIGNED (type); - - if (mode == BLKmode || memory_required) - { - HOST_WIDE_INT size = int_size_in_bytes (type); - rtx tmp; - - /* Unfortunately, we don't yet know how to allocate variable-sized - temporaries. However, sometimes we have a fixed upper limit on - the size (which is stored in TYPE_ARRAY_MAX_SIZE) and can use that - instead. This is the case for Chill variable-sized strings. */ - if (size == -1 && TREE_CODE (type) == ARRAY_TYPE - && TYPE_ARRAY_MAX_SIZE (type) != NULL_TREE - && TREE_CODE (TYPE_ARRAY_MAX_SIZE (type)) == INTEGER_CST) - size = TREE_INT_CST_LOW (TYPE_ARRAY_MAX_SIZE (type)); - - tmp = assign_stack_temp (mode, size, keep); - MEM_SET_IN_STRUCT_P (tmp, AGGREGATE_TYPE_P (type)); - return tmp; - } - -#ifndef PROMOTE_FOR_CALL_ONLY - if (! dont_promote) - mode = promote_mode (type, mode, &unsignedp, 0); -#endif - - return gen_reg_rtx (mode); -} - -/* Combine temporary stack slots which are adjacent on the stack. - - This allows for better use of already allocated stack space. This is only - done for BLKmode slots because we can be sure that we won't have alignment - problems in this case. */ - -void -combine_temp_slots () -{ - struct temp_slot *p, *q; - struct temp_slot *prev_p, *prev_q; - int num_slots; - - /* If there are a lot of temp slots, don't do anything unless - high levels of optimizaton. */ - if (! flag_expensive_optimizations) - for (p = temp_slots, num_slots = 0; p; p = p->next, num_slots++) - if (num_slots > 100 || (num_slots > 10 && optimize == 0)) - return; - - for (p = temp_slots, prev_p = 0; p; p = prev_p ? prev_p->next : temp_slots) - { - int delete_p = 0; - - if (! p->in_use && GET_MODE (p->slot) == BLKmode) - for (q = p->next, prev_q = p; q; q = prev_q->next) - { - int delete_q = 0; - if (! q->in_use && GET_MODE (q->slot) == BLKmode) - { - if (p->base_offset + p->full_size == q->base_offset) - { - /* Q comes after P; combine Q into P. */ - p->size += q->size; - p->full_size += q->full_size; - delete_q = 1; - } - else if (q->base_offset + q->full_size == p->base_offset) - { - /* P comes after Q; combine P into Q. */ - q->size += p->size; - q->full_size += p->full_size; - delete_p = 1; - break; - } - } - /* Either delete Q or advance past it. */ - if (delete_q) - prev_q->next = q->next; - else - prev_q = q; - } - /* Either delete P or advance past it. */ - if (delete_p) - { - if (prev_p) - prev_p->next = p->next; - else - temp_slots = p->next; - } - else - prev_p = p; - } -} - -/* Find the temp slot corresponding to the object at address X. */ - -static struct temp_slot * -find_temp_slot_from_address (x) - rtx x; -{ - struct temp_slot *p; - rtx next; - - for (p = temp_slots; p; p = p->next) - { - if (! p->in_use) - continue; - - else if (XEXP (p->slot, 0) == x - || p->address == x - || (GET_CODE (x) == PLUS - && XEXP (x, 0) == virtual_stack_vars_rtx - && GET_CODE (XEXP (x, 1)) == CONST_INT - && INTVAL (XEXP (x, 1)) >= p->base_offset - && INTVAL (XEXP (x, 1)) < p->base_offset + p->full_size)) - return p; - - else if (p->address != 0 && GET_CODE (p->address) == EXPR_LIST) - for (next = p->address; next; next = XEXP (next, 1)) - if (XEXP (next, 0) == x) - return p; - } - - return 0; -} - -/* Indicate that NEW is an alternate way of referring to the temp slot - that previously was known by OLD. */ - -void -update_temp_slot_address (old, new) - rtx old, new; -{ - struct temp_slot *p = find_temp_slot_from_address (old); - - /* If none, return. Else add NEW as an alias. */ - if (p == 0) - return; - else if (p->address == 0) - p->address = new; - else - { - if (GET_CODE (p->address) != EXPR_LIST) - p->address = gen_rtx_EXPR_LIST (VOIDmode, p->address, NULL_RTX); - - p->address = gen_rtx_EXPR_LIST (VOIDmode, new, p->address); - } -} - -/* If X could be a reference to a temporary slot, mark the fact that its - address was taken. */ - -void -mark_temp_addr_taken (x) - rtx x; -{ - struct temp_slot *p; - - if (x == 0) - return; - - /* If X is not in memory or is at a constant address, it cannot be in - a temporary slot. */ - if (GET_CODE (x) != MEM || CONSTANT_P (XEXP (x, 0))) - return; - - p = find_temp_slot_from_address (XEXP (x, 0)); - if (p != 0) - p->addr_taken = 1; -} - -/* If X could be a reference to a temporary slot, mark that slot as - belonging to the to one level higher than the current level. If X - matched one of our slots, just mark that one. Otherwise, we can't - easily predict which it is, so upgrade all of them. Kept slots - need not be touched. - - This is called when an ({...}) construct occurs and a statement - returns a value in memory. */ - -void -preserve_temp_slots (x) - rtx x; -{ - struct temp_slot *p = 0; - - /* If there is no result, we still might have some objects whose address - were taken, so we need to make sure they stay around. */ - if (x == 0) - { - for (p = temp_slots; p; p = p->next) - if (p->in_use && p->level == temp_slot_level && p->addr_taken) - p->level--; - - return; - } - - /* If X is a register that is being used as a pointer, see if we have - a temporary slot we know it points to. To be consistent with - the code below, we really should preserve all non-kept slots - if we can't find a match, but that seems to be much too costly. */ - if (GET_CODE (x) == REG && REGNO_POINTER_FLAG (REGNO (x))) - p = find_temp_slot_from_address (x); - - /* If X is not in memory or is at a constant address, it cannot be in - a temporary slot, but it can contain something whose address was - taken. */ - if (p == 0 && (GET_CODE (x) != MEM || CONSTANT_P (XEXP (x, 0)))) - { - for (p = temp_slots; p; p = p->next) - if (p->in_use && p->level == temp_slot_level && p->addr_taken) - p->level--; - - return; - } - - /* First see if we can find a match. */ - if (p == 0) - p = find_temp_slot_from_address (XEXP (x, 0)); - - if (p != 0) - { - /* Move everything at our level whose address was taken to our new - level in case we used its address. */ - struct temp_slot *q; - - if (p->level == temp_slot_level) - { - for (q = temp_slots; q; q = q->next) - if (q != p && q->addr_taken && q->level == p->level) - q->level--; - - p->level--; - p->addr_taken = 0; - } - return; - } - - /* Otherwise, preserve all non-kept slots at this level. */ - for (p = temp_slots; p; p = p->next) - if (p->in_use && p->level == temp_slot_level && ! p->keep) - p->level--; -} - -/* X is the result of an RTL_EXPR. If it is a temporary slot associated - with that RTL_EXPR, promote it into a temporary slot at the present - level so it will not be freed when we free slots made in the - RTL_EXPR. */ - -void -preserve_rtl_expr_result (x) - rtx x; -{ - struct temp_slot *p; - - /* If X is not in memory or is at a constant address, it cannot be in - a temporary slot. */ - if (x == 0 || GET_CODE (x) != MEM || CONSTANT_P (XEXP (x, 0))) - return; - - /* If we can find a match, move it to our level unless it is already at - an upper level. */ - p = find_temp_slot_from_address (XEXP (x, 0)); - if (p != 0) - { - p->level = MIN (p->level, temp_slot_level); - p->rtl_expr = 0; - } - - return; -} - -/* Free all temporaries used so far. This is normally called at the end - of generating code for a statement. Don't free any temporaries - currently in use for an RTL_EXPR that hasn't yet been emitted. - We could eventually do better than this since it can be reused while - generating the same RTL_EXPR, but this is complex and probably not - worthwhile. */ - -void -free_temp_slots () -{ - struct temp_slot *p; - - for (p = temp_slots; p; p = p->next) - if (p->in_use && p->level == temp_slot_level && ! p->keep - && p->rtl_expr == 0) - p->in_use = 0; - - combine_temp_slots (); -} - -/* Free all temporary slots used in T, an RTL_EXPR node. */ - -void -free_temps_for_rtl_expr (t) - tree t; -{ - struct temp_slot *p; - - for (p = temp_slots; p; p = p->next) - if (p->rtl_expr == t) - p->in_use = 0; - - combine_temp_slots (); -} - -/* Mark all temporaries ever allocated in this function as not suitable - for reuse until the current level is exited. */ - -void -mark_all_temps_used () -{ - struct temp_slot *p; - - for (p = temp_slots; p; p = p->next) - { - p->in_use = p->keep = 1; - p->level = MIN (p->level, temp_slot_level); - } -} - -/* Push deeper into the nesting level for stack temporaries. */ - -void -push_temp_slots () -{ - temp_slot_level++; -} - -/* Likewise, but save the new level as the place to allocate variables - for blocks. */ - -void -push_temp_slots_for_block () -{ - push_temp_slots (); - - var_temp_slot_level = temp_slot_level; -} - -/* Likewise, but save the new level as the place to allocate temporaries - for TARGET_EXPRs. */ - -void -push_temp_slots_for_target () -{ - push_temp_slots (); - - target_temp_slot_level = temp_slot_level; -} - -/* Set and get the value of target_temp_slot_level. The only - permitted use of these functions is to save and restore this value. */ - -int -get_target_temp_slot_level () -{ - return target_temp_slot_level; -} - -void -set_target_temp_slot_level (level) - int level; -{ - target_temp_slot_level = level; -} - -/* Pop a temporary nesting level. All slots in use in the current level - are freed. */ - -void -pop_temp_slots () -{ - struct temp_slot *p; - - for (p = temp_slots; p; p = p->next) - if (p->in_use && p->level == temp_slot_level && p->rtl_expr == 0) - p->in_use = 0; - - combine_temp_slots (); - - temp_slot_level--; -} - -/* Initialize temporary slots. */ - -void -init_temp_slots () -{ - /* We have not allocated any temporaries yet. */ - temp_slots = 0; - temp_slot_level = 0; - var_temp_slot_level = 0; - target_temp_slot_level = 0; -} - -/* Retroactively move an auto variable from a register to a stack slot. - This is done when an address-reference to the variable is seen. */ - -void -put_var_into_stack (decl) - tree decl; -{ - register rtx reg; - enum machine_mode promoted_mode, decl_mode; - struct function *function = 0; - tree context; - int can_use_addressof; - - context = decl_function_context (decl); - - /* Get the current rtl used for this object and its original mode. */ - reg = TREE_CODE (decl) == SAVE_EXPR ? SAVE_EXPR_RTL (decl) : DECL_RTL (decl); - - /* No need to do anything if decl has no rtx yet - since in that case caller is setting TREE_ADDRESSABLE - and a stack slot will be assigned when the rtl is made. */ - if (reg == 0) - return; - - /* Get the declared mode for this object. */ - decl_mode = (TREE_CODE (decl) == SAVE_EXPR ? TYPE_MODE (TREE_TYPE (decl)) - : DECL_MODE (decl)); - /* Get the mode it's actually stored in. */ - promoted_mode = GET_MODE (reg); - - /* If this variable comes from an outer function, - find that function's saved context. */ - if (context != current_function_decl && context != inline_function_decl) - for (function = outer_function_chain; function; function = function->next) - if (function->decl == context) - break; - - /* If this is a variable-size object with a pseudo to address it, - put that pseudo into the stack, if the var is nonlocal. */ - if (DECL_NONLOCAL (decl) - && GET_CODE (reg) == MEM - && GET_CODE (XEXP (reg, 0)) == REG - && REGNO (XEXP (reg, 0)) > LAST_VIRTUAL_REGISTER) - { - reg = XEXP (reg, 0); - decl_mode = promoted_mode = GET_MODE (reg); - } - - can_use_addressof - = (function == 0 - && optimize > 0 - /* FIXME make it work for promoted modes too */ - && decl_mode == promoted_mode -#ifdef NON_SAVING_SETJMP - && ! (NON_SAVING_SETJMP && current_function_calls_setjmp) -#endif - ); - - /* If we can't use ADDRESSOF, make sure we see through one we already - generated. */ - if (! can_use_addressof && GET_CODE (reg) == MEM - && GET_CODE (XEXP (reg, 0)) == ADDRESSOF) - reg = XEXP (XEXP (reg, 0), 0); - - /* Now we should have a value that resides in one or more pseudo regs. */ - - if (GET_CODE (reg) == REG) - { - /* If this variable lives in the current function and we don't need - to put things in the stack for the sake of setjmp, try to keep it - in a register until we know we actually need the address. */ - if (can_use_addressof) - gen_mem_addressof (reg, decl); - else - put_reg_into_stack (function, reg, TREE_TYPE (decl), - promoted_mode, decl_mode, - TREE_SIDE_EFFECTS (decl), 0, - TREE_USED (decl) - || DECL_INITIAL (decl) != 0); - } - else if (GET_CODE (reg) == CONCAT) - { - /* A CONCAT contains two pseudos; put them both in the stack. - We do it so they end up consecutive. */ - enum machine_mode part_mode = GET_MODE (XEXP (reg, 0)); - tree part_type = TREE_TYPE (TREE_TYPE (decl)); -#ifdef FRAME_GROWS_DOWNWARD - /* Since part 0 should have a lower address, do it second. */ - put_reg_into_stack (function, XEXP (reg, 1), part_type, part_mode, - part_mode, TREE_SIDE_EFFECTS (decl), 0, - TREE_USED (decl) || DECL_INITIAL (decl) != 0); - put_reg_into_stack (function, XEXP (reg, 0), part_type, part_mode, - part_mode, TREE_SIDE_EFFECTS (decl), 0, - TREE_USED (decl) || DECL_INITIAL (decl) != 0); -#else - put_reg_into_stack (function, XEXP (reg, 0), part_type, part_mode, - part_mode, TREE_SIDE_EFFECTS (decl), 0, - TREE_USED (decl) || DECL_INITIAL (decl) != 0); - put_reg_into_stack (function, XEXP (reg, 1), part_type, part_mode, - part_mode, TREE_SIDE_EFFECTS (decl), 0, - TREE_USED (decl) || DECL_INITIAL (decl) != 0); -#endif - - /* Change the CONCAT into a combined MEM for both parts. */ - PUT_CODE (reg, MEM); - MEM_VOLATILE_P (reg) = MEM_VOLATILE_P (XEXP (reg, 0)); - MEM_ALIAS_SET (reg) = get_alias_set (decl); - - /* The two parts are in memory order already. - Use the lower parts address as ours. */ - XEXP (reg, 0) = XEXP (XEXP (reg, 0), 0); - /* Prevent sharing of rtl that might lose. */ - if (GET_CODE (XEXP (reg, 0)) == PLUS) - XEXP (reg, 0) = copy_rtx (XEXP (reg, 0)); - } - else - return; - - if (current_function_check_memory_usage) - emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3, - XEXP (reg, 0), ptr_mode, - GEN_INT (GET_MODE_SIZE (GET_MODE (reg))), - TYPE_MODE (sizetype), - GEN_INT (MEMORY_USE_RW), - TYPE_MODE (integer_type_node)); -} - -/* Subroutine of put_var_into_stack. This puts a single pseudo reg REG - into the stack frame of FUNCTION (0 means the current function). - DECL_MODE is the machine mode of the user-level data type. - PROMOTED_MODE is the machine mode of the register. - VOLATILE_P is nonzero if this is for a "volatile" decl. - USED_P is nonzero if this reg might have already been used in an insn. */ - -static void -put_reg_into_stack (function, reg, type, promoted_mode, decl_mode, volatile_p, - original_regno, used_p) - struct function *function; - rtx reg; - tree type; - enum machine_mode promoted_mode, decl_mode; - int volatile_p; - int original_regno; - int used_p; -{ - rtx new = 0; - int regno = original_regno; - - if (regno == 0) - regno = REGNO (reg); - - if (function) - { - if (regno < function->max_parm_reg) - new = function->parm_reg_stack_loc[regno]; - if (new == 0) - new = assign_outer_stack_local (decl_mode, GET_MODE_SIZE (decl_mode), - 0, function); - } - else - { - if (regno < max_parm_reg) - new = parm_reg_stack_loc[regno]; - if (new == 0) - new = assign_stack_local (decl_mode, GET_MODE_SIZE (decl_mode), 0); - } - - PUT_MODE (reg, decl_mode); - XEXP (reg, 0) = XEXP (new, 0); - /* `volatil' bit means one thing for MEMs, another entirely for REGs. */ - MEM_VOLATILE_P (reg) = volatile_p; - PUT_CODE (reg, MEM); - - /* If this is a memory ref that contains aggregate components, - mark it as such for cse and loop optimize. If we are reusing a - previously generated stack slot, then we need to copy the bit in - case it was set for other reasons. For instance, it is set for - __builtin_va_alist. */ - MEM_SET_IN_STRUCT_P (reg, - AGGREGATE_TYPE_P (type) || MEM_IN_STRUCT_P (new)); - MEM_ALIAS_SET (reg) = get_alias_set (type); - - /* Now make sure that all refs to the variable, previously made - when it was a register, are fixed up to be valid again. */ - - if (used_p && function != 0) - { - struct var_refs_queue *temp; - - /* Variable is inherited; fix it up when we get back to its function. */ - push_obstacks (function->function_obstack, - function->function_maybepermanent_obstack); - - /* See comment in restore_tree_status in tree.c for why this needs to be - on saveable obstack. */ - temp - = (struct var_refs_queue *) savealloc (sizeof (struct var_refs_queue)); - temp->modified = reg; - temp->promoted_mode = promoted_mode; - temp->unsignedp = TREE_UNSIGNED (type); - temp->next = function->fixup_var_refs_queue; - function->fixup_var_refs_queue = temp; - pop_obstacks (); - } - else if (used_p) - /* Variable is local; fix it up now. */ - fixup_var_refs (reg, promoted_mode, TREE_UNSIGNED (type)); -} - -static void -fixup_var_refs (var, promoted_mode, unsignedp) - rtx var; - enum machine_mode promoted_mode; - int unsignedp; -{ - tree pending; - rtx first_insn = get_insns (); - struct sequence_stack *stack = sequence_stack; - tree rtl_exps = rtl_expr_chain; - - /* Must scan all insns for stack-refs that exceed the limit. */ - fixup_var_refs_insns (var, promoted_mode, unsignedp, first_insn, stack == 0); - - /* Scan all pending sequences too. */ - for (; stack; stack = stack->next) - { - push_to_sequence (stack->first); - fixup_var_refs_insns (var, promoted_mode, unsignedp, - stack->first, stack->next != 0); - /* Update remembered end of sequence - in case we added an insn at the end. */ - stack->last = get_last_insn (); - end_sequence (); - } - - /* Scan all waiting RTL_EXPRs too. */ - for (pending = rtl_exps; pending; pending = TREE_CHAIN (pending)) - { - rtx seq = RTL_EXPR_SEQUENCE (TREE_VALUE (pending)); - if (seq != const0_rtx && seq != 0) - { - push_to_sequence (seq); - fixup_var_refs_insns (var, promoted_mode, unsignedp, seq, 0); - end_sequence (); - } - } - - /* Scan the catch clauses for exception handling too. */ - push_to_sequence (catch_clauses); - fixup_var_refs_insns (var, promoted_mode, unsignedp, catch_clauses, 0); - end_sequence (); -} - -/* REPLACEMENTS is a pointer to a list of the struct fixup_replacement and X is - some part of an insn. Return a struct fixup_replacement whose OLD - value is equal to X. Allocate a new structure if no such entry exists. */ - -static struct fixup_replacement * -find_fixup_replacement (replacements, x) - struct fixup_replacement **replacements; - rtx x; -{ - struct fixup_replacement *p; - - /* See if we have already replaced this. */ - for (p = *replacements; p && p->old != x; p = p->next) - ; - - if (p == 0) - { - p = (struct fixup_replacement *) oballoc (sizeof (struct fixup_replacement)); - p->old = x; - p->new = 0; - p->next = *replacements; - *replacements = p; - } - - return p; -} - -/* Scan the insn-chain starting with INSN for refs to VAR - and fix them up. TOPLEVEL is nonzero if this chain is the - main chain of insns for the current function. */ - -static void -fixup_var_refs_insns (var, promoted_mode, unsignedp, insn, toplevel) - rtx var; - enum machine_mode promoted_mode; - int unsignedp; - rtx insn; - int toplevel; -{ - rtx call_dest = 0; - - while (insn) - { - rtx next = NEXT_INSN (insn); - rtx set, prev, prev_set; - rtx note; - - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') - { - /* If this is a CLOBBER of VAR, delete it. - - If it has a REG_LIBCALL note, delete the REG_LIBCALL - and REG_RETVAL notes too. */ - if (GET_CODE (PATTERN (insn)) == CLOBBER - && (XEXP (PATTERN (insn), 0) == var - || (GET_CODE (XEXP (PATTERN (insn), 0)) == CONCAT - && (XEXP (XEXP (PATTERN (insn), 0), 0) == var - || XEXP (XEXP (PATTERN (insn), 0), 1) == var)))) - { - if ((note = find_reg_note (insn, REG_LIBCALL, NULL_RTX)) != 0) - /* The REG_LIBCALL note will go away since we are going to - turn INSN into a NOTE, so just delete the - corresponding REG_RETVAL note. */ - remove_note (XEXP (note, 0), - find_reg_note (XEXP (note, 0), REG_RETVAL, - NULL_RTX)); - - /* In unoptimized compilation, we shouldn't call delete_insn - except in jump.c doing warnings. */ - PUT_CODE (insn, NOTE); - NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (insn) = 0; - } - - /* The insn to load VAR from a home in the arglist - is now a no-op. When we see it, just delete it. - Similarly if this is storing VAR from a register from which - it was loaded in the previous insn. This will occur - when an ADDRESSOF was made for an arglist slot. */ - else if (toplevel - && (set = single_set (insn)) != 0 - && SET_DEST (set) == var - /* If this represents the result of an insn group, - don't delete the insn. */ - && find_reg_note (insn, REG_RETVAL, NULL_RTX) == 0 - && (rtx_equal_p (SET_SRC (set), var) - || (GET_CODE (SET_SRC (set)) == REG - && (prev = prev_nonnote_insn (insn)) != 0 - && (prev_set = single_set (prev)) != 0 - && SET_DEST (prev_set) == SET_SRC (set) - && rtx_equal_p (SET_SRC (prev_set), var)))) - { - /* In unoptimized compilation, we shouldn't call delete_insn - except in jump.c doing warnings. */ - PUT_CODE (insn, NOTE); - NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (insn) = 0; - if (insn == last_parm_insn) - last_parm_insn = PREV_INSN (next); - } - else - { - struct fixup_replacement *replacements = 0; - rtx next_insn = NEXT_INSN (insn); - - if (SMALL_REGISTER_CLASSES) - { - /* If the insn that copies the results of a CALL_INSN - into a pseudo now references VAR, we have to use an - intermediate pseudo since we want the life of the - return value register to be only a single insn. - - If we don't use an intermediate pseudo, such things as - address computations to make the address of VAR valid - if it is not can be placed between the CALL_INSN and INSN. - - To make sure this doesn't happen, we record the destination - of the CALL_INSN and see if the next insn uses both that - and VAR. */ - - if (call_dest != 0 && GET_CODE (insn) == INSN - && reg_mentioned_p (var, PATTERN (insn)) - && reg_mentioned_p (call_dest, PATTERN (insn))) - { - rtx temp = gen_reg_rtx (GET_MODE (call_dest)); - - emit_insn_before (gen_move_insn (temp, call_dest), insn); - - PATTERN (insn) = replace_rtx (PATTERN (insn), - call_dest, temp); - } - - if (GET_CODE (insn) == CALL_INSN - && GET_CODE (PATTERN (insn)) == SET) - call_dest = SET_DEST (PATTERN (insn)); - else if (GET_CODE (insn) == CALL_INSN - && GET_CODE (PATTERN (insn)) == PARALLEL - && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET) - call_dest = SET_DEST (XVECEXP (PATTERN (insn), 0, 0)); - else - call_dest = 0; - } - - /* See if we have to do anything to INSN now that VAR is in - memory. If it needs to be loaded into a pseudo, use a single - pseudo for the entire insn in case there is a MATCH_DUP - between two operands. We pass a pointer to the head of - a list of struct fixup_replacements. If fixup_var_refs_1 - needs to allocate pseudos or replacement MEMs (for SUBREGs), - it will record them in this list. - - If it allocated a pseudo for any replacement, we copy into - it here. */ - - fixup_var_refs_1 (var, promoted_mode, &PATTERN (insn), insn, - &replacements); - - /* If this is last_parm_insn, and any instructions were output - after it to fix it up, then we must set last_parm_insn to - the last such instruction emitted. */ - if (insn == last_parm_insn) - last_parm_insn = PREV_INSN (next_insn); - - while (replacements) - { - if (GET_CODE (replacements->new) == REG) - { - rtx insert_before; - rtx seq; - - /* OLD might be a (subreg (mem)). */ - if (GET_CODE (replacements->old) == SUBREG) - replacements->old - = fixup_memory_subreg (replacements->old, insn, 0); - else - replacements->old - = fixup_stack_1 (replacements->old, insn); - - insert_before = insn; - - /* If we are changing the mode, do a conversion. - This might be wasteful, but combine.c will - eliminate much of the waste. */ - - if (GET_MODE (replacements->new) - != GET_MODE (replacements->old)) - { - start_sequence (); - convert_move (replacements->new, - replacements->old, unsignedp); - seq = gen_sequence (); - end_sequence (); - } - else - seq = gen_move_insn (replacements->new, - replacements->old); - - emit_insn_before (seq, insert_before); - } - - replacements = replacements->next; - } - } - - /* Also fix up any invalid exprs in the REG_NOTES of this insn. - But don't touch other insns referred to by reg-notes; - we will get them elsewhere. */ - for (note = REG_NOTES (insn); note; note = XEXP (note, 1)) - if (GET_CODE (note) != INSN_LIST) - XEXP (note, 0) - = walk_fixup_memory_subreg (XEXP (note, 0), insn, 1); - } - insn = next; - } -} - -/* VAR is a MEM that used to be a pseudo register with mode PROMOTED_MODE. - See if the rtx expression at *LOC in INSN needs to be changed. - - REPLACEMENTS is a pointer to a list head that starts out zero, but may - contain a list of original rtx's and replacements. If we find that we need - to modify this insn by replacing a memory reference with a pseudo or by - making a new MEM to implement a SUBREG, we consult that list to see if - we have already chosen a replacement. If none has already been allocated, - we allocate it and update the list. fixup_var_refs_insns will copy VAR - or the SUBREG, as appropriate, to the pseudo. */ - -static void -fixup_var_refs_1 (var, promoted_mode, loc, insn, replacements) - register rtx var; - enum machine_mode promoted_mode; - register rtx *loc; - rtx insn; - struct fixup_replacement **replacements; -{ - register int i; - register rtx x = *loc; - RTX_CODE code = GET_CODE (x); - register char *fmt; - register rtx tem, tem1; - struct fixup_replacement *replacement; - - switch (code) - { - case ADDRESSOF: - if (XEXP (x, 0) == var) - { - /* Prevent sharing of rtl that might lose. */ - rtx sub = copy_rtx (XEXP (var, 0)); - - start_sequence (); - - if (! validate_change (insn, loc, sub, 0)) - { - rtx y = force_operand (sub, NULL_RTX); - - if (! validate_change (insn, loc, y, 0)) - *loc = copy_to_reg (y); - } - - emit_insn_before (gen_sequence (), insn); - end_sequence (); - } - return; - - case MEM: - if (var == x) - { - /* If we already have a replacement, use it. Otherwise, - try to fix up this address in case it is invalid. */ - - replacement = find_fixup_replacement (replacements, var); - if (replacement->new) - { - *loc = replacement->new; - return; - } - - *loc = replacement->new = x = fixup_stack_1 (x, insn); - - /* Unless we are forcing memory to register or we changed the mode, - we can leave things the way they are if the insn is valid. */ - - INSN_CODE (insn) = -1; - if (! flag_force_mem && GET_MODE (x) == promoted_mode - && recog_memoized (insn) >= 0) - return; - - *loc = replacement->new = gen_reg_rtx (promoted_mode); - return; - } - - /* If X contains VAR, we need to unshare it here so that we update - each occurrence separately. But all identical MEMs in one insn - must be replaced with the same rtx because of the possibility of - MATCH_DUPs. */ - - if (reg_mentioned_p (var, x)) - { - replacement = find_fixup_replacement (replacements, x); - if (replacement->new == 0) - replacement->new = copy_most_rtx (x, var); - - *loc = x = replacement->new; - } - break; - - case REG: - case CC0: - case PC: - case CONST_INT: - case CONST: - case SYMBOL_REF: - case LABEL_REF: - case CONST_DOUBLE: - return; - - case SIGN_EXTRACT: - case ZERO_EXTRACT: - /* Note that in some cases those types of expressions are altered - by optimize_bit_field, and do not survive to get here. */ - if (XEXP (x, 0) == var - || (GET_CODE (XEXP (x, 0)) == SUBREG - && SUBREG_REG (XEXP (x, 0)) == var)) - { - /* Get TEM as a valid MEM in the mode presently in the insn. - - We don't worry about the possibility of MATCH_DUP here; it - is highly unlikely and would be tricky to handle. */ - - tem = XEXP (x, 0); - if (GET_CODE (tem) == SUBREG) - { - if (GET_MODE_BITSIZE (GET_MODE (tem)) - > GET_MODE_BITSIZE (GET_MODE (var))) - { - replacement = find_fixup_replacement (replacements, var); - if (replacement->new == 0) - replacement->new = gen_reg_rtx (GET_MODE (var)); - SUBREG_REG (tem) = replacement->new; - } - else - tem = fixup_memory_subreg (tem, insn, 0); - } - else - tem = fixup_stack_1 (tem, insn); - - /* Unless we want to load from memory, get TEM into the proper mode - for an extract from memory. This can only be done if the - extract is at a constant position and length. */ - - if (! flag_force_mem && GET_CODE (XEXP (x, 1)) == CONST_INT - && GET_CODE (XEXP (x, 2)) == CONST_INT - && ! mode_dependent_address_p (XEXP (tem, 0)) - && ! MEM_VOLATILE_P (tem)) - { - enum machine_mode wanted_mode = VOIDmode; - enum machine_mode is_mode = GET_MODE (tem); - HOST_WIDE_INT pos = INTVAL (XEXP (x, 2)); - -#ifdef HAVE_extzv - if (GET_CODE (x) == ZERO_EXTRACT) - { - wanted_mode = insn_operand_mode[(int) CODE_FOR_extzv][1]; - if (wanted_mode == VOIDmode) - wanted_mode = word_mode; - } -#endif -#ifdef HAVE_extv - if (GET_CODE (x) == SIGN_EXTRACT) - { - wanted_mode = insn_operand_mode[(int) CODE_FOR_extv][1]; - if (wanted_mode == VOIDmode) - wanted_mode = word_mode; - } -#endif - /* If we have a narrower mode, we can do something. */ - if (wanted_mode != VOIDmode - && GET_MODE_SIZE (wanted_mode) < GET_MODE_SIZE (is_mode)) - { - HOST_WIDE_INT offset = pos / BITS_PER_UNIT; - rtx old_pos = XEXP (x, 2); - rtx newmem; - - /* If the bytes and bits are counted differently, we - must adjust the offset. */ - if (BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN) - offset = (GET_MODE_SIZE (is_mode) - - GET_MODE_SIZE (wanted_mode) - offset); - - pos %= GET_MODE_BITSIZE (wanted_mode); - - newmem = gen_rtx_MEM (wanted_mode, - plus_constant (XEXP (tem, 0), offset)); - RTX_UNCHANGING_P (newmem) = RTX_UNCHANGING_P (tem); - MEM_COPY_ATTRIBUTES (newmem, tem); - - /* Make the change and see if the insn remains valid. */ - INSN_CODE (insn) = -1; - XEXP (x, 0) = newmem; - XEXP (x, 2) = GEN_INT (pos); - - if (recog_memoized (insn) >= 0) - return; - - /* Otherwise, restore old position. XEXP (x, 0) will be - restored later. */ - XEXP (x, 2) = old_pos; - } - } - - /* If we get here, the bitfield extract insn can't accept a memory - reference. Copy the input into a register. */ - - tem1 = gen_reg_rtx (GET_MODE (tem)); - emit_insn_before (gen_move_insn (tem1, tem), insn); - XEXP (x, 0) = tem1; - return; - } - break; - - case SUBREG: - if (SUBREG_REG (x) == var) - { - /* If this is a special SUBREG made because VAR was promoted - from a wider mode, replace it with VAR and call ourself - recursively, this time saying that the object previously - had its current mode (by virtue of the SUBREG). */ - - if (SUBREG_PROMOTED_VAR_P (x)) - { - *loc = var; - fixup_var_refs_1 (var, GET_MODE (var), loc, insn, replacements); - return; - } - - /* If this SUBREG makes VAR wider, it has become a paradoxical - SUBREG with VAR in memory, but these aren't allowed at this - stage of the compilation. So load VAR into a pseudo and take - a SUBREG of that pseudo. */ - if (GET_MODE_SIZE (GET_MODE (x)) > GET_MODE_SIZE (GET_MODE (var))) - { - replacement = find_fixup_replacement (replacements, var); - if (replacement->new == 0) - replacement->new = gen_reg_rtx (GET_MODE (var)); - SUBREG_REG (x) = replacement->new; - return; - } - - /* See if we have already found a replacement for this SUBREG. - If so, use it. Otherwise, make a MEM and see if the insn - is recognized. If not, or if we should force MEM into a register, - make a pseudo for this SUBREG. */ - replacement = find_fixup_replacement (replacements, x); - if (replacement->new) - { - *loc = replacement->new; - return; - } - - replacement->new = *loc = fixup_memory_subreg (x, insn, 0); - - INSN_CODE (insn) = -1; - if (! flag_force_mem && recog_memoized (insn) >= 0) - return; - - *loc = replacement->new = gen_reg_rtx (GET_MODE (x)); - return; - } - break; - - case SET: - /* First do special simplification of bit-field references. */ - if (GET_CODE (SET_DEST (x)) == SIGN_EXTRACT - || GET_CODE (SET_DEST (x)) == ZERO_EXTRACT) - optimize_bit_field (x, insn, 0); - if (GET_CODE (SET_SRC (x)) == SIGN_EXTRACT - || GET_CODE (SET_SRC (x)) == ZERO_EXTRACT) - optimize_bit_field (x, insn, NULL_PTR); - - /* For a paradoxical SUBREG inside a ZERO_EXTRACT, load the object - into a register and then store it back out. */ - if (GET_CODE (SET_DEST (x)) == ZERO_EXTRACT - && GET_CODE (XEXP (SET_DEST (x), 0)) == SUBREG - && SUBREG_REG (XEXP (SET_DEST (x), 0)) == var - && (GET_MODE_SIZE (GET_MODE (XEXP (SET_DEST (x), 0))) - > GET_MODE_SIZE (GET_MODE (var)))) - { - replacement = find_fixup_replacement (replacements, var); - if (replacement->new == 0) - replacement->new = gen_reg_rtx (GET_MODE (var)); - - SUBREG_REG (XEXP (SET_DEST (x), 0)) = replacement->new; - emit_insn_after (gen_move_insn (var, replacement->new), insn); - } - - /* If SET_DEST is now a paradoxical SUBREG, put the result of this - insn into a pseudo and store the low part of the pseudo into VAR. */ - if (GET_CODE (SET_DEST (x)) == SUBREG - && SUBREG_REG (SET_DEST (x)) == var - && (GET_MODE_SIZE (GET_MODE (SET_DEST (x))) - > GET_MODE_SIZE (GET_MODE (var)))) - { - SET_DEST (x) = tem = gen_reg_rtx (GET_MODE (SET_DEST (x))); - emit_insn_after (gen_move_insn (var, gen_lowpart (GET_MODE (var), - tem)), - insn); - break; - } - - { - rtx dest = SET_DEST (x); - rtx src = SET_SRC (x); -#ifdef HAVE_insv - rtx outerdest = dest; -#endif - - while (GET_CODE (dest) == SUBREG || GET_CODE (dest) == STRICT_LOW_PART - || GET_CODE (dest) == SIGN_EXTRACT - || GET_CODE (dest) == ZERO_EXTRACT) - dest = XEXP (dest, 0); - - if (GET_CODE (src) == SUBREG) - src = XEXP (src, 0); - - /* If VAR does not appear at the top level of the SET - just scan the lower levels of the tree. */ - - if (src != var && dest != var) - break; - - /* We will need to rerecognize this insn. */ - INSN_CODE (insn) = -1; - -#ifdef HAVE_insv - if (GET_CODE (outerdest) == ZERO_EXTRACT && dest == var) - { - /* Since this case will return, ensure we fixup all the - operands here. */ - fixup_var_refs_1 (var, promoted_mode, &XEXP (outerdest, 1), - insn, replacements); - fixup_var_refs_1 (var, promoted_mode, &XEXP (outerdest, 2), - insn, replacements); - fixup_var_refs_1 (var, promoted_mode, &SET_SRC (x), - insn, replacements); - - tem = XEXP (outerdest, 0); - - /* Clean up (SUBREG:SI (MEM:mode ...) 0) - that may appear inside a ZERO_EXTRACT. - This was legitimate when the MEM was a REG. */ - if (GET_CODE (tem) == SUBREG - && SUBREG_REG (tem) == var) - tem = fixup_memory_subreg (tem, insn, 0); - else - tem = fixup_stack_1 (tem, insn); - - if (GET_CODE (XEXP (outerdest, 1)) == CONST_INT - && GET_CODE (XEXP (outerdest, 2)) == CONST_INT - && ! mode_dependent_address_p (XEXP (tem, 0)) - && ! MEM_VOLATILE_P (tem)) - { - enum machine_mode wanted_mode; - enum machine_mode is_mode = GET_MODE (tem); - HOST_WIDE_INT pos = INTVAL (XEXP (outerdest, 2)); - - wanted_mode = insn_operand_mode[(int) CODE_FOR_insv][0]; - if (wanted_mode == VOIDmode) - wanted_mode = word_mode; - - /* If we have a narrower mode, we can do something. */ - if (GET_MODE_SIZE (wanted_mode) < GET_MODE_SIZE (is_mode)) - { - HOST_WIDE_INT offset = pos / BITS_PER_UNIT; - rtx old_pos = XEXP (outerdest, 2); - rtx newmem; - - if (BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN) - offset = (GET_MODE_SIZE (is_mode) - - GET_MODE_SIZE (wanted_mode) - offset); - - pos %= GET_MODE_BITSIZE (wanted_mode); - - newmem = gen_rtx_MEM (wanted_mode, - plus_constant (XEXP (tem, 0), offset)); - RTX_UNCHANGING_P (newmem) = RTX_UNCHANGING_P (tem); - MEM_COPY_ATTRIBUTES (newmem, tem); - - /* Make the change and see if the insn remains valid. */ - INSN_CODE (insn) = -1; - XEXP (outerdest, 0) = newmem; - XEXP (outerdest, 2) = GEN_INT (pos); - - if (recog_memoized (insn) >= 0) - return; - - /* Otherwise, restore old position. XEXP (x, 0) will be - restored later. */ - XEXP (outerdest, 2) = old_pos; - } - } - - /* If we get here, the bit-field store doesn't allow memory - or isn't located at a constant position. Load the value into - a register, do the store, and put it back into memory. */ - - tem1 = gen_reg_rtx (GET_MODE (tem)); - emit_insn_before (gen_move_insn (tem1, tem), insn); - emit_insn_after (gen_move_insn (tem, tem1), insn); - XEXP (outerdest, 0) = tem1; - return; - } -#endif - - /* STRICT_LOW_PART is a no-op on memory references - and it can cause combinations to be unrecognizable, - so eliminate it. */ - - if (dest == var && GET_CODE (SET_DEST (x)) == STRICT_LOW_PART) - SET_DEST (x) = XEXP (SET_DEST (x), 0); - - /* A valid insn to copy VAR into or out of a register - must be left alone, to avoid an infinite loop here. - If the reference to VAR is by a subreg, fix that up, - since SUBREG is not valid for a memref. - Also fix up the address of the stack slot. - - Note that we must not try to recognize the insn until - after we know that we have valid addresses and no - (subreg (mem ...) ...) constructs, since these interfere - with determining the validity of the insn. */ - - if ((SET_SRC (x) == var - || (GET_CODE (SET_SRC (x)) == SUBREG - && SUBREG_REG (SET_SRC (x)) == var)) - && (GET_CODE (SET_DEST (x)) == REG - || (GET_CODE (SET_DEST (x)) == SUBREG - && GET_CODE (SUBREG_REG (SET_DEST (x))) == REG)) - && GET_MODE (var) == promoted_mode - && x == single_set (insn)) - { - rtx pat; - - replacement = find_fixup_replacement (replacements, SET_SRC (x)); - if (replacement->new) - SET_SRC (x) = replacement->new; - else if (GET_CODE (SET_SRC (x)) == SUBREG) - SET_SRC (x) = replacement->new - = fixup_memory_subreg (SET_SRC (x), insn, 0); - else - SET_SRC (x) = replacement->new - = fixup_stack_1 (SET_SRC (x), insn); - - if (recog_memoized (insn) >= 0) - return; - - /* INSN is not valid, but we know that we want to - copy SET_SRC (x) to SET_DEST (x) in some way. So - we generate the move and see whether it requires more - than one insn. If it does, we emit those insns and - delete INSN. Otherwise, we an just replace the pattern - of INSN; we have already verified above that INSN has - no other function that to do X. */ - - pat = gen_move_insn (SET_DEST (x), SET_SRC (x)); - if (GET_CODE (pat) == SEQUENCE) - { - emit_insn_after (pat, insn); - PUT_CODE (insn, NOTE); - NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (insn) = 0; - } - else - PATTERN (insn) = pat; - - return; - } - - if ((SET_DEST (x) == var - || (GET_CODE (SET_DEST (x)) == SUBREG - && SUBREG_REG (SET_DEST (x)) == var)) - && (GET_CODE (SET_SRC (x)) == REG - || (GET_CODE (SET_SRC (x)) == SUBREG - && GET_CODE (SUBREG_REG (SET_SRC (x))) == REG)) - && GET_MODE (var) == promoted_mode - && x == single_set (insn)) - { - rtx pat; - - if (GET_CODE (SET_DEST (x)) == SUBREG) - SET_DEST (x) = fixup_memory_subreg (SET_DEST (x), insn, 0); - else - SET_DEST (x) = fixup_stack_1 (SET_DEST (x), insn); - - if (recog_memoized (insn) >= 0) - return; - - pat = gen_move_insn (SET_DEST (x), SET_SRC (x)); - if (GET_CODE (pat) == SEQUENCE) - { - emit_insn_after (pat, insn); - PUT_CODE (insn, NOTE); - NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (insn) = 0; - } - else - PATTERN (insn) = pat; - - return; - } - - /* Otherwise, storing into VAR must be handled specially - by storing into a temporary and copying that into VAR - with a new insn after this one. Note that this case - will be used when storing into a promoted scalar since - the insn will now have different modes on the input - and output and hence will be invalid (except for the case - of setting it to a constant, which does not need any - change if it is valid). We generate extra code in that case, - but combine.c will eliminate it. */ - - if (dest == var) - { - rtx temp; - rtx fixeddest = SET_DEST (x); - - /* STRICT_LOW_PART can be discarded, around a MEM. */ - if (GET_CODE (fixeddest) == STRICT_LOW_PART) - fixeddest = XEXP (fixeddest, 0); - /* Convert (SUBREG (MEM)) to a MEM in a changed mode. */ - if (GET_CODE (fixeddest) == SUBREG) - { - fixeddest = fixup_memory_subreg (fixeddest, insn, 0); - promoted_mode = GET_MODE (fixeddest); - } - else - fixeddest = fixup_stack_1 (fixeddest, insn); - - temp = gen_reg_rtx (promoted_mode); - - emit_insn_after (gen_move_insn (fixeddest, - gen_lowpart (GET_MODE (fixeddest), - temp)), - insn); - - SET_DEST (x) = temp; - } - } - - default: - break; - } - - /* Nothing special about this RTX; fix its operands. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - fixup_var_refs_1 (var, promoted_mode, &XEXP (x, i), insn, replacements); - if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - fixup_var_refs_1 (var, promoted_mode, &XVECEXP (x, i, j), - insn, replacements); - } - } -} - -/* Given X, an rtx of the form (SUBREG:m1 (MEM:m2 addr)), - return an rtx (MEM:m1 newaddr) which is equivalent. - If any insns must be emitted to compute NEWADDR, put them before INSN. - - UNCRITICAL nonzero means accept paradoxical subregs. - This is used for subregs found inside REG_NOTES. */ - -static rtx -fixup_memory_subreg (x, insn, uncritical) - rtx x; - rtx insn; - int uncritical; -{ - int offset = SUBREG_WORD (x) * UNITS_PER_WORD; - rtx addr = XEXP (SUBREG_REG (x), 0); - enum machine_mode mode = GET_MODE (x); - rtx result; - - /* Paradoxical SUBREGs are usually invalid during RTL generation. */ - if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))) - && ! uncritical) - abort (); - - if (BYTES_BIG_ENDIAN) - offset += (MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) - - MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode))); - addr = plus_constant (addr, offset); - if (!flag_force_addr && memory_address_p (mode, addr)) - /* Shortcut if no insns need be emitted. */ - return change_address (SUBREG_REG (x), mode, addr); - start_sequence (); - result = change_address (SUBREG_REG (x), mode, addr); - emit_insn_before (gen_sequence (), insn); - end_sequence (); - return result; -} - -/* Do fixup_memory_subreg on all (SUBREG (MEM ...) ...) contained in X. - Replace subexpressions of X in place. - If X itself is a (SUBREG (MEM ...) ...), return the replacement expression. - Otherwise return X, with its contents possibly altered. - - If any insns must be emitted to compute NEWADDR, put them before INSN. - - UNCRITICAL is as in fixup_memory_subreg. */ - -static rtx -walk_fixup_memory_subreg (x, insn, uncritical) - register rtx x; - rtx insn; - int uncritical; -{ - register enum rtx_code code; - register char *fmt; - register int i; - - if (x == 0) - return 0; - - code = GET_CODE (x); - - if (code == SUBREG && GET_CODE (SUBREG_REG (x)) == MEM) - return fixup_memory_subreg (x, insn, uncritical); - - /* Nothing special about this RTX; fix its operands. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - XEXP (x, i) = walk_fixup_memory_subreg (XEXP (x, i), insn, uncritical); - if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - XVECEXP (x, i, j) - = walk_fixup_memory_subreg (XVECEXP (x, i, j), insn, uncritical); - } - } - return x; -} - -/* For each memory ref within X, if it refers to a stack slot - with an out of range displacement, put the address in a temp register - (emitting new insns before INSN to load these registers) - and alter the memory ref to use that register. - Replace each such MEM rtx with a copy, to avoid clobberage. */ - -static rtx -fixup_stack_1 (x, insn) - rtx x; - rtx insn; -{ - register int i; - register RTX_CODE code = GET_CODE (x); - register char *fmt; - - if (code == MEM) - { - register rtx ad = XEXP (x, 0); - /* If we have address of a stack slot but it's not valid - (displacement is too large), compute the sum in a register. */ - if (GET_CODE (ad) == PLUS - && GET_CODE (XEXP (ad, 0)) == REG - && ((REGNO (XEXP (ad, 0)) >= FIRST_VIRTUAL_REGISTER - && REGNO (XEXP (ad, 0)) <= LAST_VIRTUAL_REGISTER) - || REGNO (XEXP (ad, 0)) == FRAME_POINTER_REGNUM -#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM - || REGNO (XEXP (ad, 0)) == HARD_FRAME_POINTER_REGNUM -#endif - || REGNO (XEXP (ad, 0)) == STACK_POINTER_REGNUM - || REGNO (XEXP (ad, 0)) == ARG_POINTER_REGNUM - || XEXP (ad, 0) == current_function_internal_arg_pointer) - && GET_CODE (XEXP (ad, 1)) == CONST_INT) - { - rtx temp, seq; - if (memory_address_p (GET_MODE (x), ad)) - return x; - - start_sequence (); - temp = copy_to_reg (ad); - seq = gen_sequence (); - end_sequence (); - emit_insn_before (seq, insn); - return change_address (x, VOIDmode, temp); - } - return x; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - XEXP (x, i) = fixup_stack_1 (XEXP (x, i), insn); - if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - XVECEXP (x, i, j) = fixup_stack_1 (XVECEXP (x, i, j), insn); - } - } - return x; -} - -/* Optimization: a bit-field instruction whose field - happens to be a byte or halfword in memory - can be changed to a move instruction. - - We call here when INSN is an insn to examine or store into a bit-field. - BODY is the SET-rtx to be altered. - - EQUIV_MEM is the table `reg_equiv_mem' if that is available; else 0. - (Currently this is called only from function.c, and EQUIV_MEM - is always 0.) */ - -static void -optimize_bit_field (body, insn, equiv_mem) - rtx body; - rtx insn; - rtx *equiv_mem; -{ - register rtx bitfield; - int destflag; - rtx seq = 0; - enum machine_mode mode; - - if (GET_CODE (SET_DEST (body)) == SIGN_EXTRACT - || GET_CODE (SET_DEST (body)) == ZERO_EXTRACT) - bitfield = SET_DEST (body), destflag = 1; - else - bitfield = SET_SRC (body), destflag = 0; - - /* First check that the field being stored has constant size and position - and is in fact a byte or halfword suitably aligned. */ - - if (GET_CODE (XEXP (bitfield, 1)) == CONST_INT - && GET_CODE (XEXP (bitfield, 2)) == CONST_INT - && ((mode = mode_for_size (INTVAL (XEXP (bitfield, 1)), MODE_INT, 1)) - != BLKmode) - && INTVAL (XEXP (bitfield, 2)) % INTVAL (XEXP (bitfield, 1)) == 0) - { - register rtx memref = 0; - - /* Now check that the containing word is memory, not a register, - and that it is safe to change the machine mode. */ - - if (GET_CODE (XEXP (bitfield, 0)) == MEM) - memref = XEXP (bitfield, 0); - else if (GET_CODE (XEXP (bitfield, 0)) == REG - && equiv_mem != 0) - memref = equiv_mem[REGNO (XEXP (bitfield, 0))]; - else if (GET_CODE (XEXP (bitfield, 0)) == SUBREG - && GET_CODE (SUBREG_REG (XEXP (bitfield, 0))) == MEM) - memref = SUBREG_REG (XEXP (bitfield, 0)); - else if (GET_CODE (XEXP (bitfield, 0)) == SUBREG - && equiv_mem != 0 - && GET_CODE (SUBREG_REG (XEXP (bitfield, 0))) == REG) - memref = equiv_mem[REGNO (SUBREG_REG (XEXP (bitfield, 0)))]; - - if (memref - && ! mode_dependent_address_p (XEXP (memref, 0)) - && ! MEM_VOLATILE_P (memref)) - { - /* Now adjust the address, first for any subreg'ing - that we are now getting rid of, - and then for which byte of the word is wanted. */ - - HOST_WIDE_INT offset = INTVAL (XEXP (bitfield, 2)); - rtx insns; - - /* Adjust OFFSET to count bits from low-address byte. */ - if (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN) - offset = (GET_MODE_BITSIZE (GET_MODE (XEXP (bitfield, 0))) - - offset - INTVAL (XEXP (bitfield, 1))); - - /* Adjust OFFSET to count bytes from low-address byte. */ - offset /= BITS_PER_UNIT; - if (GET_CODE (XEXP (bitfield, 0)) == SUBREG) - { - offset += SUBREG_WORD (XEXP (bitfield, 0)) * UNITS_PER_WORD; - if (BYTES_BIG_ENDIAN) - offset -= (MIN (UNITS_PER_WORD, - GET_MODE_SIZE (GET_MODE (XEXP (bitfield, 0)))) - - MIN (UNITS_PER_WORD, - GET_MODE_SIZE (GET_MODE (memref)))); - } - - start_sequence (); - memref = change_address (memref, mode, - plus_constant (XEXP (memref, 0), offset)); - insns = get_insns (); - end_sequence (); - emit_insns_before (insns, insn); - - /* Store this memory reference where - we found the bit field reference. */ - - if (destflag) - { - validate_change (insn, &SET_DEST (body), memref, 1); - if (! CONSTANT_ADDRESS_P (SET_SRC (body))) - { - rtx src = SET_SRC (body); - while (GET_CODE (src) == SUBREG - && SUBREG_WORD (src) == 0) - src = SUBREG_REG (src); - if (GET_MODE (src) != GET_MODE (memref)) - src = gen_lowpart (GET_MODE (memref), SET_SRC (body)); - validate_change (insn, &SET_SRC (body), src, 1); - } - else if (GET_MODE (SET_SRC (body)) != VOIDmode - && GET_MODE (SET_SRC (body)) != GET_MODE (memref)) - /* This shouldn't happen because anything that didn't have - one of these modes should have got converted explicitly - and then referenced through a subreg. - This is so because the original bit-field was - handled by agg_mode and so its tree structure had - the same mode that memref now has. */ - abort (); - } - else - { - rtx dest = SET_DEST (body); - - while (GET_CODE (dest) == SUBREG - && SUBREG_WORD (dest) == 0 - && (GET_MODE_CLASS (GET_MODE (dest)) - == GET_MODE_CLASS (GET_MODE (SUBREG_REG (dest))))) - dest = SUBREG_REG (dest); - - validate_change (insn, &SET_DEST (body), dest, 1); - - if (GET_MODE (dest) == GET_MODE (memref)) - validate_change (insn, &SET_SRC (body), memref, 1); - else - { - /* Convert the mem ref to the destination mode. */ - rtx newreg = gen_reg_rtx (GET_MODE (dest)); - - start_sequence (); - convert_move (newreg, memref, - GET_CODE (SET_SRC (body)) == ZERO_EXTRACT); - seq = get_insns (); - end_sequence (); - - validate_change (insn, &SET_SRC (body), newreg, 1); - } - } - - /* See if we can convert this extraction or insertion into - a simple move insn. We might not be able to do so if this - was, for example, part of a PARALLEL. - - If we succeed, write out any needed conversions. If we fail, - it is hard to guess why we failed, so don't do anything - special; just let the optimization be suppressed. */ - - if (apply_change_group () && seq) - emit_insns_before (seq, insn); - } - } -} - -/* These routines are responsible for converting virtual register references - to the actual hard register references once RTL generation is complete. - - The following four variables are used for communication between the - routines. They contain the offsets of the virtual registers from their - respective hard registers. */ - -static int in_arg_offset; -static int var_offset; -static int dynamic_offset; -static int out_arg_offset; -static int cfa_offset; - -/* In most machines, the stack pointer register is equivalent to the bottom - of the stack. */ - -#ifndef STACK_POINTER_OFFSET -#define STACK_POINTER_OFFSET 0 -#endif - -/* If not defined, pick an appropriate default for the offset of dynamically - allocated memory depending on the value of ACCUMULATE_OUTGOING_ARGS, - REG_PARM_STACK_SPACE, and OUTGOING_REG_PARM_STACK_SPACE. */ - -#ifndef STACK_DYNAMIC_OFFSET - -#ifdef ACCUMULATE_OUTGOING_ARGS -/* The bottom of the stack points to the actual arguments. If - REG_PARM_STACK_SPACE is defined, this includes the space for the register - parameters. However, if OUTGOING_REG_PARM_STACK space is not defined, - stack space for register parameters is not pushed by the caller, but - rather part of the fixed stack areas and hence not included in - `current_function_outgoing_args_size'. Nevertheless, we must allow - for it when allocating stack dynamic objects. */ - -#if defined(REG_PARM_STACK_SPACE) && ! defined(OUTGOING_REG_PARM_STACK_SPACE) -#define STACK_DYNAMIC_OFFSET(FNDECL) \ -(current_function_outgoing_args_size \ - + REG_PARM_STACK_SPACE (FNDECL) + (STACK_POINTER_OFFSET)) - -#else -#define STACK_DYNAMIC_OFFSET(FNDECL) \ -(current_function_outgoing_args_size + (STACK_POINTER_OFFSET)) -#endif - -#else -#define STACK_DYNAMIC_OFFSET(FNDECL) STACK_POINTER_OFFSET -#endif -#endif - -/* On a few machines, the CFA coincides with the arg pointer. */ - -#ifndef ARG_POINTER_CFA_OFFSET -#define ARG_POINTER_CFA_OFFSET 0 -#endif - - -/* Build up a (MEM (ADDRESSOF (REG))) rtx for a register REG that just had - its address taken. DECL is the decl for the object stored in the - register, for later use if we do need to force REG into the stack. - REG is overwritten by the MEM like in put_reg_into_stack. */ - -rtx -gen_mem_addressof (reg, decl) - rtx reg; - tree decl; -{ - tree type = TREE_TYPE (decl); - rtx r = gen_rtx_ADDRESSOF (Pmode, gen_reg_rtx (GET_MODE (reg)), REGNO (reg)); - SET_ADDRESSOF_DECL (r, decl); - /* If the original REG was a user-variable, then so is the REG whose - address is being taken. */ - REG_USERVAR_P (XEXP (r, 0)) = REG_USERVAR_P (reg); - - XEXP (reg, 0) = r; - PUT_CODE (reg, MEM); - PUT_MODE (reg, DECL_MODE (decl)); - MEM_VOLATILE_P (reg) = TREE_SIDE_EFFECTS (decl); - MEM_SET_IN_STRUCT_P (reg, AGGREGATE_TYPE_P (type)); - MEM_ALIAS_SET (reg) = get_alias_set (decl); - - if (TREE_USED (decl) || DECL_INITIAL (decl) != 0) - fixup_var_refs (reg, GET_MODE (reg), TREE_UNSIGNED (type)); - - return reg; -} - -/* If DECL has an RTL that is an ADDRESSOF rtx, put it into the stack. */ - -void -flush_addressof (decl) - tree decl; -{ - if ((TREE_CODE (decl) == PARM_DECL || TREE_CODE (decl) == VAR_DECL) - && DECL_RTL (decl) != 0 - && GET_CODE (DECL_RTL (decl)) == MEM - && GET_CODE (XEXP (DECL_RTL (decl), 0)) == ADDRESSOF - && GET_CODE (XEXP (XEXP (DECL_RTL (decl), 0), 0)) == REG) - put_addressof_into_stack (XEXP (DECL_RTL (decl), 0)); -} - -/* Force the register pointed to by R, an ADDRESSOF rtx, into the stack. */ - -static void -put_addressof_into_stack (r) - rtx r; -{ - tree decl = ADDRESSOF_DECL (r); - rtx reg = XEXP (r, 0); - - if (GET_CODE (reg) != REG) - abort (); - - put_reg_into_stack (0, reg, TREE_TYPE (decl), GET_MODE (reg), - DECL_MODE (decl), TREE_SIDE_EFFECTS (decl), - ADDRESSOF_REGNO (r), - TREE_USED (decl) || DECL_INITIAL (decl) != 0); -} - -/* List of replacements made below in purge_addressof_1 when creating - bitfield insertions. */ -static rtx purge_addressof_replacements; - -/* Helper function for purge_addressof. See if the rtx expression at *LOC - in INSN needs to be changed. If FORCE, always put any ADDRESSOFs into - the stack. */ - -static void -purge_addressof_1 (loc, insn, force, store) - rtx *loc; - rtx insn; - int force, store; -{ - rtx x; - RTX_CODE code; - int i, j; - char *fmt; - - /* Re-start here to avoid recursion in common cases. */ - restart: - - x = *loc; - if (x == 0) - return; - - code = GET_CODE (x); - - if (code == ADDRESSOF && GET_CODE (XEXP (x, 0)) == MEM) - { - rtx insns; - /* We must create a copy of the rtx because it was created by - overwriting a REG rtx which is always shared. */ - rtx sub = copy_rtx (XEXP (XEXP (x, 0), 0)); - - if (validate_change (insn, loc, sub, 0)) - return; - - start_sequence (); - if (! validate_change (insn, loc, - force_operand (sub, NULL_RTX), - 0)) - abort (); - - insns = gen_sequence (); - end_sequence (); - emit_insn_before (insns, insn); - return; - } - else if (code == MEM && GET_CODE (XEXP (x, 0)) == ADDRESSOF && ! force) - { - rtx sub = XEXP (XEXP (x, 0), 0); - - if (GET_CODE (sub) == MEM) - sub = gen_rtx_MEM (GET_MODE (x), copy_rtx (XEXP (sub, 0))); - - if (GET_CODE (sub) == REG - && (MEM_VOLATILE_P (x) || GET_MODE (x) == BLKmode)) - { - put_addressof_into_stack (XEXP (x, 0)); - return; - } - else if (GET_CODE (sub) == REG && GET_MODE (x) != GET_MODE (sub)) - { - int size_x, size_sub; - - if (!insn) - { - /* When processing REG_NOTES look at the list of - replacements done on the insn to find the register that X - was replaced by. */ - rtx tem; - - for (tem = purge_addressof_replacements; tem != NULL_RTX; - tem = XEXP (XEXP (tem, 1), 1)) - { - rtx y = XEXP (tem, 0); - if (GET_CODE (y) == MEM - && rtx_equal_p (XEXP (x, 0), XEXP (y, 0))) - { - /* It can happen that the note may speak of things in - a wider (or just different) mode than the code did. - This is especially true of REG_RETVAL. */ - - rtx z = XEXP (XEXP (tem, 1), 0); - if (GET_MODE (x) != GET_MODE (y)) - { - if (GET_CODE (z) == SUBREG && SUBREG_WORD (z) == 0) - z = SUBREG_REG (z); - - /* ??? If we'd gotten into any of the really complex - cases below, I'm not sure we can do a proper - replacement. Might we be able to delete the - note in some cases? */ - if (GET_MODE_SIZE (GET_MODE (x)) - < GET_MODE_SIZE (GET_MODE (y))) - abort (); - - if (GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD - && (GET_MODE_SIZE (GET_MODE (x)) - > GET_MODE_SIZE (GET_MODE (z)))) - { - /* This can occur as a result in invalid - pointer casts, e.g. float f; ... - *(long long int *)&f. - ??? We could emit a warning here, but - without a line number that wouldn't be - very helpful. */ - z = gen_rtx_SUBREG (GET_MODE (x), z, 0); - } - else - z = gen_lowpart (GET_MODE (x), z); - } - - *loc = z; - return; - } - } - - /* There should always be such a replacement. */ - abort (); - } - - size_x = GET_MODE_BITSIZE (GET_MODE (x)); - size_sub = GET_MODE_BITSIZE (GET_MODE (sub)); - - /* Don't even consider working with paradoxical subregs, - or the moral equivalent seen here. */ - if (size_x <= size_sub - && int_mode_for_mode (GET_MODE (sub)) != BLKmode) - { - /* Do a bitfield insertion to mirror what would happen - in memory. */ - - rtx val, seq; - - if (store) - { - rtx p; - - start_sequence (); - val = gen_reg_rtx (GET_MODE (x)); - if (! validate_change (insn, loc, val, 0)) - { - /* Discard the current sequence and put the - ADDRESSOF on stack. */ - end_sequence (); - goto give_up; - } - seq = gen_sequence (); - end_sequence (); - emit_insn_before (seq, insn); - - start_sequence (); - store_bit_field (sub, size_x, 0, GET_MODE (x), - val, GET_MODE_SIZE (GET_MODE (sub)), - GET_MODE_SIZE (GET_MODE (sub))); - - /* Make sure to unshare any shared rtl that store_bit_field - might have created. */ - for (p = get_insns(); p; p = NEXT_INSN (p)) - { - reset_used_flags (PATTERN (p)); - reset_used_flags (REG_NOTES (p)); - reset_used_flags (LOG_LINKS (p)); - } - unshare_all_rtl (get_insns ()); - - seq = gen_sequence (); - end_sequence (); - emit_insn_after (seq, insn); - } - else - { - start_sequence (); - val = extract_bit_field (sub, size_x, 0, 1, NULL_RTX, - GET_MODE (x), GET_MODE (x), - GET_MODE_SIZE (GET_MODE (sub)), - GET_MODE_SIZE (GET_MODE (sub))); - - if (! validate_change (insn, loc, val, 0)) - { - /* Discard the current sequence and put the - ADDRESSOF on stack. */ - end_sequence (); - goto give_up; - } - - seq = gen_sequence (); - end_sequence (); - emit_insn_before (seq, insn); - } - - /* Remember the replacement so that the same one can be done - on the REG_NOTES. */ - purge_addressof_replacements - = gen_rtx_EXPR_LIST (VOIDmode, x, - gen_rtx_EXPR_LIST (VOIDmode, val, - purge_addressof_replacements)); - - /* We replaced with a reg -- all done. */ - return; - } - } - else if (validate_change (insn, loc, sub, 0)) - { - /* Remember the replacement so that the same one can be done - on the REG_NOTES. */ - purge_addressof_replacements - = gen_rtx_EXPR_LIST (VOIDmode, x, - gen_rtx_EXPR_LIST (VOIDmode, sub, - purge_addressof_replacements)); - goto restart; - } - give_up:; - /* else give up and put it into the stack */ - } - else if (code == ADDRESSOF) - { - put_addressof_into_stack (x); - return; - } - else if (code == SET) - { - purge_addressof_1 (&SET_DEST (x), insn, force, 1); - purge_addressof_1 (&SET_SRC (x), insn, force, 0); - return; - } - else if (code == CALL) - { - purge_addressof_1 (&XEXP (x, 0), insn, 1, 0); - purge_addressof_1 (&XEXP (x, 1), insn, force, 0); - return; - } - - /* Scan all subexpressions. */ - fmt = GET_RTX_FORMAT (code); - for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++) - { - if (*fmt == 'e') - purge_addressof_1 (&XEXP (x, i), insn, force, 0); - else if (*fmt == 'E') - for (j = 0; j < XVECLEN (x, i); j++) - purge_addressof_1 (&XVECEXP (x, i, j), insn, force, 0); - } -} - -/* Eliminate all occurrences of ADDRESSOF from INSNS. Elide any remaining - (MEM (ADDRESSOF)) patterns, and force any needed registers into the - stack. */ - -void -purge_addressof (insns) - rtx insns; -{ - rtx insn; - for (insn = insns; insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN - || GET_CODE (insn) == CALL_INSN) - { - purge_addressof_1 (&PATTERN (insn), insn, - asm_noperands (PATTERN (insn)) > 0, 0); - purge_addressof_1 (®_NOTES (insn), NULL_RTX, 0, 0); - } - purge_addressof_replacements = 0; -} - -/* Pass through the INSNS of function FNDECL and convert virtual register - references to hard register references. */ - -void -instantiate_virtual_regs (fndecl, insns) - tree fndecl; - rtx insns; -{ - rtx insn; - int i; - - /* Compute the offsets to use for this function. */ - in_arg_offset = FIRST_PARM_OFFSET (fndecl); - var_offset = STARTING_FRAME_OFFSET; - dynamic_offset = STACK_DYNAMIC_OFFSET (fndecl); - out_arg_offset = STACK_POINTER_OFFSET; - cfa_offset = ARG_POINTER_CFA_OFFSET; - - /* Scan all variables and parameters of this function. For each that is - in memory, instantiate all virtual registers if the result is a valid - address. If not, we do it later. That will handle most uses of virtual - regs on many machines. */ - instantiate_decls (fndecl, 1); - - /* Initialize recognition, indicating that volatile is OK. */ - init_recog (); - - /* Scan through all the insns, instantiating every virtual register still - present. */ - for (insn = insns; insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN - || GET_CODE (insn) == CALL_INSN) - { - instantiate_virtual_regs_1 (&PATTERN (insn), insn, 1); - instantiate_virtual_regs_1 (®_NOTES (insn), NULL_RTX, 0); - } - - /* Instantiate the stack slots for the parm registers, for later use in - addressof elimination. */ - for (i = 0; i < max_parm_reg; ++i) - if (parm_reg_stack_loc[i]) - instantiate_virtual_regs_1 (&parm_reg_stack_loc[i], NULL_RTX, 0); - - /* Now instantiate the remaining register equivalences for debugging info. - These will not be valid addresses. */ - instantiate_decls (fndecl, 0); - - /* Indicate that, from now on, assign_stack_local should use - frame_pointer_rtx. */ - virtuals_instantiated = 1; -} - -/* Scan all decls in FNDECL (both variables and parameters) and instantiate - all virtual registers in their DECL_RTL's. - - If VALID_ONLY, do this only if the resulting address is still valid. - Otherwise, always do it. */ - -static void -instantiate_decls (fndecl, valid_only) - tree fndecl; - int valid_only; -{ - tree decl; - - if (DECL_SAVED_INSNS (fndecl)) - /* When compiling an inline function, the obstack used for - rtl allocation is the maybepermanent_obstack. Calling - `resume_temporary_allocation' switches us back to that - obstack while we process this function's parameters. */ - resume_temporary_allocation (); - - /* Process all parameters of the function. */ - for (decl = DECL_ARGUMENTS (fndecl); decl; decl = TREE_CHAIN (decl)) - { - HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (decl)); - - instantiate_decl (DECL_RTL (decl), size, valid_only); - - /* If the parameter was promoted, then the incoming RTL mode may be - larger than the declared type size. We must use the larger of - the two sizes. */ - size = MAX (GET_MODE_SIZE (GET_MODE (DECL_INCOMING_RTL (decl))), size); - instantiate_decl (DECL_INCOMING_RTL (decl), size, valid_only); - } - - /* Now process all variables defined in the function or its subblocks. */ - instantiate_decls_1 (DECL_INITIAL (fndecl), valid_only); - - if (DECL_INLINE (fndecl) || DECL_DEFER_OUTPUT (fndecl)) - { - /* Save all rtl allocated for this function by raising the - high-water mark on the maybepermanent_obstack. */ - preserve_data (); - /* All further rtl allocation is now done in the current_obstack. */ - rtl_in_current_obstack (); - } -} - -/* Subroutine of instantiate_decls: Process all decls in the given - BLOCK node and all its subblocks. */ - -static void -instantiate_decls_1 (let, valid_only) - tree let; - int valid_only; -{ - tree t; - - for (t = BLOCK_VARS (let); t; t = TREE_CHAIN (t)) - instantiate_decl (DECL_RTL (t), int_size_in_bytes (TREE_TYPE (t)), - valid_only); - - /* Process all subblocks. */ - for (t = BLOCK_SUBBLOCKS (let); t; t = TREE_CHAIN (t)) - instantiate_decls_1 (t, valid_only); -} - -/* Subroutine of the preceding procedures: Given RTL representing a - decl and the size of the object, do any instantiation required. - - If VALID_ONLY is non-zero, it means that the RTL should only be - changed if the new address is valid. */ - -static void -instantiate_decl (x, size, valid_only) - rtx x; - int size; - int valid_only; -{ - enum machine_mode mode; - rtx addr; - - /* If this is not a MEM, no need to do anything. Similarly if the - address is a constant or a register that is not a virtual register. */ - - if (x == 0 || GET_CODE (x) != MEM) - return; - - addr = XEXP (x, 0); - if (CONSTANT_P (addr) - || (GET_CODE (addr) == ADDRESSOF && GET_CODE (XEXP (addr, 0)) == REG) - || (GET_CODE (addr) == REG - && (REGNO (addr) < FIRST_VIRTUAL_REGISTER - || REGNO (addr) > LAST_VIRTUAL_REGISTER))) - return; - - /* If we should only do this if the address is valid, copy the address. - We need to do this so we can undo any changes that might make the - address invalid. This copy is unfortunate, but probably can't be - avoided. */ - - if (valid_only) - addr = copy_rtx (addr); - - instantiate_virtual_regs_1 (&addr, NULL_RTX, 0); - - if (valid_only) - { - /* Now verify that the resulting address is valid for every integer or - floating-point mode up to and including SIZE bytes long. We do this - since the object might be accessed in any mode and frame addresses - are shared. */ - - for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); - mode != VOIDmode && GET_MODE_SIZE (mode) <= size; - mode = GET_MODE_WIDER_MODE (mode)) - if (! memory_address_p (mode, addr)) - return; - - for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); - mode != VOIDmode && GET_MODE_SIZE (mode) <= size; - mode = GET_MODE_WIDER_MODE (mode)) - if (! memory_address_p (mode, addr)) - return; - } - - /* Put back the address now that we have updated it and we either know - it is valid or we don't care whether it is valid. */ - - XEXP (x, 0) = addr; -} - -/* Given a pointer to a piece of rtx and an optional pointer to the - containing object, instantiate any virtual registers present in it. - - If EXTRA_INSNS, we always do the replacement and generate - any extra insns before OBJECT. If it zero, we do nothing if replacement - is not valid. - - Return 1 if we either had nothing to do or if we were able to do the - needed replacement. Return 0 otherwise; we only return zero if - EXTRA_INSNS is zero. - - We first try some simple transformations to avoid the creation of extra - pseudos. */ - -static int -instantiate_virtual_regs_1 (loc, object, extra_insns) - rtx *loc; - rtx object; - int extra_insns; -{ - rtx x; - RTX_CODE code; - rtx new = 0; - HOST_WIDE_INT offset; - rtx temp; - rtx seq; - int i, j; - char *fmt; - - /* Re-start here to avoid recursion in common cases. */ - restart: - - x = *loc; - if (x == 0) - return 1; - - code = GET_CODE (x); - - /* Check for some special cases. */ - switch (code) - { - case CONST_INT: - case CONST_DOUBLE: - case CONST: - case SYMBOL_REF: - case CODE_LABEL: - case PC: - case CC0: - case ASM_INPUT: - case ADDR_VEC: - case ADDR_DIFF_VEC: - case RETURN: - return 1; - - case SET: - /* We are allowed to set the virtual registers. This means that - the actual register should receive the source minus the - appropriate offset. This is used, for example, in the handling - of non-local gotos. */ - if (SET_DEST (x) == virtual_incoming_args_rtx) - new = arg_pointer_rtx, offset = - in_arg_offset; - else if (SET_DEST (x) == virtual_stack_vars_rtx) - new = frame_pointer_rtx, offset = - var_offset; - else if (SET_DEST (x) == virtual_stack_dynamic_rtx) - new = stack_pointer_rtx, offset = - dynamic_offset; - else if (SET_DEST (x) == virtual_outgoing_args_rtx) - new = stack_pointer_rtx, offset = - out_arg_offset; - else if (SET_DEST (x) == virtual_cfa_rtx) - new = arg_pointer_rtx, offset = - cfa_offset; - - if (new) - { - /* The only valid sources here are PLUS or REG. Just do - the simplest possible thing to handle them. */ - if (GET_CODE (SET_SRC (x)) != REG - && GET_CODE (SET_SRC (x)) != PLUS) - abort (); - - start_sequence (); - if (GET_CODE (SET_SRC (x)) != REG) - temp = force_operand (SET_SRC (x), NULL_RTX); - else - temp = SET_SRC (x); - temp = force_operand (plus_constant (temp, offset), NULL_RTX); - seq = get_insns (); - end_sequence (); - - emit_insns_before (seq, object); - SET_DEST (x) = new; - - if (! validate_change (object, &SET_SRC (x), temp, 0) - || ! extra_insns) - abort (); - - return 1; - } - - instantiate_virtual_regs_1 (&SET_DEST (x), object, extra_insns); - loc = &SET_SRC (x); - goto restart; - - case PLUS: - /* Handle special case of virtual register plus constant. */ - if (CONSTANT_P (XEXP (x, 1))) - { - rtx old, new_offset; - - /* Check for (plus (plus VIRT foo) (const_int)) first. */ - if (GET_CODE (XEXP (x, 0)) == PLUS) - { - rtx inner = XEXP (XEXP (x, 0), 0); - - if (inner == virtual_incoming_args_rtx) - new = arg_pointer_rtx, offset = in_arg_offset; - else if (inner == virtual_stack_vars_rtx) - new = frame_pointer_rtx, offset = var_offset; - else if (inner == virtual_stack_dynamic_rtx) - new = stack_pointer_rtx, offset = dynamic_offset; - else if (inner == virtual_outgoing_args_rtx) - new = stack_pointer_rtx, offset = out_arg_offset; - else if (inner == virtual_cfa_rtx) - new = arg_pointer_rtx, offset = cfa_offset; - else - { - loc = &XEXP (x, 0); - goto restart; - } - - instantiate_virtual_regs_1 (&XEXP (XEXP (x, 0), 1), object, - extra_insns); - new = gen_rtx_PLUS (Pmode, new, XEXP (XEXP (x, 0), 1)); - } - - else if (XEXP (x, 0) == virtual_incoming_args_rtx) - new = arg_pointer_rtx, offset = in_arg_offset; - else if (XEXP (x, 0) == virtual_stack_vars_rtx) - new = frame_pointer_rtx, offset = var_offset; - else if (XEXP (x, 0) == virtual_stack_dynamic_rtx) - new = stack_pointer_rtx, offset = dynamic_offset; - else if (XEXP (x, 0) == virtual_outgoing_args_rtx) - new = stack_pointer_rtx, offset = out_arg_offset; - else if (XEXP (x, 0) == virtual_cfa_rtx) - new = arg_pointer_rtx, offset = cfa_offset; - else - { - /* We know the second operand is a constant. Unless the - first operand is a REG (which has been already checked), - it needs to be checked. */ - if (GET_CODE (XEXP (x, 0)) != REG) - { - loc = &XEXP (x, 0); - goto restart; - } - return 1; - } - - new_offset = plus_constant (XEXP (x, 1), offset); - - /* If the new constant is zero, try to replace the sum with just - the register. */ - if (new_offset == const0_rtx - && validate_change (object, loc, new, 0)) - return 1; - - /* Next try to replace the register and new offset. - There are two changes to validate here and we can't assume that - in the case of old offset equals new just changing the register - will yield a valid insn. In the interests of a little efficiency, - however, we only call validate change once (we don't queue up the - changes and then call apply_change_group). */ - - old = XEXP (x, 0); - if (offset == 0 - ? ! validate_change (object, &XEXP (x, 0), new, 0) - : (XEXP (x, 0) = new, - ! validate_change (object, &XEXP (x, 1), new_offset, 0))) - { - if (! extra_insns) - { - XEXP (x, 0) = old; - return 0; - } - - /* Otherwise copy the new constant into a register and replace - constant with that register. */ - temp = gen_reg_rtx (Pmode); - XEXP (x, 0) = new; - if (validate_change (object, &XEXP (x, 1), temp, 0)) - emit_insn_before (gen_move_insn (temp, new_offset), object); - else - { - /* If that didn't work, replace this expression with a - register containing the sum. */ - - XEXP (x, 0) = old; - new = gen_rtx_PLUS (Pmode, new, new_offset); - - start_sequence (); - temp = force_operand (new, NULL_RTX); - seq = get_insns (); - end_sequence (); - - emit_insns_before (seq, object); - if (! validate_change (object, loc, temp, 0) - && ! validate_replace_rtx (x, temp, object)) - abort (); - } - } - - return 1; - } - - /* Fall through to generic two-operand expression case. */ - case EXPR_LIST: - case CALL: - case COMPARE: - case MINUS: - case MULT: - case DIV: case UDIV: - case MOD: case UMOD: - case AND: case IOR: case XOR: - case ROTATERT: case ROTATE: - case ASHIFTRT: case LSHIFTRT: case ASHIFT: - case NE: case EQ: - case GE: case GT: case GEU: case GTU: - case LE: case LT: case LEU: case LTU: - if (XEXP (x, 1) && ! CONSTANT_P (XEXP (x, 1))) - instantiate_virtual_regs_1 (&XEXP (x, 1), object, extra_insns); - loc = &XEXP (x, 0); - goto restart; - - case MEM: - /* Most cases of MEM that convert to valid addresses have already been - handled by our scan of decls. The only special handling we - need here is to make a copy of the rtx to ensure it isn't being - shared if we have to change it to a pseudo. - - If the rtx is a simple reference to an address via a virtual register, - it can potentially be shared. In such cases, first try to make it - a valid address, which can also be shared. Otherwise, copy it and - proceed normally. - - First check for common cases that need no processing. These are - usually due to instantiation already being done on a previous instance - of a shared rtx. */ - - temp = XEXP (x, 0); - if (CONSTANT_ADDRESS_P (temp) -#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM - || temp == arg_pointer_rtx -#endif -#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM - || temp == hard_frame_pointer_rtx -#endif - || temp == frame_pointer_rtx) - return 1; - - if (GET_CODE (temp) == PLUS - && CONSTANT_ADDRESS_P (XEXP (temp, 1)) - && (XEXP (temp, 0) == frame_pointer_rtx -#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM - || XEXP (temp, 0) == hard_frame_pointer_rtx -#endif -#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM - || XEXP (temp, 0) == arg_pointer_rtx -#endif - )) - return 1; - - if (temp == virtual_stack_vars_rtx - || temp == virtual_incoming_args_rtx - || (GET_CODE (temp) == PLUS - && CONSTANT_ADDRESS_P (XEXP (temp, 1)) - && (XEXP (temp, 0) == virtual_stack_vars_rtx - || XEXP (temp, 0) == virtual_incoming_args_rtx))) - { - /* This MEM may be shared. If the substitution can be done without - the need to generate new pseudos, we want to do it in place - so all copies of the shared rtx benefit. The call below will - only make substitutions if the resulting address is still - valid. - - Note that we cannot pass X as the object in the recursive call - since the insn being processed may not allow all valid - addresses. However, if we were not passed on object, we can - only modify X without copying it if X will have a valid - address. - - ??? Also note that this can still lose if OBJECT is an insn that - has less restrictions on an address that some other insn. - In that case, we will modify the shared address. This case - doesn't seem very likely, though. One case where this could - happen is in the case of a USE or CLOBBER reference, but we - take care of that below. */ - - if (instantiate_virtual_regs_1 (&XEXP (x, 0), - object ? object : x, 0)) - return 1; - - /* Otherwise make a copy and process that copy. We copy the entire - RTL expression since it might be a PLUS which could also be - shared. */ - *loc = x = copy_rtx (x); - } - - /* Fall through to generic unary operation case. */ - case SUBREG: - case STRICT_LOW_PART: - case NEG: case NOT: - case PRE_DEC: case PRE_INC: case POST_DEC: case POST_INC: - case SIGN_EXTEND: case ZERO_EXTEND: - case TRUNCATE: case FLOAT_EXTEND: case FLOAT_TRUNCATE: - case FLOAT: case FIX: - case UNSIGNED_FIX: case UNSIGNED_FLOAT: - case ABS: - case SQRT: - case FFS: - /* These case either have just one operand or we know that we need not - check the rest of the operands. */ - loc = &XEXP (x, 0); - goto restart; - - case USE: - case CLOBBER: - /* If the operand is a MEM, see if the change is a valid MEM. If not, - go ahead and make the invalid one, but do it to a copy. For a REG, - just make the recursive call, since there's no chance of a problem. */ - - if ((GET_CODE (XEXP (x, 0)) == MEM - && instantiate_virtual_regs_1 (&XEXP (XEXP (x, 0), 0), XEXP (x, 0), - 0)) - || (GET_CODE (XEXP (x, 0)) == REG - && instantiate_virtual_regs_1 (&XEXP (x, 0), object, 0))) - return 1; - - XEXP (x, 0) = copy_rtx (XEXP (x, 0)); - loc = &XEXP (x, 0); - goto restart; - - case REG: - /* Try to replace with a PLUS. If that doesn't work, compute the sum - in front of this insn and substitute the temporary. */ - if (x == virtual_incoming_args_rtx) - new = arg_pointer_rtx, offset = in_arg_offset; - else if (x == virtual_stack_vars_rtx) - new = frame_pointer_rtx, offset = var_offset; - else if (x == virtual_stack_dynamic_rtx) - new = stack_pointer_rtx, offset = dynamic_offset; - else if (x == virtual_outgoing_args_rtx) - new = stack_pointer_rtx, offset = out_arg_offset; - else if (x == virtual_cfa_rtx) - new = arg_pointer_rtx, offset = cfa_offset; - - if (new) - { - temp = plus_constant (new, offset); - if (!validate_change (object, loc, temp, 0)) - { - if (! extra_insns) - return 0; - - start_sequence (); - temp = force_operand (temp, NULL_RTX); - seq = get_insns (); - end_sequence (); - - emit_insns_before (seq, object); - if (! validate_change (object, loc, temp, 0) - && ! validate_replace_rtx (x, temp, object)) - abort (); - } - } - - return 1; - - case ADDRESSOF: - if (GET_CODE (XEXP (x, 0)) == REG) - return 1; - - else if (GET_CODE (XEXP (x, 0)) == MEM) - { - /* If we have a (addressof (mem ..)), do any instantiation inside - since we know we'll be making the inside valid when we finally - remove the ADDRESSOF. */ - instantiate_virtual_regs_1 (&XEXP (XEXP (x, 0), 0), NULL_RTX, 0); - return 1; - } - break; - - default: - break; - } - - /* Scan all subexpressions. */ - fmt = GET_RTX_FORMAT (code); - for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++) - if (*fmt == 'e') - { - if (!instantiate_virtual_regs_1 (&XEXP (x, i), object, extra_insns)) - return 0; - } - else if (*fmt == 'E') - for (j = 0; j < XVECLEN (x, i); j++) - if (! instantiate_virtual_regs_1 (&XVECEXP (x, i, j), object, - extra_insns)) - return 0; - - return 1; -} - -/* Optimization: assuming this function does not receive nonlocal gotos, - delete the handlers for such, as well as the insns to establish - and disestablish them. */ - -static void -delete_handlers () -{ - rtx insn; - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - { - /* Delete the handler by turning off the flag that would - prevent jump_optimize from deleting it. - Also permit deletion of the nonlocal labels themselves - if nothing local refers to them. */ - if (GET_CODE (insn) == CODE_LABEL) - { - tree t, last_t; - - LABEL_PRESERVE_P (insn) = 0; - - /* Remove it from the nonlocal_label list, to avoid confusing - flow. */ - for (t = nonlocal_labels, last_t = 0; t; - last_t = t, t = TREE_CHAIN (t)) - if (DECL_RTL (TREE_VALUE (t)) == insn) - break; - if (t) - { - if (! last_t) - nonlocal_labels = TREE_CHAIN (nonlocal_labels); - else - TREE_CHAIN (last_t) = TREE_CHAIN (t); - } - } - if (GET_CODE (insn) == INSN) - { - int can_delete = 0; - rtx t; - for (t = nonlocal_goto_handler_slots; t != 0; t = XEXP (t, 1)) - if (reg_mentioned_p (t, PATTERN (insn))) - { - can_delete = 1; - break; - } - if (can_delete - || (nonlocal_goto_stack_level != 0 - && reg_mentioned_p (nonlocal_goto_stack_level, - PATTERN (insn)))) - delete_insn (insn); - } - } -} - -/* Return a list (chain of EXPR_LIST nodes) for the nonlocal labels - of the current function. */ - -rtx -nonlocal_label_rtx_list () -{ - tree t; - rtx x = 0; - - for (t = nonlocal_labels; t; t = TREE_CHAIN (t)) - x = gen_rtx_EXPR_LIST (VOIDmode, label_rtx (TREE_VALUE (t)), x); - - return x; -} - -/* Output a USE for any register use in RTL. - This is used with -noreg to mark the extent of lifespan - of any registers used in a user-visible variable's DECL_RTL. */ - -void -use_variable (rtl) - rtx rtl; -{ - if (GET_CODE (rtl) == REG) - /* This is a register variable. */ - emit_insn (gen_rtx_USE (VOIDmode, rtl)); - else if (GET_CODE (rtl) == MEM - && GET_CODE (XEXP (rtl, 0)) == REG - && (REGNO (XEXP (rtl, 0)) < FIRST_VIRTUAL_REGISTER - || REGNO (XEXP (rtl, 0)) > LAST_VIRTUAL_REGISTER) - && XEXP (rtl, 0) != current_function_internal_arg_pointer) - /* This is a variable-sized structure. */ - emit_insn (gen_rtx_USE (VOIDmode, XEXP (rtl, 0))); -} - -/* Like use_variable except that it outputs the USEs after INSN - instead of at the end of the insn-chain. */ - -void -use_variable_after (rtl, insn) - rtx rtl, insn; -{ - if (GET_CODE (rtl) == REG) - /* This is a register variable. */ - emit_insn_after (gen_rtx_USE (VOIDmode, rtl), insn); - else if (GET_CODE (rtl) == MEM - && GET_CODE (XEXP (rtl, 0)) == REG - && (REGNO (XEXP (rtl, 0)) < FIRST_VIRTUAL_REGISTER - || REGNO (XEXP (rtl, 0)) > LAST_VIRTUAL_REGISTER) - && XEXP (rtl, 0) != current_function_internal_arg_pointer) - /* This is a variable-sized structure. */ - emit_insn_after (gen_rtx_USE (VOIDmode, XEXP (rtl, 0)), insn); -} - -int -max_parm_reg_num () -{ - return max_parm_reg; -} - -/* Return the first insn following those generated by `assign_parms'. */ - -rtx -get_first_nonparm_insn () -{ - if (last_parm_insn) - return NEXT_INSN (last_parm_insn); - return get_insns (); -} - -/* Return the first NOTE_INSN_BLOCK_BEG note in the function. - Crash if there is none. */ - -rtx -get_first_block_beg () -{ - register rtx searcher; - register rtx insn = get_first_nonparm_insn (); - - for (searcher = insn; searcher; searcher = NEXT_INSN (searcher)) - if (GET_CODE (searcher) == NOTE - && NOTE_LINE_NUMBER (searcher) == NOTE_INSN_BLOCK_BEG) - return searcher; - - abort (); /* Invalid call to this function. (See comments above.) */ - return NULL_RTX; -} - -/* Return 1 if EXP is an aggregate type (or a value with aggregate type). - This means a type for which function calls must pass an address to the - function or get an address back from the function. - EXP may be a type node or an expression (whose type is tested). */ - -int -aggregate_value_p (exp) - tree exp; -{ - int i, regno, nregs; - rtx reg; - tree type; - if (TREE_CODE_CLASS (TREE_CODE (exp)) == 't') - type = exp; - else - type = TREE_TYPE (exp); - - if (RETURN_IN_MEMORY (type)) - return 1; - /* Types that are TREE_ADDRESSABLE must be constructed in memory, - and thus can't be returned in registers. */ - if (TREE_ADDRESSABLE (type)) - return 1; - if (flag_pcc_struct_return && AGGREGATE_TYPE_P (type)) - return 1; - /* Make sure we have suitable call-clobbered regs to return - the value in; if not, we must return it in memory. */ - reg = hard_function_value (type, 0); - - /* If we have something other than a REG (e.g. a PARALLEL), then assume - it is OK. */ - if (GET_CODE (reg) != REG) - return 0; - - regno = REGNO (reg); - nregs = HARD_REGNO_NREGS (regno, TYPE_MODE (type)); - for (i = 0; i < nregs; i++) - if (! call_used_regs[regno + i]) - return 1; - return 0; -} - -/* Assign RTL expressions to the function's parameters. - This may involve copying them into registers and using - those registers as the RTL for them. - - If SECOND_TIME is non-zero it means that this function is being - called a second time. This is done by integrate.c when a function's - compilation is deferred. We need to come back here in case the - FUNCTION_ARG macro computes items needed for the rest of the compilation - (such as changing which registers are fixed or caller-saved). But suppress - writing any insns or setting DECL_RTL of anything in this case. */ - -void -assign_parms (fndecl, second_time) - tree fndecl; - int second_time; -{ - register tree parm; - register rtx entry_parm = 0; - register rtx stack_parm = 0; - CUMULATIVE_ARGS args_so_far; - enum machine_mode promoted_mode, passed_mode; - enum machine_mode nominal_mode, promoted_nominal_mode; - int unsignedp; - /* Total space needed so far for args on the stack, - given as a constant and a tree-expression. */ - struct args_size stack_args_size; - tree fntype = TREE_TYPE (fndecl); - tree fnargs = DECL_ARGUMENTS (fndecl); - /* This is used for the arg pointer when referring to stack args. */ - rtx internal_arg_pointer; - /* This is a dummy PARM_DECL that we used for the function result if - the function returns a structure. */ - tree function_result_decl = 0; - int varargs_setup = 0; - rtx conversion_insns = 0; - - /* Nonzero if the last arg is named `__builtin_va_alist', - which is used on some machines for old-fashioned non-ANSI varargs.h; - this should be stuck onto the stack as if it had arrived there. */ - int hide_last_arg - = (current_function_varargs - && fnargs - && (parm = tree_last (fnargs)) != 0 - && DECL_NAME (parm) - && (! strcmp (IDENTIFIER_POINTER (DECL_NAME (parm)), - "__builtin_va_alist"))); - - /* Nonzero if function takes extra anonymous args. - This means the last named arg must be on the stack - right before the anonymous ones. */ - int stdarg - = (TYPE_ARG_TYPES (fntype) != 0 - && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) - != void_type_node)); - - current_function_stdarg = stdarg; - - /* If the reg that the virtual arg pointer will be translated into is - not a fixed reg or is the stack pointer, make a copy of the virtual - arg pointer, and address parms via the copy. The frame pointer is - considered fixed even though it is not marked as such. - - The second time through, simply use ap to avoid generating rtx. */ - - if ((ARG_POINTER_REGNUM == STACK_POINTER_REGNUM - || ! (fixed_regs[ARG_POINTER_REGNUM] - || ARG_POINTER_REGNUM == FRAME_POINTER_REGNUM)) - && ! second_time) - internal_arg_pointer = copy_to_reg (virtual_incoming_args_rtx); - else - internal_arg_pointer = virtual_incoming_args_rtx; - current_function_internal_arg_pointer = internal_arg_pointer; - - stack_args_size.constant = 0; - stack_args_size.var = 0; - - /* If struct value address is treated as the first argument, make it so. */ - if (aggregate_value_p (DECL_RESULT (fndecl)) - && ! current_function_returns_pcc_struct - && struct_value_incoming_rtx == 0) - { - tree type = build_pointer_type (TREE_TYPE (fntype)); - - function_result_decl = build_decl (PARM_DECL, NULL_TREE, type); - - DECL_ARG_TYPE (function_result_decl) = type; - TREE_CHAIN (function_result_decl) = fnargs; - fnargs = function_result_decl; - } - - max_parm_reg = LAST_VIRTUAL_REGISTER + 1; - parm_reg_stack_loc = (rtx *) savealloc (max_parm_reg * sizeof (rtx)); - zero_memory ((char *) parm_reg_stack_loc, max_parm_reg * sizeof (rtx)); - -#ifdef INIT_CUMULATIVE_INCOMING_ARGS - INIT_CUMULATIVE_INCOMING_ARGS (args_so_far, fntype, NULL_RTX); -#else - INIT_CUMULATIVE_ARGS (args_so_far, fntype, NULL_RTX, 0); -#endif - - /* We haven't yet found an argument that we must push and pretend the - caller did. */ - current_function_pretend_args_size = 0; - - for (parm = fnargs; parm; parm = TREE_CHAIN (parm)) - { - int aggregate = AGGREGATE_TYPE_P (TREE_TYPE (parm)); - struct args_size stack_offset; - struct args_size arg_size; - int passed_pointer = 0; - int did_conversion = 0; - tree passed_type = DECL_ARG_TYPE (parm); - tree nominal_type = TREE_TYPE (parm); - - /* Set LAST_NAMED if this is last named arg before some - anonymous args. */ - int last_named = ((TREE_CHAIN (parm) == 0 - || DECL_NAME (TREE_CHAIN (parm)) == 0) - && (stdarg || current_function_varargs)); - /* Set NAMED_ARG if this arg should be treated as a named arg. For - most machines, if this is a varargs/stdarg function, then we treat - the last named arg as if it were anonymous too. */ - int named_arg = STRICT_ARGUMENT_NAMING ? 1 : ! last_named; - - if (TREE_TYPE (parm) == error_mark_node - /* This can happen after weird syntax errors - or if an enum type is defined among the parms. */ - || TREE_CODE (parm) != PARM_DECL - || passed_type == NULL) - { - DECL_INCOMING_RTL (parm) = DECL_RTL (parm) - = gen_rtx_MEM (BLKmode, const0_rtx); - TREE_USED (parm) = 1; - continue; - } - - /* For varargs.h function, save info about regs and stack space - used by the individual args, not including the va_alist arg. */ - if (hide_last_arg && last_named) - current_function_args_info = args_so_far; - - /* Find mode of arg as it is passed, and mode of arg - as it should be during execution of this function. */ - passed_mode = TYPE_MODE (passed_type); - nominal_mode = TYPE_MODE (nominal_type); - - /* If the parm's mode is VOID, its value doesn't matter, - and avoid the usual things like emit_move_insn that could crash. */ - if (nominal_mode == VOIDmode) - { - DECL_INCOMING_RTL (parm) = DECL_RTL (parm) = const0_rtx; - continue; - } - - /* If the parm is to be passed as a transparent union, use the - type of the first field for the tests below. We have already - verified that the modes are the same. */ - if (DECL_TRANSPARENT_UNION (parm) - || TYPE_TRANSPARENT_UNION (passed_type)) - passed_type = TREE_TYPE (TYPE_FIELDS (passed_type)); - - /* See if this arg was passed by invisible reference. It is if - it is an object whose size depends on the contents of the - object itself or if the machine requires these objects be passed - that way. */ - - if ((TREE_CODE (TYPE_SIZE (passed_type)) != INTEGER_CST - && contains_placeholder_p (TYPE_SIZE (passed_type))) - || TREE_ADDRESSABLE (passed_type) -#ifdef FUNCTION_ARG_PASS_BY_REFERENCE - || FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, passed_mode, - passed_type, named_arg) -#endif - ) - { - passed_type = nominal_type = build_pointer_type (passed_type); - passed_pointer = 1; - passed_mode = nominal_mode = Pmode; - } - - promoted_mode = passed_mode; - -#ifdef PROMOTE_FUNCTION_ARGS - /* Compute the mode in which the arg is actually extended to. */ - unsignedp = TREE_UNSIGNED (passed_type); - promoted_mode = promote_mode (passed_type, promoted_mode, &unsignedp, 1); -#endif - - /* Let machine desc say which reg (if any) the parm arrives in. - 0 means it arrives on the stack. */ -#ifdef FUNCTION_INCOMING_ARG - entry_parm = FUNCTION_INCOMING_ARG (args_so_far, promoted_mode, - passed_type, named_arg); -#else - entry_parm = FUNCTION_ARG (args_so_far, promoted_mode, - passed_type, named_arg); -#endif - - if (entry_parm == 0) - promoted_mode = passed_mode; - -#ifdef SETUP_INCOMING_VARARGS - /* If this is the last named parameter, do any required setup for - varargs or stdargs. We need to know about the case of this being an - addressable type, in which case we skip the registers it - would have arrived in. - - For stdargs, LAST_NAMED will be set for two parameters, the one that - is actually the last named, and the dummy parameter. We only - want to do this action once. - - Also, indicate when RTL generation is to be suppressed. */ - if (last_named && !varargs_setup) - { - SETUP_INCOMING_VARARGS (args_so_far, promoted_mode, passed_type, - current_function_pretend_args_size, - second_time); - varargs_setup = 1; - } -#endif - - /* Determine parm's home in the stack, - in case it arrives in the stack or we should pretend it did. - - Compute the stack position and rtx where the argument arrives - and its size. - - There is one complexity here: If this was a parameter that would - have been passed in registers, but wasn't only because it is - __builtin_va_alist, we want locate_and_pad_parm to treat it as if - it came in a register so that REG_PARM_STACK_SPACE isn't skipped. - In this case, we call FUNCTION_ARG with NAMED set to 1 instead of - 0 as it was the previous time. */ - - locate_and_pad_parm (promoted_mode, passed_type, -#ifdef STACK_PARMS_IN_REG_PARM_AREA - 1, -#else -#ifdef FUNCTION_INCOMING_ARG - FUNCTION_INCOMING_ARG (args_so_far, promoted_mode, - passed_type, - (named_arg - || varargs_setup)) != 0, -#else - FUNCTION_ARG (args_so_far, promoted_mode, - passed_type, - named_arg || varargs_setup) != 0, -#endif -#endif - fndecl, &stack_args_size, &stack_offset, &arg_size); - - if (! second_time) - { - rtx offset_rtx = ARGS_SIZE_RTX (stack_offset); - - if (offset_rtx == const0_rtx) - stack_parm = gen_rtx_MEM (promoted_mode, internal_arg_pointer); - else - stack_parm = gen_rtx_MEM (promoted_mode, - gen_rtx_PLUS (Pmode, - internal_arg_pointer, - offset_rtx)); - - /* If this is a memory ref that contains aggregate components, - mark it as such for cse and loop optimize. Likewise if it - is readonly. */ - MEM_SET_IN_STRUCT_P (stack_parm, aggregate); - RTX_UNCHANGING_P (stack_parm) = TREE_READONLY (parm); - MEM_ALIAS_SET (stack_parm) = get_alias_set (parm); - } - - /* If this parameter was passed both in registers and in the stack, - use the copy on the stack. */ - if (MUST_PASS_IN_STACK (promoted_mode, passed_type)) - entry_parm = 0; - -#ifdef FUNCTION_ARG_PARTIAL_NREGS - /* If this parm was passed part in regs and part in memory, - pretend it arrived entirely in memory - by pushing the register-part onto the stack. - - In the special case of a DImode or DFmode that is split, - we could put it together in a pseudoreg directly, - but for now that's not worth bothering with. */ - - if (entry_parm) - { - int nregs = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, promoted_mode, - passed_type, named_arg); - - if (nregs > 0) - { - current_function_pretend_args_size - = (((nregs * UNITS_PER_WORD) + (PARM_BOUNDARY / BITS_PER_UNIT) - 1) - / (PARM_BOUNDARY / BITS_PER_UNIT) - * (PARM_BOUNDARY / BITS_PER_UNIT)); - - if (! second_time) - { - /* Handle calls that pass values in multiple non-contiguous - locations. The Irix 6 ABI has examples of this. */ - if (GET_CODE (entry_parm) == PARALLEL) - emit_group_store (validize_mem (stack_parm), entry_parm, - int_size_in_bytes (TREE_TYPE (parm)), - (TYPE_ALIGN (TREE_TYPE (parm)) - / BITS_PER_UNIT)); - else - move_block_from_reg (REGNO (entry_parm), - validize_mem (stack_parm), nregs, - int_size_in_bytes (TREE_TYPE (parm))); - } - entry_parm = stack_parm; - } - } -#endif - - /* If we didn't decide this parm came in a register, - by default it came on the stack. */ - if (entry_parm == 0) - entry_parm = stack_parm; - - /* Record permanently how this parm was passed. */ - if (! second_time) - DECL_INCOMING_RTL (parm) = entry_parm; - - /* If there is actually space on the stack for this parm, - count it in stack_args_size; otherwise set stack_parm to 0 - to indicate there is no preallocated stack slot for the parm. */ - - if (entry_parm == stack_parm -#if defined (REG_PARM_STACK_SPACE) && ! defined (MAYBE_REG_PARM_STACK_SPACE) - /* On some machines, even if a parm value arrives in a register - there is still an (uninitialized) stack slot allocated for it. - - ??? When MAYBE_REG_PARM_STACK_SPACE is defined, we can't tell - whether this parameter already has a stack slot allocated, - because an arg block exists only if current_function_args_size - is larger than some threshold, and we haven't calculated that - yet. So, for now, we just assume that stack slots never exist - in this case. */ - || REG_PARM_STACK_SPACE (fndecl) > 0 -#endif - ) - { - stack_args_size.constant += arg_size.constant; - if (arg_size.var) - ADD_PARM_SIZE (stack_args_size, arg_size.var); - } - else - /* No stack slot was pushed for this parm. */ - stack_parm = 0; - - /* Update info on where next arg arrives in registers. */ - - FUNCTION_ARG_ADVANCE (args_so_far, promoted_mode, - passed_type, named_arg); - - /* If this is our second time through, we are done with this parm. */ - if (second_time) - continue; - - /* If we can't trust the parm stack slot to be aligned enough - for its ultimate type, don't use that slot after entry. - We'll make another stack slot, if we need one. */ - { - int thisparm_boundary - = FUNCTION_ARG_BOUNDARY (promoted_mode, passed_type); - - if (GET_MODE_ALIGNMENT (nominal_mode) > thisparm_boundary) - stack_parm = 0; - } - - /* If parm was passed in memory, and we need to convert it on entry, - don't store it back in that same slot. */ - if (entry_parm != 0 - && nominal_mode != BLKmode && nominal_mode != passed_mode) - stack_parm = 0; - -#if 0 - /* Now adjust STACK_PARM to the mode and precise location - where this parameter should live during execution, - if we discover that it must live in the stack during execution. - To make debuggers happier on big-endian machines, we store - the value in the last bytes of the space available. */ - - if (nominal_mode != BLKmode && nominal_mode != passed_mode - && stack_parm != 0) - { - rtx offset_rtx; - - if (BYTES_BIG_ENDIAN - && GET_MODE_SIZE (nominal_mode) < UNITS_PER_WORD) - stack_offset.constant += (GET_MODE_SIZE (passed_mode) - - GET_MODE_SIZE (nominal_mode)); - - offset_rtx = ARGS_SIZE_RTX (stack_offset); - if (offset_rtx == const0_rtx) - stack_parm = gen_rtx_MEM (nominal_mode, internal_arg_pointer); - else - stack_parm = gen_rtx_MEM (nominal_mode, - gen_rtx_PLUS (Pmode, - internal_arg_pointer, - offset_rtx)); - - /* If this is a memory ref that contains aggregate components, - mark it as such for cse and loop optimize. */ - MEM_SET_IN_STRUCT_P (stack_parm, aggregate); - } -#endif /* 0 */ - -#ifdef STACK_REGS - /* We need this "use" info, because the gcc-register->stack-register - converter in reg-stack.c needs to know which registers are active - at the start of the function call. The actual parameter loading - instructions are not always available then anymore, since they might - have been optimised away. */ - - if (GET_CODE (entry_parm) == REG && !(hide_last_arg && last_named)) - emit_insn (gen_rtx_USE (GET_MODE (entry_parm), entry_parm)); -#endif - - /* ENTRY_PARM is an RTX for the parameter as it arrives, - in the mode in which it arrives. - STACK_PARM is an RTX for a stack slot where the parameter can live - during the function (in case we want to put it there). - STACK_PARM is 0 if no stack slot was pushed for it. - - Now output code if necessary to convert ENTRY_PARM to - the type in which this function declares it, - and store that result in an appropriate place, - which may be a pseudo reg, may be STACK_PARM, - or may be a local stack slot if STACK_PARM is 0. - - Set DECL_RTL to that place. */ - - if (nominal_mode == BLKmode || GET_CODE (entry_parm) == PARALLEL) - { - /* If a BLKmode arrives in registers, copy it to a stack slot. - Handle calls that pass values in multiple non-contiguous - locations. The Irix 6 ABI has examples of this. */ - if (GET_CODE (entry_parm) == REG - || GET_CODE (entry_parm) == PARALLEL) - { - int size_stored - = CEIL_ROUND (int_size_in_bytes (TREE_TYPE (parm)), - UNITS_PER_WORD); - - /* Note that we will be storing an integral number of words. - So we have to be careful to ensure that we allocate an - integral number of words. We do this below in the - assign_stack_local if space was not allocated in the argument - list. If it was, this will not work if PARM_BOUNDARY is not - a multiple of BITS_PER_WORD. It isn't clear how to fix this - if it becomes a problem. */ - - if (stack_parm == 0) - { - stack_parm - = assign_stack_local (GET_MODE (entry_parm), - size_stored, 0); - - /* If this is a memory ref that contains aggregate - components, mark it as such for cse and loop optimize. */ - MEM_SET_IN_STRUCT_P (stack_parm, aggregate); - } - - else if (PARM_BOUNDARY % BITS_PER_WORD != 0) - abort (); - - if (TREE_READONLY (parm)) - RTX_UNCHANGING_P (stack_parm) = 1; - - /* Handle calls that pass values in multiple non-contiguous - locations. The Irix 6 ABI has examples of this. */ - if (GET_CODE (entry_parm) == PARALLEL) - emit_group_store (validize_mem (stack_parm), entry_parm, - int_size_in_bytes (TREE_TYPE (parm)), - (TYPE_ALIGN (TREE_TYPE (parm)) - / BITS_PER_UNIT)); - else - move_block_from_reg (REGNO (entry_parm), - validize_mem (stack_parm), - size_stored / UNITS_PER_WORD, - int_size_in_bytes (TREE_TYPE (parm))); - } - DECL_RTL (parm) = stack_parm; - } - else if (! ((obey_regdecls && ! DECL_REGISTER (parm) - && ! DECL_INLINE (fndecl)) - /* layout_decl may set this. */ - || TREE_ADDRESSABLE (parm) - || TREE_SIDE_EFFECTS (parm) - /* If -ffloat-store specified, don't put explicit - float variables into registers. */ - || (flag_float_store - && TREE_CODE (TREE_TYPE (parm)) == REAL_TYPE)) - /* Always assign pseudo to structure return or item passed - by invisible reference. */ - || passed_pointer || parm == function_result_decl) - { - /* Store the parm in a pseudoregister during the function, but we - may need to do it in a wider mode. */ - - register rtx parmreg; - int regno, regnoi = 0, regnor = 0; - - unsignedp = TREE_UNSIGNED (TREE_TYPE (parm)); - - promoted_nominal_mode - = promote_mode (TREE_TYPE (parm), nominal_mode, &unsignedp, 0); - - parmreg = gen_reg_rtx (promoted_nominal_mode); - mark_user_reg (parmreg); - - /* If this was an item that we received a pointer to, set DECL_RTL - appropriately. */ - if (passed_pointer) - { - DECL_RTL (parm) - = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (passed_type)), parmreg); - MEM_SET_IN_STRUCT_P (DECL_RTL (parm), aggregate); - } - else - DECL_RTL (parm) = parmreg; - - /* Copy the value into the register. */ - if (nominal_mode != passed_mode - || promoted_nominal_mode != promoted_mode) - { - int save_tree_used; - /* ENTRY_PARM has been converted to PROMOTED_MODE, its - mode, by the caller. We now have to convert it to - NOMINAL_MODE, if different. However, PARMREG may be in - a different mode than NOMINAL_MODE if it is being stored - promoted. - - If ENTRY_PARM is a hard register, it might be in a register - not valid for operating in its mode (e.g., an odd-numbered - register for a DFmode). In that case, moves are the only - thing valid, so we can't do a convert from there. This - occurs when the calling sequence allow such misaligned - usages. - - In addition, the conversion may involve a call, which could - clobber parameters which haven't been copied to pseudo - registers yet. Therefore, we must first copy the parm to - a pseudo reg here, and save the conversion until after all - parameters have been moved. */ - - rtx tempreg = gen_reg_rtx (GET_MODE (entry_parm)); - - emit_move_insn (tempreg, validize_mem (entry_parm)); - - push_to_sequence (conversion_insns); - tempreg = convert_to_mode (nominal_mode, tempreg, unsignedp); - - /* TREE_USED gets set erroneously during expand_assignment. */ - save_tree_used = TREE_USED (parm); - expand_assignment (parm, - make_tree (nominal_type, tempreg), 0, 0); - TREE_USED (parm) = save_tree_used; - conversion_insns = get_insns (); - did_conversion = 1; - end_sequence (); - } - else - emit_move_insn (parmreg, validize_mem (entry_parm)); - - /* If we were passed a pointer but the actual value - can safely live in a register, put it in one. */ - if (passed_pointer && TYPE_MODE (TREE_TYPE (parm)) != BLKmode - /* CYGNUS LOCAL -- FUNCTION_ARG_KEEP_AS_REFERENCE/meissner */ -#ifdef FUNCTION_ARG_KEEP_AS_REFERENCE - && !FUNCTION_ARG_KEEP_AS_REFERENCE (args_so_far, passed_mode, - passed_type, ! last_named) -#endif - /* END CYGNUS LOCAL */ - && ! ((obey_regdecls && ! DECL_REGISTER (parm) - && ! DECL_INLINE (fndecl)) - /* layout_decl may set this. */ - || TREE_ADDRESSABLE (parm) - || TREE_SIDE_EFFECTS (parm) - /* If -ffloat-store specified, don't put explicit - float variables into registers. */ - || (flag_float_store - && TREE_CODE (TREE_TYPE (parm)) == REAL_TYPE))) - { - /* We can't use nominal_mode, because it will have been set to - Pmode above. We must use the actual mode of the parm. */ - parmreg = gen_reg_rtx (TYPE_MODE (TREE_TYPE (parm))); - mark_user_reg (parmreg); - emit_move_insn (parmreg, DECL_RTL (parm)); - DECL_RTL (parm) = parmreg; - /* STACK_PARM is the pointer, not the parm, and PARMREG is - now the parm. */ - stack_parm = 0; - } -#ifdef FUNCTION_ARG_CALLEE_COPIES - /* If we are passed an arg by reference and it is our responsibility - to make a copy, do it now. - PASSED_TYPE and PASSED mode now refer to the pointer, not the - original argument, so we must recreate them in the call to - FUNCTION_ARG_CALLEE_COPIES. */ - /* ??? Later add code to handle the case that if the argument isn't - modified, don't do the copy. */ - - else if (passed_pointer - && FUNCTION_ARG_CALLEE_COPIES (args_so_far, - TYPE_MODE (DECL_ARG_TYPE (parm)), - DECL_ARG_TYPE (parm), - named_arg) - && ! TREE_ADDRESSABLE (DECL_ARG_TYPE (parm))) - { - rtx copy; - tree type = DECL_ARG_TYPE (parm); - - /* This sequence may involve a library call perhaps clobbering - registers that haven't been copied to pseudos yet. */ - - push_to_sequence (conversion_insns); - - if (TYPE_SIZE (type) == 0 - || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST) - /* This is a variable sized object. */ - copy = gen_rtx_MEM (BLKmode, - allocate_dynamic_stack_space - (expr_size (parm), NULL_RTX, - TYPE_ALIGN (type))); - else - copy = assign_stack_temp (TYPE_MODE (type), - int_size_in_bytes (type), 1); - MEM_SET_IN_STRUCT_P (copy, AGGREGATE_TYPE_P (type)); - RTX_UNCHANGING_P (copy) = TREE_READONLY (parm); - - store_expr (parm, copy, 0); - emit_move_insn (parmreg, XEXP (copy, 0)); - if (current_function_check_memory_usage) - emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3, - XEXP (copy, 0), ptr_mode, - GEN_INT (int_size_in_bytes (type)), - TYPE_MODE (sizetype), - GEN_INT (MEMORY_USE_RW), - TYPE_MODE (integer_type_node)); - conversion_insns = get_insns (); - did_conversion = 1; - end_sequence (); - } -#endif /* FUNCTION_ARG_CALLEE_COPIES */ - - /* In any case, record the parm's desired stack location - in case we later discover it must live in the stack. - - If it is a COMPLEX value, store the stack location for both - halves. */ - - if (GET_CODE (parmreg) == CONCAT) - regno = MAX (REGNO (XEXP (parmreg, 0)), REGNO (XEXP (parmreg, 1))); - else - regno = REGNO (parmreg); - - if (regno >= max_parm_reg) - { - rtx *new; - int old_max_parm_reg = max_parm_reg; - - /* It's slow to expand this one register at a time, - but it's also rare and we need max_parm_reg to be - precisely correct. */ - max_parm_reg = regno + 1; - new = (rtx *) savealloc (max_parm_reg * sizeof (rtx)); - copy_memory ((char *) parm_reg_stack_loc, (char *) new, - old_max_parm_reg * sizeof (rtx)); - zero_memory ((char *) (new + old_max_parm_reg), - (max_parm_reg - old_max_parm_reg) * sizeof (rtx)); - parm_reg_stack_loc = new; - } - - if (GET_CODE (parmreg) == CONCAT) - { - enum machine_mode submode = GET_MODE (XEXP (parmreg, 0)); - - regnor = REGNO (gen_realpart (submode, parmreg)); - regnoi = REGNO (gen_imagpart (submode, parmreg)); - - if (stack_parm != 0) - { - parm_reg_stack_loc[regnor] - = gen_realpart (submode, stack_parm); - parm_reg_stack_loc[regnoi] - = gen_imagpart (submode, stack_parm); - } - else - { - parm_reg_stack_loc[regnor] = 0; - parm_reg_stack_loc[regnoi] = 0; - } - } - else - parm_reg_stack_loc[REGNO (parmreg)] = stack_parm; - - /* Mark the register as eliminable if we did no conversion - and it was copied from memory at a fixed offset, - and the arg pointer was not copied to a pseudo-reg. - If the arg pointer is a pseudo reg or the offset formed - an invalid address, such memory-equivalences - as we make here would screw up life analysis for it. */ - if (nominal_mode == passed_mode - && ! did_conversion - && stack_parm != 0 - && GET_CODE (stack_parm) == MEM - && stack_offset.var == 0 - && reg_mentioned_p (virtual_incoming_args_rtx, - XEXP (stack_parm, 0))) - { - rtx linsn = get_last_insn (); - rtx sinsn, set; - - /* Mark complex types separately. */ - if (GET_CODE (parmreg) == CONCAT) - /* Scan backwards for the set of the real and - imaginary parts. */ - for (sinsn = linsn; sinsn != 0; - sinsn = prev_nonnote_insn (sinsn)) - { - set = single_set (sinsn); - if (set != 0 - && SET_DEST (set) == regno_reg_rtx [regnoi]) - REG_NOTES (sinsn) - = gen_rtx_EXPR_LIST (REG_EQUIV, - parm_reg_stack_loc[regnoi], - REG_NOTES (sinsn)); - else if (set != 0 - && SET_DEST (set) == regno_reg_rtx [regnor]) - REG_NOTES (sinsn) - = gen_rtx_EXPR_LIST (REG_EQUIV, - parm_reg_stack_loc[regnor], - REG_NOTES (sinsn)); - } - else if ((set = single_set (linsn)) != 0 - && SET_DEST (set) == parmreg) - REG_NOTES (linsn) - = gen_rtx_EXPR_LIST (REG_EQUIV, - stack_parm, REG_NOTES (linsn)); - } - - /* For pointer data type, suggest pointer register. */ - if (POINTER_TYPE_P (TREE_TYPE (parm))) - mark_reg_pointer (parmreg, - (TYPE_ALIGN (TREE_TYPE (TREE_TYPE (parm))) - / BITS_PER_UNIT)); - } - else - { - /* Value must be stored in the stack slot STACK_PARM - during function execution. */ - - if (promoted_mode != nominal_mode) - { - /* Conversion is required. */ - rtx tempreg = gen_reg_rtx (GET_MODE (entry_parm)); - - emit_move_insn (tempreg, validize_mem (entry_parm)); - - push_to_sequence (conversion_insns); - entry_parm = convert_to_mode (nominal_mode, tempreg, - TREE_UNSIGNED (TREE_TYPE (parm))); - if (stack_parm) - { - /* ??? This may need a big-endian conversion on sparc64. */ - stack_parm = change_address (stack_parm, nominal_mode, - NULL_RTX); - } - conversion_insns = get_insns (); - did_conversion = 1; - end_sequence (); - } - - if (entry_parm != stack_parm) - { - if (stack_parm == 0) - { - stack_parm - = assign_stack_local (GET_MODE (entry_parm), - GET_MODE_SIZE (GET_MODE (entry_parm)), 0); - /* If this is a memory ref that contains aggregate components, - mark it as such for cse and loop optimize. */ - MEM_SET_IN_STRUCT_P (stack_parm, aggregate); - } - - if (promoted_mode != nominal_mode) - { - push_to_sequence (conversion_insns); - emit_move_insn (validize_mem (stack_parm), - validize_mem (entry_parm)); - conversion_insns = get_insns (); - end_sequence (); - } - else - emit_move_insn (validize_mem (stack_parm), - validize_mem (entry_parm)); - } - if (current_function_check_memory_usage) - { - push_to_sequence (conversion_insns); - emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3, - XEXP (stack_parm, 0), ptr_mode, - GEN_INT (GET_MODE_SIZE (GET_MODE - (entry_parm))), - TYPE_MODE (sizetype), - GEN_INT (MEMORY_USE_RW), - TYPE_MODE (integer_type_node)); - - conversion_insns = get_insns (); - end_sequence (); - } - DECL_RTL (parm) = stack_parm; - } - - /* If this "parameter" was the place where we are receiving the - function's incoming structure pointer, set up the result. */ - if (parm == function_result_decl) - { - tree result = DECL_RESULT (fndecl); - tree restype = TREE_TYPE (result); - - DECL_RTL (result) - = gen_rtx_MEM (DECL_MODE (result), DECL_RTL (parm)); - - MEM_SET_IN_STRUCT_P (DECL_RTL (result), - AGGREGATE_TYPE_P (restype)); - } - - if (TREE_THIS_VOLATILE (parm)) - MEM_VOLATILE_P (DECL_RTL (parm)) = 1; - if (TREE_READONLY (parm)) - RTX_UNCHANGING_P (DECL_RTL (parm)) = 1; - } - - /* Output all parameter conversion instructions (possibly including calls) - now that all parameters have been copied out of hard registers. */ - emit_insns (conversion_insns); - - last_parm_insn = get_last_insn (); - - current_function_args_size = stack_args_size.constant; - - /* Adjust function incoming argument size for alignment and - minimum length. */ - -#ifdef REG_PARM_STACK_SPACE -#ifndef MAYBE_REG_PARM_STACK_SPACE - current_function_args_size = MAX (current_function_args_size, - REG_PARM_STACK_SPACE (fndecl)); -#endif -#endif - -#ifdef PREFERRED_STACK_BOUNDARY -#define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT) - - current_function_args_size - = ((current_function_args_size + STACK_BYTES - 1) - / STACK_BYTES) * STACK_BYTES; -#endif - -#ifdef ARGS_GROW_DOWNWARD - current_function_arg_offset_rtx - = (stack_args_size.var == 0 ? GEN_INT (-stack_args_size.constant) - : expand_expr (size_binop (MINUS_EXPR, stack_args_size.var, - size_int (-stack_args_size.constant)), - NULL_RTX, VOIDmode, EXPAND_MEMORY_USE_BAD)); -#else - current_function_arg_offset_rtx = ARGS_SIZE_RTX (stack_args_size); -#endif - - /* See how many bytes, if any, of its args a function should try to pop - on return. */ - - current_function_pops_args = RETURN_POPS_ARGS (fndecl, TREE_TYPE (fndecl), - current_function_args_size); - - /* For stdarg.h function, save info about - regs and stack space used by the named args. */ - - if (!hide_last_arg) - current_function_args_info = args_so_far; - - /* Set the rtx used for the function return value. Put this in its - own variable so any optimizers that need this information don't have - to include tree.h. Do this here so it gets done when an inlined - function gets output. */ - - current_function_return_rtx = DECL_RTL (DECL_RESULT (fndecl)); -} - -/* Indicate whether REGNO is an incoming argument to the current function - that was promoted to a wider mode. If so, return the RTX for the - register (to get its mode). PMODE and PUNSIGNEDP are set to the mode - that REGNO is promoted from and whether the promotion was signed or - unsigned. */ - -#ifdef PROMOTE_FUNCTION_ARGS - -rtx -promoted_input_arg (regno, pmode, punsignedp) - int regno; - enum machine_mode *pmode; - int *punsignedp; -{ - tree arg; - - for (arg = DECL_ARGUMENTS (current_function_decl); arg; - arg = TREE_CHAIN (arg)) - if (GET_CODE (DECL_INCOMING_RTL (arg)) == REG - && REGNO (DECL_INCOMING_RTL (arg)) == regno - && TYPE_MODE (DECL_ARG_TYPE (arg)) == TYPE_MODE (TREE_TYPE (arg))) - { - enum machine_mode mode = TYPE_MODE (TREE_TYPE (arg)); - int unsignedp = TREE_UNSIGNED (TREE_TYPE (arg)); - - mode = promote_mode (TREE_TYPE (arg), mode, &unsignedp, 1); - if (mode == GET_MODE (DECL_INCOMING_RTL (arg)) - && mode != DECL_MODE (arg)) - { - *pmode = DECL_MODE (arg); - *punsignedp = unsignedp; - return DECL_INCOMING_RTL (arg); - } - } - - return 0; -} - -#endif - -/* Compute the size and offset from the start of the stacked arguments for a - parm passed in mode PASSED_MODE and with type TYPE. - - INITIAL_OFFSET_PTR points to the current offset into the stacked - arguments. - - The starting offset and size for this parm are returned in *OFFSET_PTR - and *ARG_SIZE_PTR, respectively. - - IN_REGS is non-zero if the argument will be passed in registers. It will - never be set if REG_PARM_STACK_SPACE is not defined. - - FNDECL is the function in which the argument was defined. - - There are two types of rounding that are done. The first, controlled by - FUNCTION_ARG_BOUNDARY, forces the offset from the start of the argument - list to be aligned to the specific boundary (in bits). This rounding - affects the initial and starting offsets, but not the argument size. - - The second, controlled by FUNCTION_ARG_PADDING and PARM_BOUNDARY, - optionally rounds the size of the parm to PARM_BOUNDARY. The - initial offset is not affected by this rounding, while the size always - is and the starting offset may be. */ - -/* offset_ptr will be negative for ARGS_GROW_DOWNWARD case; - initial_offset_ptr is positive because locate_and_pad_parm's - callers pass in the total size of args so far as - initial_offset_ptr. arg_size_ptr is always positive.*/ - -void -locate_and_pad_parm (passed_mode, type, in_regs, fndecl, - initial_offset_ptr, offset_ptr, arg_size_ptr) - enum machine_mode passed_mode; - tree type; - int in_regs; - tree fndecl; - struct args_size *initial_offset_ptr; - struct args_size *offset_ptr; - struct args_size *arg_size_ptr; -{ - tree sizetree - = type ? size_in_bytes (type) : size_int (GET_MODE_SIZE (passed_mode)); - enum direction where_pad = FUNCTION_ARG_PADDING (passed_mode, type); - int boundary = FUNCTION_ARG_BOUNDARY (passed_mode, type); - -#ifdef REG_PARM_STACK_SPACE - /* If we have found a stack parm before we reach the end of the - area reserved for registers, skip that area. */ - if (! in_regs) - { - int reg_parm_stack_space = 0; - -#ifdef MAYBE_REG_PARM_STACK_SPACE - reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE; -#else - reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl); -#endif - if (reg_parm_stack_space > 0) - { - if (initial_offset_ptr->var) - { - initial_offset_ptr->var - = size_binop (MAX_EXPR, ARGS_SIZE_TREE (*initial_offset_ptr), - size_int (reg_parm_stack_space)); - initial_offset_ptr->constant = 0; - } - else if (initial_offset_ptr->constant < reg_parm_stack_space) - initial_offset_ptr->constant = reg_parm_stack_space; - } - } -#endif /* REG_PARM_STACK_SPACE */ - - arg_size_ptr->var = 0; - arg_size_ptr->constant = 0; - -#ifdef ARGS_GROW_DOWNWARD - if (initial_offset_ptr->var) - { - offset_ptr->constant = 0; - offset_ptr->var = size_binop (MINUS_EXPR, integer_zero_node, - initial_offset_ptr->var); - } - else - { - offset_ptr->constant = - initial_offset_ptr->constant; - offset_ptr->var = 0; - } - if (where_pad != none - && (TREE_CODE (sizetree) != INTEGER_CST - || ((TREE_INT_CST_LOW (sizetree) * BITS_PER_UNIT) % PARM_BOUNDARY))) - sizetree = round_up (sizetree, PARM_BOUNDARY / BITS_PER_UNIT); - SUB_PARM_SIZE (*offset_ptr, sizetree); - if (where_pad != downward) - pad_to_arg_alignment (offset_ptr, boundary); - if (initial_offset_ptr->var) - { - arg_size_ptr->var = size_binop (MINUS_EXPR, - size_binop (MINUS_EXPR, - integer_zero_node, - initial_offset_ptr->var), - offset_ptr->var); - } - else - { - arg_size_ptr->constant = (- initial_offset_ptr->constant - - offset_ptr->constant); - } -#else /* !ARGS_GROW_DOWNWARD */ - pad_to_arg_alignment (initial_offset_ptr, boundary); - *offset_ptr = *initial_offset_ptr; - -#ifdef PUSH_ROUNDING - if (passed_mode != BLKmode) - sizetree = size_int (PUSH_ROUNDING (TREE_INT_CST_LOW (sizetree))); -#endif - - /* Pad_below needs the pre-rounded size to know how much to pad below - so this must be done before rounding up. */ - if (where_pad == downward - /* However, BLKmode args passed in regs have their padding done elsewhere. - The stack slot must be able to hold the entire register. */ - && !(in_regs && passed_mode == BLKmode)) - pad_below (offset_ptr, passed_mode, sizetree); - - if (where_pad != none - && (TREE_CODE (sizetree) != INTEGER_CST - || ((TREE_INT_CST_LOW (sizetree) * BITS_PER_UNIT) % PARM_BOUNDARY))) - sizetree = round_up (sizetree, PARM_BOUNDARY / BITS_PER_UNIT); - - ADD_PARM_SIZE (*arg_size_ptr, sizetree); -#endif /* ARGS_GROW_DOWNWARD */ -} - -/* Round the stack offset in *OFFSET_PTR up to a multiple of BOUNDARY. - BOUNDARY is measured in bits, but must be a multiple of a storage unit. */ - -static void -pad_to_arg_alignment (offset_ptr, boundary) - struct args_size *offset_ptr; - int boundary; -{ - int boundary_in_bytes = boundary / BITS_PER_UNIT; - - if (boundary > BITS_PER_UNIT) - { - if (offset_ptr->var) - { - offset_ptr->var = -#ifdef ARGS_GROW_DOWNWARD - round_down -#else - round_up -#endif - (ARGS_SIZE_TREE (*offset_ptr), - boundary / BITS_PER_UNIT); - offset_ptr->constant = 0; /*?*/ - } - else - offset_ptr->constant = -#ifdef ARGS_GROW_DOWNWARD - FLOOR_ROUND (offset_ptr->constant, boundary_in_bytes); -#else - CEIL_ROUND (offset_ptr->constant, boundary_in_bytes); -#endif - } -} - -#ifndef ARGS_GROW_DOWNWARD -static void -pad_below (offset_ptr, passed_mode, sizetree) - struct args_size *offset_ptr; - enum machine_mode passed_mode; - tree sizetree; -{ - if (passed_mode != BLKmode) - { - if (GET_MODE_BITSIZE (passed_mode) % PARM_BOUNDARY) - offset_ptr->constant - += (((GET_MODE_BITSIZE (passed_mode) + PARM_BOUNDARY - 1) - / PARM_BOUNDARY * PARM_BOUNDARY / BITS_PER_UNIT) - - GET_MODE_SIZE (passed_mode)); - } - else - { - if (TREE_CODE (sizetree) != INTEGER_CST - || (TREE_INT_CST_LOW (sizetree) * BITS_PER_UNIT) % PARM_BOUNDARY) - { - /* Round the size up to multiple of PARM_BOUNDARY bits. */ - tree s2 = round_up (sizetree, PARM_BOUNDARY / BITS_PER_UNIT); - /* Add it in. */ - ADD_PARM_SIZE (*offset_ptr, s2); - SUB_PARM_SIZE (*offset_ptr, sizetree); - } - } -} -#endif - -#ifdef ARGS_GROW_DOWNWARD -static tree -round_down (value, divisor) - tree value; - int divisor; -{ - return size_binop (MULT_EXPR, - size_binop (FLOOR_DIV_EXPR, value, size_int (divisor)), - size_int (divisor)); -} -#endif - -/* Walk the tree of blocks describing the binding levels within a function - and warn about uninitialized variables. - This is done after calling flow_analysis and before global_alloc - clobbers the pseudo-regs to hard regs. */ - -void -uninitialized_vars_warning (block) - tree block; -{ - register tree decl, sub; - for (decl = BLOCK_VARS (block); decl; decl = TREE_CHAIN (decl)) - { - if (TREE_CODE (decl) == VAR_DECL - /* These warnings are unreliable for and aggregates - because assigning the fields one by one can fail to convince - flow.c that the entire aggregate was initialized. - Unions are troublesome because members may be shorter. */ - && ! AGGREGATE_TYPE_P (TREE_TYPE (decl)) - && DECL_RTL (decl) != 0 - && GET_CODE (DECL_RTL (decl)) == REG - /* Global optimizations can make it difficult to determine if a - particular variable has been initialized. However, a VAR_DECL - with a nonzero DECL_INITIAL had an initializer, so do not - claim it is potentially uninitialized. - - We do not care about the actual value in DECL_INITIAL, so we do - not worry that it may be a dangling pointer. */ - && DECL_INITIAL (decl) == NULL_TREE - && regno_uninitialized (REGNO (DECL_RTL (decl)))) - warning_with_decl (decl, - "`%s' might be used uninitialized in this function"); - if (TREE_CODE (decl) == VAR_DECL - && DECL_RTL (decl) != 0 - && GET_CODE (DECL_RTL (decl)) == REG - && regno_clobbered_at_setjmp (REGNO (DECL_RTL (decl)))) - warning_with_decl (decl, - "variable `%s' might be clobbered by `longjmp' or `vfork'"); - } - for (sub = BLOCK_SUBBLOCKS (block); sub; sub = TREE_CHAIN (sub)) - uninitialized_vars_warning (sub); -} - -/* Do the appropriate part of uninitialized_vars_warning - but for arguments instead of local variables. */ - -void -setjmp_args_warning () -{ - register tree decl; - for (decl = DECL_ARGUMENTS (current_function_decl); - decl; decl = TREE_CHAIN (decl)) - if (DECL_RTL (decl) != 0 - && GET_CODE (DECL_RTL (decl)) == REG - && regno_clobbered_at_setjmp (REGNO (DECL_RTL (decl)))) - warning_with_decl (decl, "argument `%s' might be clobbered by `longjmp' or `vfork'"); -} - -/* If this function call setjmp, put all vars into the stack - unless they were declared `register'. */ - -void -setjmp_protect (block) - tree block; -{ - register tree decl, sub; - for (decl = BLOCK_VARS (block); decl; decl = TREE_CHAIN (decl)) - if ((TREE_CODE (decl) == VAR_DECL - || TREE_CODE (decl) == PARM_DECL) - && DECL_RTL (decl) != 0 - && (GET_CODE (DECL_RTL (decl)) == REG - || (GET_CODE (DECL_RTL (decl)) == MEM - && GET_CODE (XEXP (DECL_RTL (decl), 0)) == ADDRESSOF)) - /* If this variable came from an inline function, it must be - that its life doesn't overlap the setjmp. If there was a - setjmp in the function, it would already be in memory. We - must exclude such variable because their DECL_RTL might be - set to strange things such as virtual_stack_vars_rtx. */ - && ! DECL_FROM_INLINE (decl) - && ( -#ifdef NON_SAVING_SETJMP - /* If longjmp doesn't restore the registers, - don't put anything in them. */ - NON_SAVING_SETJMP - || -#endif - ! DECL_REGISTER (decl))) - put_var_into_stack (decl); - for (sub = BLOCK_SUBBLOCKS (block); sub; sub = TREE_CHAIN (sub)) - setjmp_protect (sub); -} - -/* Like the previous function, but for args instead of local variables. */ - -void -setjmp_protect_args () -{ - register tree decl; - for (decl = DECL_ARGUMENTS (current_function_decl); - decl; decl = TREE_CHAIN (decl)) - if ((TREE_CODE (decl) == VAR_DECL - || TREE_CODE (decl) == PARM_DECL) - && DECL_RTL (decl) != 0 - && (GET_CODE (DECL_RTL (decl)) == REG - || (GET_CODE (DECL_RTL (decl)) == MEM - && GET_CODE (XEXP (DECL_RTL (decl), 0)) == ADDRESSOF)) - && ( - /* If longjmp doesn't restore the registers, - don't put anything in them. */ -#ifdef NON_SAVING_SETJMP - NON_SAVING_SETJMP - || -#endif - ! DECL_REGISTER (decl))) - put_var_into_stack (decl); -} - -/* Return the context-pointer register corresponding to DECL, - or 0 if it does not need one. */ - -rtx -lookup_static_chain (decl) - tree decl; -{ - tree context = decl_function_context (decl); - tree link; - - if (context == 0 - || (TREE_CODE (decl) == FUNCTION_DECL && DECL_NO_STATIC_CHAIN (decl))) - return 0; - - /* We treat inline_function_decl as an alias for the current function - because that is the inline function whose vars, types, etc. - are being merged into the current function. - See expand_inline_function. */ - if (context == current_function_decl || context == inline_function_decl) - return virtual_stack_vars_rtx; - - for (link = context_display; link; link = TREE_CHAIN (link)) - if (TREE_PURPOSE (link) == context) - return RTL_EXPR_RTL (TREE_VALUE (link)); - - abort (); -} - -/* Convert a stack slot address ADDR for variable VAR - (from a containing function) - into an address valid in this function (using a static chain). */ - -rtx -fix_lexical_addr (addr, var) - rtx addr; - tree var; -{ - rtx basereg; - HOST_WIDE_INT displacement; - tree context = decl_function_context (var); - struct function *fp; - rtx base = 0; - - /* If this is the present function, we need not do anything. */ - if (context == current_function_decl || context == inline_function_decl) - return addr; - - for (fp = outer_function_chain; fp; fp = fp->next) - if (fp->decl == context) - break; - - if (fp == 0) - abort (); - - if (GET_CODE (addr) == ADDRESSOF && GET_CODE (XEXP (addr, 0)) == MEM) - addr = XEXP (XEXP (addr, 0), 0); - - /* Decode given address as base reg plus displacement. */ - if (GET_CODE (addr) == REG) - basereg = addr, displacement = 0; - else if (GET_CODE (addr) == PLUS && GET_CODE (XEXP (addr, 1)) == CONST_INT) - basereg = XEXP (addr, 0), displacement = INTVAL (XEXP (addr, 1)); - else - abort (); - - /* We accept vars reached via the containing function's - incoming arg pointer and via its stack variables pointer. */ - if (basereg == fp->internal_arg_pointer) - { - /* If reached via arg pointer, get the arg pointer value - out of that function's stack frame. - - There are two cases: If a separate ap is needed, allocate a - slot in the outer function for it and dereference it that way. - This is correct even if the real ap is actually a pseudo. - Otherwise, just adjust the offset from the frame pointer to - compensate. */ - -#ifdef NEED_SEPARATE_AP - rtx addr; - - if (fp->arg_pointer_save_area == 0) - fp->arg_pointer_save_area - = assign_outer_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0, fp); - - addr = fix_lexical_addr (XEXP (fp->arg_pointer_save_area, 0), var); - addr = memory_address (Pmode, addr); - - base = copy_to_reg (gen_rtx_MEM (Pmode, addr)); -#else - displacement += (FIRST_PARM_OFFSET (context) - STARTING_FRAME_OFFSET); - base = lookup_static_chain (var); -#endif - } - - else if (basereg == virtual_stack_vars_rtx) - { - /* This is the same code as lookup_static_chain, duplicated here to - avoid an extra call to decl_function_context. */ - tree link; - - for (link = context_display; link; link = TREE_CHAIN (link)) - if (TREE_PURPOSE (link) == context) - { - base = RTL_EXPR_RTL (TREE_VALUE (link)); - break; - } - } - - if (base == 0) - abort (); - - /* Use same offset, relative to appropriate static chain or argument - pointer. */ - return plus_constant (base, displacement); -} - -/* Return the address of the trampoline for entering nested fn FUNCTION. - If necessary, allocate a trampoline (in the stack frame) - and emit rtl to initialize its contents (at entry to this function). */ - -rtx -trampoline_address (function) - tree function; -{ - tree link; - tree rtlexp; - rtx tramp; - struct function *fp; - tree fn_context; - - /* Find an existing trampoline and return it. */ - for (link = trampoline_list; link; link = TREE_CHAIN (link)) - if (TREE_PURPOSE (link) == function) - return - round_trampoline_addr (XEXP (RTL_EXPR_RTL (TREE_VALUE (link)), 0)); - - for (fp = outer_function_chain; fp; fp = fp->next) - for (link = fp->trampoline_list; link; link = TREE_CHAIN (link)) - if (TREE_PURPOSE (link) == function) - { - tramp = fix_lexical_addr (XEXP (RTL_EXPR_RTL (TREE_VALUE (link)), 0), - function); - return round_trampoline_addr (tramp); - } - - /* None exists; we must make one. */ - - /* Find the `struct function' for the function containing FUNCTION. */ - fp = 0; - fn_context = decl_function_context (function); - if (fn_context != current_function_decl - && fn_context != inline_function_decl) - for (fp = outer_function_chain; fp; fp = fp->next) - if (fp->decl == fn_context) - break; - - /* Allocate run-time space for this trampoline - (usually in the defining function's stack frame). */ -#ifdef ALLOCATE_TRAMPOLINE - tramp = ALLOCATE_TRAMPOLINE (fp); -#else - /* If rounding needed, allocate extra space - to ensure we have TRAMPOLINE_SIZE bytes left after rounding up. */ -#ifdef TRAMPOLINE_ALIGNMENT -#define TRAMPOLINE_REAL_SIZE \ - (TRAMPOLINE_SIZE + (TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT) - 1) -#else -#define TRAMPOLINE_REAL_SIZE (TRAMPOLINE_SIZE) -#endif - if (fp != 0) - tramp = assign_outer_stack_local (BLKmode, TRAMPOLINE_REAL_SIZE, 0, fp); - else - tramp = assign_stack_local (BLKmode, TRAMPOLINE_REAL_SIZE, 0); -#endif - - /* Record the trampoline for reuse and note it for later initialization - by expand_function_end. */ - if (fp != 0) - { - push_obstacks (fp->function_maybepermanent_obstack, - fp->function_maybepermanent_obstack); - rtlexp = make_node (RTL_EXPR); - RTL_EXPR_RTL (rtlexp) = tramp; - fp->trampoline_list = tree_cons (function, rtlexp, fp->trampoline_list); - pop_obstacks (); - } - else - { - /* Make the RTL_EXPR node temporary, not momentary, so that the - trampoline_list doesn't become garbage. */ - int momentary = suspend_momentary (); - rtlexp = make_node (RTL_EXPR); - resume_momentary (momentary); - - RTL_EXPR_RTL (rtlexp) = tramp; - trampoline_list = tree_cons (function, rtlexp, trampoline_list); - } - - tramp = fix_lexical_addr (XEXP (tramp, 0), function); - return round_trampoline_addr (tramp); -} - -/* Given a trampoline address, - round it to multiple of TRAMPOLINE_ALIGNMENT. */ - -static rtx -round_trampoline_addr (tramp) - rtx tramp; -{ -#ifdef TRAMPOLINE_ALIGNMENT - /* Round address up to desired boundary. */ - rtx temp = gen_reg_rtx (Pmode); - temp = expand_binop (Pmode, add_optab, tramp, - GEN_INT (TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT - 1), - temp, 0, OPTAB_LIB_WIDEN); - tramp = expand_binop (Pmode, and_optab, temp, - GEN_INT (- TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT), - temp, 0, OPTAB_LIB_WIDEN); -#endif - return tramp; -} - -/* The functions identify_blocks and reorder_blocks provide a way to - reorder the tree of BLOCK nodes, for optimizers that reshuffle or - duplicate portions of the RTL code. Call identify_blocks before - changing the RTL, and call reorder_blocks after. */ - -/* Put all this function's BLOCK nodes including those that are chained - onto the first block into a vector, and return it. - Also store in each NOTE for the beginning or end of a block - the index of that block in the vector. - The arguments are BLOCK, the chain of top-level blocks of the function, - and INSNS, the insn chain of the function. */ - -tree * -identify_blocks (block, insns) - tree block; - rtx insns; -{ - int n_blocks; - tree *block_vector; - int *block_stack; - int depth = 0; - int next_block_number = 1; - int current_block_number = 1; - rtx insn; - - if (block == 0) - return 0; - - n_blocks = all_blocks (block, 0); - block_vector = (tree *) xmalloc (n_blocks * sizeof (tree)); - block_stack = (int *) alloca (n_blocks * sizeof (int)); - - all_blocks (block, block_vector); - - for (insn = insns; insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == NOTE) - { - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG) - { - block_stack[depth++] = current_block_number; - current_block_number = next_block_number; - NOTE_BLOCK_NUMBER (insn) = next_block_number++; - } - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END) - { - NOTE_BLOCK_NUMBER (insn) = current_block_number; - current_block_number = block_stack[--depth]; - } - } - - if (n_blocks != next_block_number) - abort (); - - return block_vector; -} - -/* Given BLOCK_VECTOR which was returned by identify_blocks, - and a revised instruction chain, rebuild the tree structure - of BLOCK nodes to correspond to the new order of RTL. - The new block tree is inserted below TOP_BLOCK. - Returns the current top-level block. */ - -tree -reorder_blocks (block_vector, block, insns) - tree *block_vector; - tree block; - rtx insns; -{ - tree current_block = block; - rtx insn; - - if (block_vector == 0) - return block; - - /* Prune the old trees away, so that it doesn't get in the way. */ - BLOCK_SUBBLOCKS (current_block) = 0; - BLOCK_CHAIN (current_block) = 0; - - /* CYGNUS LOCAL LRS */ - for (insn = insns; insn; insn = NEXT_INSN (insn)) - { - tree block, range_start_block = NULL_TREE; - - if (GET_CODE (insn) == NOTE) - switch (NOTE_LINE_NUMBER (insn)) - { - /* Block beginning, link into block chain */ - case NOTE_INSN_BLOCK_BEG: - if (NOTE_BLOCK_NUMBER (insn) == NOTE_BLOCK_LIVE_RANGE_BLOCK) - { - range_start_block = block = make_node (BLOCK); - BLOCK_LIVE_RANGE_FLAG (block) = TRUE; - TREE_USED (block) = TRUE; - } - else if (NOTE_BLOCK_NUMBER (insn) <= 0) - abort (); - else - { - block = block_vector[NOTE_BLOCK_NUMBER (insn)]; - range_start_block = NULL_TREE; - - /* If we have seen this block before, copy it. */ - if (TREE_ASM_WRITTEN (block)) - block = copy_node (block); - } - - BLOCK_SUBBLOCKS (block) = 0; - TREE_ASM_WRITTEN (block) = 1; - BLOCK_SUPERCONTEXT (block) = current_block; - BLOCK_CHAIN (block) = BLOCK_SUBBLOCKS (current_block); - BLOCK_SUBBLOCKS (current_block) = block; - NOTE_SOURCE_FILE (insn) = 0; - current_block = block; - break; - - /* Block ending, restore current block, reset block number. */ - case NOTE_INSN_BLOCK_END: - BLOCK_SUBBLOCKS (current_block) - = blocks_nreverse (BLOCK_SUBBLOCKS (current_block)); - current_block = BLOCK_SUPERCONTEXT (current_block); - NOTE_BLOCK_NUMBER (insn) = 0; - break; - - /* Range start, if we created a new block for the range, link - any new copies into the range. */ - case NOTE_INSN_RANGE_START: - if (range_start_block) - { - rtx ri = NOTE_RANGE_INFO (insn); - int i; - for (i = 0; i < (int)RANGE_INFO_NUM_REGS (ri); i++) - if (RANGE_REG_SYMBOL_NODE (ri, i)) - { - tree new_sym = copy_node (RANGE_REG_SYMBOL_NODE (ri, i)); - DECL_RTL (new_sym) = regno_reg_rtx[RANGE_REG_COPY (ri, i)]; - TREE_CHAIN (new_sym) = BLOCK_VARS (range_start_block); - BLOCK_VARS (range_start_block) = new_sym; - RANGE_REG_SYMBOL_NODE (ri, i) = new_sym; - RANGE_REG_BLOCK_NODE (ri, i) = range_start_block; - } - } - break; - } - } - /* END CYGNUS LOCAL */ - - BLOCK_SUBBLOCKS (current_block) - = blocks_nreverse (BLOCK_SUBBLOCKS (current_block)); - return current_block; -} - -/* Reverse the order of elements in the chain T of blocks, - and return the new head of the chain (old last element). */ - -static tree -blocks_nreverse (t) - tree t; -{ - register tree prev = 0, decl, next; - for (decl = t; decl; decl = next) - { - next = BLOCK_CHAIN (decl); - BLOCK_CHAIN (decl) = prev; - prev = decl; - } - return prev; -} - -/* Count the subblocks of the list starting with BLOCK, and list them - all into the vector VECTOR. Also clear TREE_ASM_WRITTEN in all - blocks. */ - -static int -all_blocks (block, vector) - tree block; - tree *vector; -{ - int n_blocks = 0; - - while (block) - { - TREE_ASM_WRITTEN (block) = 0; - - /* Record this block. */ - if (vector) - vector[n_blocks] = block; - - ++n_blocks; - - /* Record the subblocks, and their subblocks... */ - n_blocks += all_blocks (BLOCK_SUBBLOCKS (block), - vector ? vector + n_blocks : 0); - block = BLOCK_CHAIN (block); - } - - return n_blocks; -} - -/* Generate RTL for the start of the function SUBR (a FUNCTION_DECL tree node) - and initialize static variables for generating RTL for the statements - of the function. */ - -void -init_function_start (subr, filename, line) - tree subr; - char *filename; - int line; -{ - init_stmt_for_function (); - - cse_not_expected = ! optimize; - - /* Caller save not needed yet. */ - caller_save_needed = 0; - - /* No stack slots have been made yet. */ - stack_slot_list = 0; - - /* There is no stack slot for handling nonlocal gotos. */ - nonlocal_goto_handler_slots = 0; - nonlocal_goto_stack_level = 0; - - /* No labels have been declared for nonlocal use. */ - nonlocal_labels = 0; - - /* No function calls so far in this function. */ - function_call_count = 0; - - /* No parm regs have been allocated. - (This is important for output_inline_function.) */ - max_parm_reg = LAST_VIRTUAL_REGISTER + 1; - - /* Initialize the RTL mechanism. */ - init_emit (); - - /* Initialize the queue of pending postincrement and postdecrements, - and some other info in expr.c. */ - init_expr (); - - /* We haven't done register allocation yet. */ - reg_renumber = 0; - - init_const_rtx_hash_table (); - - current_function_name = (*decl_printable_name) (subr, 2); - - /* Nonzero if this is a nested function that uses a static chain. */ - - current_function_needs_context - = (decl_function_context (current_function_decl) != 0 - && ! DECL_NO_STATIC_CHAIN (current_function_decl)); - - /* Set if a call to setjmp is seen. */ - current_function_calls_setjmp = 0; - - /* Set if a call to longjmp is seen. */ - current_function_calls_longjmp = 0; - - current_function_calls_alloca = 0; - current_function_has_nonlocal_label = 0; - current_function_has_nonlocal_goto = 0; - current_function_contains_functions = 0; - current_function_sp_is_unchanging = 0; - current_function_is_thunk = 0; - - current_function_returns_pcc_struct = 0; - current_function_returns_struct = 0; - current_function_epilogue_delay_list = 0; - current_function_uses_const_pool = 0; - current_function_uses_pic_offset_table = 0; - current_function_cannot_inline = 0; - /* CYGNUS LOCAL -- Branch Prediction */ - current_function_uses_expect = 0; - current_function_processing_expect = 0; - /* END CYGNUS LOCAL -- Branch Prediction */ - - /* We have not yet needed to make a label to jump to for tail-recursion. */ - tail_recursion_label = 0; - - /* We haven't had a need to make a save area for ap yet. */ - - arg_pointer_save_area = 0; - - /* No stack slots allocated yet. */ - frame_offset = 0; - - /* No SAVE_EXPRs in this function yet. */ - save_expr_regs = 0; - - /* No RTL_EXPRs in this function yet. */ - rtl_expr_chain = 0; - - /* Set up to allocate temporaries. */ - init_temp_slots (); - - /* Within function body, compute a type's size as soon it is laid out. */ - immediate_size_expand++; - - /* We haven't made any trampolines for this function yet. */ - trampoline_list = 0; - - init_pending_stack_adjust (); - inhibit_defer_pop = 0; - - current_function_outgoing_args_size = 0; - - /* Prevent ever trying to delete the first instruction of a function. - Also tell final how to output a linenum before the function prologue. - Note linenums could be missing, e.g. when compiling a Java .class file. */ - if (line > 0) - emit_line_note (filename, line); - - /* Make sure first insn is a note even if we don't want linenums. - This makes sure the first insn will never be deleted. - Also, final expects a note to appear there. */ - emit_note (NULL_PTR, NOTE_INSN_DELETED); - - /* Set flags used by final.c. */ - if (aggregate_value_p (DECL_RESULT (subr))) - { -#ifdef PCC_STATIC_STRUCT_RETURN - current_function_returns_pcc_struct = 1; -#endif - current_function_returns_struct = 1; - } - - /* Warn if this value is an aggregate type, - regardless of which calling convention we are using for it. */ - if (warn_aggregate_return - && AGGREGATE_TYPE_P (TREE_TYPE (DECL_RESULT (subr)))) - warning ("function returns an aggregate"); - - current_function_returns_pointer - = POINTER_TYPE_P (TREE_TYPE (DECL_RESULT (subr))); - - /* Indicate that we need to distinguish between the return value of the - present function and the return value of a function being called. */ - rtx_equal_function_value_matters = 1; - - /* Indicate that we have not instantiated virtual registers yet. */ - virtuals_instantiated = 0; - - /* Indicate we have no need of a frame pointer yet. */ - frame_pointer_needed = 0; - - /* By default assume not varargs or stdarg. */ - current_function_varargs = 0; - current_function_stdarg = 0; -} - -/* Indicate that the current function uses extra args - not explicitly mentioned in the argument list in any fashion. */ - -void -mark_varargs () -{ - current_function_varargs = 1; -} - -/* Expand a call to __main at the beginning of a possible main function. */ - -#if defined(INIT_SECTION_ASM_OP) && !defined(INVOKE__main) -#undef HAS_INIT_SECTION -#define HAS_INIT_SECTION -#endif - -void -expand_main_function () -{ -#if !defined (HAS_INIT_SECTION) - emit_library_call (gen_rtx_SYMBOL_REF (Pmode, NAME__MAIN), 0, - VOIDmode, 0); -#endif /* not HAS_INIT_SECTION */ -} - -extern struct obstack permanent_obstack; - -/* Start the RTL for a new function, and set variables used for - emitting RTL. - SUBR is the FUNCTION_DECL node. - PARMS_HAVE_CLEANUPS is nonzero if there are cleanups associated with - the function's parameters, which must be run at any return statement. */ - -void -expand_function_start (subr, parms_have_cleanups) - tree subr; - int parms_have_cleanups; -{ - register int i; - tree tem; - rtx last_ptr = NULL_RTX; - - /* Make sure volatile mem refs aren't considered - valid operands of arithmetic insns. */ - init_recog_no_volatile (); - - /* Set this before generating any memory accesses. */ - current_function_check_memory_usage - = (flag_check_memory_usage - && ! DECL_NO_CHECK_MEMORY_USAGE (current_function_decl)); - - current_function_instrument_entry_exit - = (flag_instrument_function_entry_exit - && ! DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (subr)); - - /* If function gets a static chain arg, store it in the stack frame. - Do this first, so it gets the first stack slot offset. */ - if (current_function_needs_context) - { - last_ptr = assign_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0); - - /* Delay copying static chain if it is not a register to avoid - conflicts with regs used for parameters. */ - if (! SMALL_REGISTER_CLASSES - || GET_CODE (static_chain_incoming_rtx) == REG) - emit_move_insn (last_ptr, static_chain_incoming_rtx); - } - - /* If the parameters of this function need cleaning up, get a label - for the beginning of the code which executes those cleanups. This must - be done before doing anything with return_label. */ - if (parms_have_cleanups) - cleanup_label = gen_label_rtx (); - else - cleanup_label = 0; - - /* Make the label for return statements to jump to, if this machine - does not have a one-instruction return and uses an epilogue, - or if it returns a structure, or if it has parm cleanups. */ -#ifdef HAVE_return - if (cleanup_label == 0 && HAVE_return - && ! current_function_instrument_entry_exit - && ! current_function_returns_pcc_struct - && ! (current_function_returns_struct && ! optimize)) - return_label = 0; - else - return_label = gen_label_rtx (); -#else - return_label = gen_label_rtx (); -#endif - - /* Initialize rtx used to return the value. */ - /* Do this before assign_parms so that we copy the struct value address - before any library calls that assign parms might generate. */ - - /* Decide whether to return the value in memory or in a register. */ - if (aggregate_value_p (DECL_RESULT (subr))) - { - /* Returning something that won't go in a register. */ - register rtx value_address = 0; - -#ifdef PCC_STATIC_STRUCT_RETURN - if (current_function_returns_pcc_struct) - { - int size = int_size_in_bytes (TREE_TYPE (DECL_RESULT (subr))); - value_address = assemble_static_space (size); - } - else -#endif - { - /* Expect to be passed the address of a place to store the value. - If it is passed as an argument, assign_parms will take care of - it. */ - if (struct_value_incoming_rtx) - { - value_address = gen_reg_rtx (Pmode); - emit_move_insn (value_address, struct_value_incoming_rtx); - } - } - if (value_address) - { - DECL_RTL (DECL_RESULT (subr)) - = gen_rtx_MEM (DECL_MODE (DECL_RESULT (subr)), value_address); - MEM_SET_IN_STRUCT_P (DECL_RTL (DECL_RESULT (subr)), - AGGREGATE_TYPE_P (TREE_TYPE - (DECL_RESULT - (subr)))); - } - } - else if (DECL_MODE (DECL_RESULT (subr)) == VOIDmode) - /* If return mode is void, this decl rtl should not be used. */ - DECL_RTL (DECL_RESULT (subr)) = 0; - else if (parms_have_cleanups || current_function_instrument_entry_exit) - { - /* If function will end with cleanup code for parms, - compute the return values into a pseudo reg, - which we will copy into the true return register - after the cleanups are done. */ - - enum machine_mode mode = DECL_MODE (DECL_RESULT (subr)); - -#ifdef PROMOTE_FUNCTION_RETURN - tree type = TREE_TYPE (DECL_RESULT (subr)); - int unsignedp = TREE_UNSIGNED (type); - - mode = promote_mode (type, mode, &unsignedp, 1); -#endif - - DECL_RTL (DECL_RESULT (subr)) = gen_reg_rtx (mode); - } - else - /* Scalar, returned in a register. */ - { -#ifdef FUNCTION_OUTGOING_VALUE - DECL_RTL (DECL_RESULT (subr)) - = FUNCTION_OUTGOING_VALUE (TREE_TYPE (DECL_RESULT (subr)), subr); -#else - DECL_RTL (DECL_RESULT (subr)) - = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (subr)), subr); -#endif - - /* Mark this reg as the function's return value. */ - if (GET_CODE (DECL_RTL (DECL_RESULT (subr))) == REG) - { - REG_FUNCTION_VALUE_P (DECL_RTL (DECL_RESULT (subr))) = 1; - /* Needed because we may need to move this to memory - in case it's a named return value whose address is taken. */ - DECL_REGISTER (DECL_RESULT (subr)) = 1; - } - } - - /* Initialize rtx for parameters and local variables. - In some cases this requires emitting insns. */ - - assign_parms (subr, 0); - - /* Copy the static chain now if it wasn't a register. The delay is to - avoid conflicts with the parameter passing registers. */ - - if (SMALL_REGISTER_CLASSES && current_function_needs_context) - if (GET_CODE (static_chain_incoming_rtx) != REG) - emit_move_insn (last_ptr, static_chain_incoming_rtx); - - /* The following was moved from init_function_start. - The move is supposed to make sdb output more accurate. */ - /* Indicate the beginning of the function body, - as opposed to parm setup. */ - emit_note (NULL_PTR, NOTE_INSN_FUNCTION_BEG); - - /* If doing stupid allocation, mark parms as born here. */ - - if (GET_CODE (get_last_insn ()) != NOTE) - emit_note (NULL_PTR, NOTE_INSN_DELETED); - parm_birth_insn = get_last_insn (); - - if (obey_regdecls) - { - for (i = LAST_VIRTUAL_REGISTER + 1; i < max_parm_reg; i++) - use_variable (regno_reg_rtx[i]); - - if (current_function_internal_arg_pointer != virtual_incoming_args_rtx) - use_variable (current_function_internal_arg_pointer); - } - - context_display = 0; - if (current_function_needs_context) - { - /* Fetch static chain values for containing functions. */ - tem = decl_function_context (current_function_decl); - /* If not doing stupid register allocation copy the static chain - pointer into a pseudo. If we have small register classes, copy - the value from memory if static_chain_incoming_rtx is a REG. If - we do stupid register allocation, we use the stack address - generated above. */ - if (tem && ! obey_regdecls) - { - /* If the static chain originally came in a register, put it back - there, then move it out in the next insn. The reason for - this peculiar code is to satisfy function integration. */ - if (SMALL_REGISTER_CLASSES - && GET_CODE (static_chain_incoming_rtx) == REG) - emit_move_insn (static_chain_incoming_rtx, last_ptr); - last_ptr = copy_to_reg (static_chain_incoming_rtx); - } - - while (tem) - { - tree rtlexp = make_node (RTL_EXPR); - - RTL_EXPR_RTL (rtlexp) = last_ptr; - context_display = tree_cons (tem, rtlexp, context_display); - tem = decl_function_context (tem); - if (tem == 0) - break; - /* Chain thru stack frames, assuming pointer to next lexical frame - is found at the place we always store it. */ -#ifdef FRAME_GROWS_DOWNWARD - last_ptr = plus_constant (last_ptr, - GET_MODE_SIZE (Pmode)); -#endif - last_ptr = copy_to_reg (gen_rtx_MEM (Pmode, - memory_address (Pmode, last_ptr))); - - /* If we are not optimizing, ensure that we know that this - piece of context is live over the entire function. */ - if (! optimize) - save_expr_regs = gen_rtx_EXPR_LIST (VOIDmode, last_ptr, - save_expr_regs); - } - } - - if (current_function_instrument_entry_exit) - { - rtx fun = DECL_RTL (current_function_decl); - if (GET_CODE (fun) == MEM) - fun = XEXP (fun, 0); - else - abort (); - emit_library_call (profile_function_entry_libfunc, 0, VOIDmode, 2, - fun, Pmode, - expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS, - 0, - hard_frame_pointer_rtx), - Pmode); - } - - /* After the display initializations is where the tail-recursion label - should go, if we end up needing one. Ensure we have a NOTE here - since some things (like trampolines) get placed before this. */ - tail_recursion_reentry = emit_note (NULL_PTR, NOTE_INSN_DELETED); - - /* Evaluate now the sizes of any types declared among the arguments. */ - for (tem = nreverse (get_pending_sizes ()); tem; tem = TREE_CHAIN (tem)) - { - expand_expr (TREE_VALUE (tem), const0_rtx, VOIDmode, - EXPAND_MEMORY_USE_BAD); - /* Flush the queue in case this parameter declaration has - side-effects. */ - emit_queue (); - } - - /* Make sure there is a line number after the function entry setup code. */ - force_next_line_note (); -} - -/* Generate RTL for the end of the current function. - FILENAME and LINE are the current position in the source file. - - It is up to language-specific callers to do cleanups for parameters-- - or else, supply 1 for END_BINDINGS and we will call expand_end_bindings. */ - -void -expand_function_end (filename, line, end_bindings) - char *filename; - int line; - int end_bindings; -{ - register int i; - tree link; - -#ifdef TRAMPOLINE_TEMPLATE - static rtx initial_trampoline; -#endif - -#ifdef NON_SAVING_SETJMP - /* Don't put any variables in registers if we call setjmp - on a machine that fails to restore the registers. */ - if (NON_SAVING_SETJMP && current_function_calls_setjmp) - { - if (DECL_INITIAL (current_function_decl) != error_mark_node) - setjmp_protect (DECL_INITIAL (current_function_decl)); - - setjmp_protect_args (); - } -#endif - - /* Save the argument pointer if a save area was made for it. */ - if (arg_pointer_save_area) - { - /* arg_pointer_save_area may not be a valid memory address, so we - have to check it and fix it if necessary. */ - rtx seq; - start_sequence (); - emit_move_insn (validize_mem (arg_pointer_save_area), - virtual_incoming_args_rtx); - seq = gen_sequence (); - end_sequence (); - emit_insn_before (seq, tail_recursion_reentry); - } - - /* Initialize any trampolines required by this function. */ - for (link = trampoline_list; link; link = TREE_CHAIN (link)) - { - tree function = TREE_PURPOSE (link); - rtx context = lookup_static_chain (function); - rtx tramp = RTL_EXPR_RTL (TREE_VALUE (link)); -#ifdef TRAMPOLINE_TEMPLATE - rtx blktramp; -#endif - rtx seq; - -#ifdef TRAMPOLINE_TEMPLATE - /* First make sure this compilation has a template for - initializing trampolines. */ - if (initial_trampoline == 0) - { - end_temporary_allocation (); - initial_trampoline - = gen_rtx_MEM (BLKmode, assemble_trampoline_template ()); - resume_temporary_allocation (); - } -#endif - - /* Generate insns to initialize the trampoline. */ - start_sequence (); - tramp = round_trampoline_addr (XEXP (tramp, 0)); -#ifdef TRAMPOLINE_TEMPLATE - blktramp = change_address (initial_trampoline, BLKmode, tramp); - emit_block_move (blktramp, initial_trampoline, - GEN_INT (TRAMPOLINE_SIZE), - TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT); -#endif - INITIALIZE_TRAMPOLINE (tramp, XEXP (DECL_RTL (function), 0), context); - seq = get_insns (); - end_sequence (); - - /* Put those insns at entry to the containing function (this one). */ - emit_insns_before (seq, tail_recursion_reentry); - } - - /* If we are doing stack checking and this function makes calls, - do a stack probe at the start of the function to ensure we have enough - space for another stack frame. */ - if (flag_stack_check && ! STACK_CHECK_BUILTIN) - { - rtx insn, seq; - - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == CALL_INSN) - { - start_sequence (); - probe_stack_range (STACK_CHECK_PROTECT, - GEN_INT (STACK_CHECK_MAX_FRAME_SIZE)); - seq = get_insns (); - end_sequence (); - emit_insns_before (seq, tail_recursion_reentry); - break; - } - } - - /* Warn about unused parms if extra warnings were specified. */ - if (warn_unused && extra_warnings) - { - tree decl; - - for (decl = DECL_ARGUMENTS (current_function_decl); - decl; decl = TREE_CHAIN (decl)) - if (! TREE_USED (decl) && TREE_CODE (decl) == PARM_DECL - && DECL_NAME (decl) && ! DECL_ARTIFICIAL (decl)) - warning_with_decl (decl, "unused parameter `%s'"); - } - - /* Delete handlers for nonlocal gotos if nothing uses them. */ - if (nonlocal_goto_handler_slots != 0 - && ! current_function_has_nonlocal_label) - delete_handlers (); - - /* End any sequences that failed to be closed due to syntax errors. */ - while (in_sequence_p ()) - end_sequence (); - - /* Outside function body, can't compute type's actual size - until next function's body starts. */ - immediate_size_expand--; - - /* If doing stupid register allocation, - mark register parms as dying here. */ - - if (obey_regdecls) - { - rtx tem; - for (i = LAST_VIRTUAL_REGISTER + 1; i < max_parm_reg; i++) - use_variable (regno_reg_rtx[i]); - - /* Likewise for the regs of all the SAVE_EXPRs in the function. */ - - for (tem = save_expr_regs; tem; tem = XEXP (tem, 1)) - { - use_variable (XEXP (tem, 0)); - use_variable_after (XEXP (tem, 0), parm_birth_insn); - } - - if (current_function_internal_arg_pointer != virtual_incoming_args_rtx) - use_variable (current_function_internal_arg_pointer); - } - - clear_pending_stack_adjust (); - do_pending_stack_adjust (); - - /* Mark the end of the function body. - If control reaches this insn, the function can drop through - without returning a value. */ - emit_note (NULL_PTR, NOTE_INSN_FUNCTION_END); - - /* Output a linenumber for the end of the function. - SDB depends on this. */ - emit_line_note_force (filename, line); - - /* Output the label for the actual return from the function, - if one is expected. This happens either because a function epilogue - is used instead of a return instruction, or because a return was done - with a goto in order to run local cleanups, or because of pcc-style - structure returning. */ - - if (return_label) - emit_label (return_label); - - /* C++ uses this. */ - if (end_bindings) - expand_end_bindings (0, 0, 0); - - /* Now handle any leftover exception regions that may have been - created for the parameters. */ - { - rtx last = get_last_insn (); - rtx label; - - expand_leftover_cleanups (); - - /* If the above emitted any code, may sure we jump around it. */ - if (last != get_last_insn ()) - { - label = gen_label_rtx (); - last = emit_jump_insn_after (gen_jump (label), last); - last = emit_barrier_after (last); - emit_label (label); - } - } - - if (current_function_instrument_entry_exit) - { - rtx fun = DECL_RTL (current_function_decl); - if (GET_CODE (fun) == MEM) - fun = XEXP (fun, 0); - else - abort (); - emit_library_call (profile_function_exit_libfunc, 0, VOIDmode, 2, - fun, Pmode, - expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS, - 0, - hard_frame_pointer_rtx), - Pmode); - } - - /* If we had calls to alloca, and this machine needs - an accurate stack pointer to exit the function, - insert some code to save and restore the stack pointer. */ -#ifdef EXIT_IGNORE_STACK - if (! EXIT_IGNORE_STACK) -#endif - if (current_function_calls_alloca) - { - rtx tem = 0; - - emit_stack_save (SAVE_FUNCTION, &tem, parm_birth_insn); - emit_stack_restore (SAVE_FUNCTION, tem, NULL_RTX); - } - - /* If scalar return value was computed in a pseudo-reg, - copy that to the hard return register. */ - if (DECL_RTL (DECL_RESULT (current_function_decl)) != 0 - && GET_CODE (DECL_RTL (DECL_RESULT (current_function_decl))) == REG - && (REGNO (DECL_RTL (DECL_RESULT (current_function_decl))) - >= FIRST_PSEUDO_REGISTER)) - { - rtx real_decl_result; - -#ifdef FUNCTION_OUTGOING_VALUE - real_decl_result - = FUNCTION_OUTGOING_VALUE (TREE_TYPE (DECL_RESULT (current_function_decl)), - current_function_decl); -#else - real_decl_result - = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (current_function_decl)), - current_function_decl); -#endif - REG_FUNCTION_VALUE_P (real_decl_result) = 1; - /* If this is a BLKmode structure being returned in registers, then use - the mode computed in expand_return. */ - if (GET_MODE (real_decl_result) == BLKmode) - PUT_MODE (real_decl_result, - GET_MODE (DECL_RTL (DECL_RESULT (current_function_decl)))); - emit_move_insn (real_decl_result, - DECL_RTL (DECL_RESULT (current_function_decl))); - emit_insn (gen_rtx_USE (VOIDmode, real_decl_result)); - - /* The delay slot scheduler assumes that current_function_return_rtx - holds the hard register containing the return value, not a temporary - pseudo. */ - current_function_return_rtx = real_decl_result; - } - - /* If returning a structure, arrange to return the address of the value - in a place where debuggers expect to find it. - - If returning a structure PCC style, - the caller also depends on this value. - And current_function_returns_pcc_struct is not necessarily set. */ - if (current_function_returns_struct - || current_function_returns_pcc_struct) - { - rtx value_address = XEXP (DECL_RTL (DECL_RESULT (current_function_decl)), 0); - tree type = TREE_TYPE (DECL_RESULT (current_function_decl)); -#ifdef FUNCTION_OUTGOING_VALUE - rtx outgoing - = FUNCTION_OUTGOING_VALUE (build_pointer_type (type), - current_function_decl); -#else - rtx outgoing - = FUNCTION_VALUE (build_pointer_type (type), - current_function_decl); -#endif - - /* Mark this as a function return value so integrate will delete the - assignment and USE below when inlining this function. */ - REG_FUNCTION_VALUE_P (outgoing) = 1; - - emit_move_insn (outgoing, value_address); - use_variable (outgoing); - } - - /* If this is an implementation of __throw, do what's necessary to - communicate between __builtin_eh_return and the epilogue. */ - expand_eh_return (); - - /* Output a return insn if we are using one. - Otherwise, let the rtl chain end here, to drop through - into the epilogue. */ - -#ifdef HAVE_return - if (HAVE_return) - { - emit_jump_insn (gen_return ()); - emit_barrier (); - } -#endif - - /* Fix up any gotos that jumped out to the outermost - binding level of the function. - Must follow emitting RETURN_LABEL. */ - - /* If you have any cleanups to do at this point, - and they need to create temporary variables, - then you will lose. */ - expand_fixups (get_insns ()); -} - -/* These arrays record the INSN_UIDs of the prologue and epilogue insns. */ - -static int *prologue; -static int *epilogue; - -/* Create an array that records the INSN_UIDs of INSNS (either a sequence - or a single insn). */ - -#if defined (HAVE_prologue) || defined (HAVE_epilogue) -static int * -record_insns (insns) - rtx insns; -{ - int *vec; - - if (GET_CODE (insns) == SEQUENCE) - { - int len = XVECLEN (insns, 0); - vec = (int *) oballoc ((len + 1) * sizeof (int)); - vec[len] = 0; - while (--len >= 0) - vec[len] = INSN_UID (XVECEXP (insns, 0, len)); - } - else - { - vec = (int *) oballoc (2 * sizeof (int)); - vec[0] = INSN_UID (insns); - vec[1] = 0; - } - return vec; -} - -/* Determine how many INSN_UIDs in VEC are part of INSN. */ - -static int -contains (insn, vec) - rtx insn; - int *vec; -{ - register int i, j; - - if (GET_CODE (insn) == INSN - && GET_CODE (PATTERN (insn)) == SEQUENCE) - { - int count = 0; - for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--) - for (j = 0; vec[j]; j++) - if (INSN_UID (XVECEXP (PATTERN (insn), 0, i)) == vec[j]) - count++; - return count; - } - else - { - for (j = 0; vec[j]; j++) - if (INSN_UID (insn) == vec[j]) - return 1; - } - return 0; -} -#endif /* HAVE_prologue || HAVE_epilogue */ - -/* Generate the prologue and epilogue RTL if the machine supports it. Thread - this into place with notes indicating where the prologue ends and where - the epilogue begins. Update the basic block information when possible. */ - -void -thread_prologue_and_epilogue_insns (f) - rtx f ATTRIBUTE_UNUSED; -{ -#ifdef HAVE_prologue - if (HAVE_prologue) - { - rtx head, seq; - - /* The first insn (a NOTE_INSN_DELETED) is followed by zero or more - prologue insns and a NOTE_INSN_PROLOGUE_END. */ - emit_note_after (NOTE_INSN_PROLOGUE_END, f); - seq = gen_prologue (); - head = emit_insn_after (seq, f); - - /* Include the new prologue insns in the first block. Ignore them - if they form a basic block unto themselves. */ - if (x_basic_block_head && n_basic_blocks - && GET_CODE (BLOCK_HEAD (0)) != CODE_LABEL) - BLOCK_HEAD (0) = NEXT_INSN (f); - - /* Retain a map of the prologue insns. */ - prologue = record_insns (GET_CODE (seq) == SEQUENCE ? seq : head); - } - else -#endif - prologue = 0; - -#ifdef HAVE_epilogue - if (HAVE_epilogue) - { - rtx insn = get_last_insn (); - rtx prev = prev_nonnote_insn (insn); - - /* If we end with a BARRIER, we don't need an epilogue. */ - if (! (prev && GET_CODE (prev) == BARRIER)) - { - rtx tail, seq, tem; - rtx first_use = 0; - rtx last_use = 0; - - /* The last basic block ends with a NOTE_INSN_EPILOGUE_BEG, the - epilogue insns, the USE insns at the end of a function, - the jump insn that returns, and then a BARRIER. */ - - /* Move the USE insns at the end of a function onto a list. */ - while (prev - && GET_CODE (prev) == INSN - && GET_CODE (PATTERN (prev)) == USE) - { - tem = prev; - prev = prev_nonnote_insn (prev); - - NEXT_INSN (PREV_INSN (tem)) = NEXT_INSN (tem); - PREV_INSN (NEXT_INSN (tem)) = PREV_INSN (tem); - if (first_use) - { - NEXT_INSN (tem) = first_use; - PREV_INSN (first_use) = tem; - } - first_use = tem; - if (!last_use) - last_use = tem; - } - - emit_barrier_after (insn); - - seq = gen_epilogue (); - tail = emit_jump_insn_after (seq, insn); - - /* Insert the USE insns immediately before the return insn, which - must be the first instruction before the final barrier. */ - if (first_use) - { - tem = prev_nonnote_insn (get_last_insn ()); - NEXT_INSN (PREV_INSN (tem)) = first_use; - PREV_INSN (first_use) = PREV_INSN (tem); - PREV_INSN (tem) = last_use; - NEXT_INSN (last_use) = tem; - } - - emit_note_after (NOTE_INSN_EPILOGUE_BEG, insn); - - /* Include the new epilogue insns in the last block. Ignore - them if they form a basic block unto themselves. */ - if (x_basic_block_end && n_basic_blocks - && GET_CODE (BLOCK_END (n_basic_blocks - 1)) != JUMP_INSN) - BLOCK_END (n_basic_blocks - 1) = tail; - - /* Retain a map of the epilogue insns. */ - epilogue = record_insns (GET_CODE (seq) == SEQUENCE ? seq : tail); - return; - } - } -#endif - epilogue = 0; -} - -/* Reposition the prologue-end and epilogue-begin notes after instruction - scheduling and delayed branch scheduling. */ - -void -reposition_prologue_and_epilogue_notes (f) - rtx f ATTRIBUTE_UNUSED; -{ -#if defined (HAVE_prologue) || defined (HAVE_epilogue) - /* Reposition the prologue and epilogue notes. */ - if (n_basic_blocks) - { - int len; - - if (prologue) - { - register rtx insn, note = 0; - - /* Scan from the beginning until we reach the last prologue insn. - We apparently can't depend on basic_block_{head,end} after - reorg has run. */ - for (len = 0; prologue[len]; len++) - ; - for (insn = f; len && insn; insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) == NOTE) - { - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_PROLOGUE_END) - note = insn; - } - else if ((len -= contains (insn, prologue)) == 0) - { - rtx next; - /* Find the prologue-end note if we haven't already, and - move it to just after the last prologue insn. */ - if (note == 0) - { - for (note = insn; (note = NEXT_INSN (note));) - if (GET_CODE (note) == NOTE - && NOTE_LINE_NUMBER (note) == NOTE_INSN_PROLOGUE_END) - break; - } - - next = NEXT_INSN (note); - - /* Whether or not we can depend on BLOCK_HEAD, - attempt to keep it up-to-date. */ - if (BLOCK_HEAD (0) == note) - BLOCK_HEAD (0) = next; - - remove_insn (note); - add_insn_after (note, insn); - } - } - } - - if (epilogue) - { - register rtx insn, note = 0; - - /* Scan from the end until we reach the first epilogue insn. - We apparently can't depend on basic_block_{head,end} after - reorg has run. */ - for (len = 0; epilogue[len]; len++) - ; - for (insn = get_last_insn (); len && insn; insn = PREV_INSN (insn)) - { - if (GET_CODE (insn) == NOTE) - { - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EPILOGUE_BEG) - note = insn; - } - else if ((len -= contains (insn, epilogue)) == 0) - { - /* Find the epilogue-begin note if we haven't already, and - move it to just before the first epilogue insn. */ - if (note == 0) - { - for (note = insn; (note = PREV_INSN (note));) - if (GET_CODE (note) == NOTE - && NOTE_LINE_NUMBER (note) == NOTE_INSN_EPILOGUE_BEG) - break; - } - - /* Whether or not we can depend on BLOCK_HEAD, - attempt to keep it up-to-date. */ - if (n_basic_blocks - && BLOCK_HEAD (n_basic_blocks-1) == insn) - BLOCK_HEAD (n_basic_blocks-1) = note; - - remove_insn (note); - add_insn_before (note, insn); - } - } - } - } -#endif /* HAVE_prologue or HAVE_epilogue */ -} diff --git a/gcc/rtl_020422.c b/gcc/rtl_020422.c deleted file mode 100755 index 291c157..0000000 --- a/gcc/rtl_020422.c +++ /dev/null @@ -1,935 +0,0 @@ -/* Allocate and read RTL for GNU C Compiler. - Copyright (C) 1987, 1988, 1991, 1994, 1997, 1998, 2002 - Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC 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 2, or (at your option) -any later version. - -GNU CC 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 GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ - - -#include "config.h" -#include "system.h" -#include "rtl.h" -#include "real.h" -#include "bitmap.h" - -#include "obstack.h" -#define obstack_chunk_alloc xmalloc -#define obstack_chunk_free free - -/* Obstack used for allocating RTL objects. - Between functions, this is the permanent_obstack. - While parsing and expanding a function, this is maybepermanent_obstack - so we can save it if it is an inline function. - During optimization and output, this is function_obstack. */ - -extern struct obstack *rtl_obstack; - -/* Indexed by rtx code, gives number of operands for an rtx with that code. - Does NOT include rtx header data (code and links). - This array is initialized in init_rtl. */ - -int rtx_length[NUM_RTX_CODE + 1]; - -/* Indexed by rtx code, gives the name of that kind of rtx, as a C string. */ - -#define DEF_RTL_EXPR(ENUM, NAME, FORMAT, CLASS) NAME , - -char *rtx_name[] = { -#include "rtl.def" /* rtl expressions are documented here */ -}; - -#undef DEF_RTL_EXPR - -/* Indexed by machine mode, gives the name of that machine mode. - This name does not include the letters "mode". */ - -#define DEF_MACHMODE(SYM, NAME, CLASS, SIZE, UNIT, WIDER) NAME, - -char *mode_name[(int) MAX_MACHINE_MODE + 1] = { -#include "machmode.def" - -#ifdef EXTRA_CC_MODES - EXTRA_CC_NAMES, -#endif - /* Add an extra field to avoid a core dump if someone tries to convert - MAX_MACHINE_MODE to a string. */ - "" -}; - -#undef DEF_MACHMODE - -/* Indexed by machine mode, gives the length of the mode, in bytes. - GET_MODE_CLASS uses this. */ - -#define DEF_MACHMODE(SYM, NAME, CLASS, SIZE, UNIT, WIDER) CLASS, - -enum mode_class mode_class[(int) MAX_MACHINE_MODE] = { -#include "machmode.def" -}; - -#undef DEF_MACHMODE - -/* Indexed by machine mode, gives the length of the mode, in bytes. - GET_MODE_SIZE uses this. */ - -#define DEF_MACHMODE(SYM, NAME, CLASS, SIZE, UNIT, WIDER) SIZE, - -int mode_size[(int) MAX_MACHINE_MODE] = { -#include "machmode.def" -}; - -#undef DEF_MACHMODE - -/* Indexed by machine mode, gives the length of the mode's subunit. - GET_MODE_UNIT_SIZE uses this. */ - -#define DEF_MACHMODE(SYM, NAME, CLASS, SIZE, UNIT, WIDER) UNIT, - -int mode_unit_size[(int) MAX_MACHINE_MODE] = { -#include "machmode.def" /* machine modes are documented here */ -}; - -#undef DEF_MACHMODE - -/* Indexed by machine mode, gives next wider natural mode - (QI -> HI -> SI -> DI, etc.) Widening multiply instructions - use this. */ - -#define DEF_MACHMODE(SYM, NAME, CLASS, SIZE, UNIT, WIDER) \ - (unsigned char) WIDER, - -unsigned char mode_wider_mode[(int) MAX_MACHINE_MODE] = { -#include "machmode.def" /* machine modes are documented here */ -}; - -#undef DEF_MACHMODE - -#define DEF_MACHMODE(SYM, NAME, CLASS, SIZE, UNIT, WIDER) \ - ((SIZE) * BITS_PER_UNIT >= HOST_BITS_PER_WIDE_INT) ? ~(unsigned HOST_WIDE_INT)0 : ((unsigned HOST_WIDE_INT) 1 << (SIZE) * BITS_PER_UNIT) - 1, - -/* Indexed by machine mode, gives mask of significant bits in mode. */ - -unsigned HOST_WIDE_INT mode_mask_array[(int) MAX_MACHINE_MODE] = { -#include "machmode.def" -}; - -/* Indexed by mode class, gives the narrowest mode for each class. */ - -enum machine_mode class_narrowest_mode[(int) MAX_MODE_CLASS]; - -/* Indexed by rtx code, gives a sequence of operand-types for - rtx's of that code. The sequence is a C string in which - each character describes one operand. */ - -char *rtx_format[] = { - /* "*" undefined. - can cause a warning message - "0" field is unused (or used in a phase-dependent manner) - prints nothing - "i" an integer - prints the integer - "n" like "i", but prints entries from `note_insn_name' - "w" an integer of width HOST_BITS_PER_WIDE_INT - prints the integer - "s" a pointer to a string - prints the string - "S" like "s", but optional: - the containing rtx may end before this operand - "e" a pointer to an rtl expression - prints the expression - "E" a pointer to a vector that points to a number of rtl expressions - prints a list of the rtl expressions - "V" like "E", but optional: - the containing rtx may end before this operand - "u" a pointer to another insn - prints the uid of the insn. - "b" is a pointer to a bitmap header. - "t" is a tree pointer. */ - -#define DEF_RTL_EXPR(ENUM, NAME, FORMAT, CLASS) FORMAT , -#include "rtl.def" /* rtl expressions are defined here */ -#undef DEF_RTL_EXPR -}; - -/* Indexed by rtx code, gives a character representing the "class" of - that rtx code. See rtl.def for documentation on the defined classes. */ - -char rtx_class[] = { -#define DEF_RTL_EXPR(ENUM, NAME, FORMAT, CLASS) CLASS, -#include "rtl.def" /* rtl expressions are defined here */ -#undef DEF_RTL_EXPR -}; - -/* Names for kinds of NOTEs and REG_NOTEs. */ - -char *note_insn_name[] = { 0 , "NOTE_INSN_DELETED", - "NOTE_INSN_BLOCK_BEG", "NOTE_INSN_BLOCK_END", - "NOTE_INSN_LOOP_BEG", "NOTE_INSN_LOOP_END", - "NOTE_INSN_FUNCTION_END", "NOTE_INSN_SETJMP", - "NOTE_INSN_LOOP_CONT", "NOTE_INSN_LOOP_VTOP", - "NOTE_INSN_PROLOGUE_END", "NOTE_INSN_EPILOGUE_BEG", - "NOTE_INSN_DELETED_LABEL", "NOTE_INSN_FUNCTION_BEG", - "NOTE_INSN_EH_REGION_BEG", "NOTE_INSN_EH_REGION_END", - "NOTE_REPEATED_LINE_NUMBER", "NOTE_INSN_RANGE_START", - "NOTE_INSN_RANGE_END", "NOTE_INSN_LIVE" }; - -char *reg_note_name[] = { "", "REG_DEAD", "REG_INC", "REG_EQUIV", "REG_WAS_0", - "REG_EQUAL", "REG_RETVAL", "REG_LIBCALL", - "REG_NONNEG", "REG_NO_CONFLICT", "REG_UNUSED", - "REG_CC_SETTER", "REG_CC_USER", "REG_LABEL", - "REG_DEP_ANTI", "REG_DEP_OUTPUT", - "REG_NOALIAS", "REG_SAVE_AREA", - "REG_BR_PRED", "REG_EH_CONTEXT", - "REG_FRAME_RELATED_EXPR", "REG_EH_REGION", - "REG_EH_RETHROW" }; - -static void dump_and_abort PROTO((int, int, FILE *)) ATTRIBUTE_NORETURN; -static void read_name PROTO((char *, FILE *)); - -/* Allocate an rtx vector of N elements. - Store the length, and initialize all elements to zero. */ - -rtvec -rtvec_alloc (n) - int n; -{ - rtvec rt; - int i; - - rt = (rtvec) obstack_alloc (rtl_obstack, - sizeof (struct rtvec_def) - + (( n - 1) * sizeof (rtunion))); - - /* clear out the vector */ - PUT_NUM_ELEM (rt, n); - - for (i = 0; i < n; i++) - rt->elem[i].rtwint = 0; - - return rt; -} - -/* Allocate an rtx of code CODE. The CODE is stored in the rtx; - all the rest is initialized to zero. */ - -rtx -rtx_alloc (code) - RTX_CODE code; -{ - rtx rt; - register struct obstack *ob = rtl_obstack; - register int nelts = GET_RTX_LENGTH (code); - register int length = sizeof (struct rtx_def) - + (nelts - 1) * sizeof (rtunion); - - /* This function is called more than any other in GCC, - so we manipulate the obstack directly. - - Even though rtx objects are word aligned, we may be sharing an obstack - with tree nodes, which may have to be double-word aligned. So align - our length to the alignment mask in the obstack. */ - - length = (length + ob->alignment_mask) & ~ ob->alignment_mask; - - if (ob->chunk_limit - ob->next_free < length) - _obstack_newchunk (ob, length); - rt = (rtx)ob->object_base; - ob->next_free += length; - ob->object_base = ob->next_free; - - /* We want to clear everything up to the FLD array. Normally, this is - one int, but we don't want to assume that and it isn't very portable - anyway; this is. */ - - memset (rt, 0, sizeof (struct rtx_def) - sizeof (rtunion)); - - PUT_CODE (rt, code); - - return rt; -} - -/* Free the rtx X and all RTL allocated since X. */ - -void -rtx_free (x) - rtx x; -{ - obstack_free (rtl_obstack, x); -} - -/* Create a new copy of an rtx. - Recursively copies the operands of the rtx, - except for those few rtx codes that are sharable. */ - -rtx -copy_rtx (orig) - register rtx orig; -{ - switch (GET_CODE (orig)) - { - case REG: - case QUEUED: - case CONST_INT: - case CONST_DOUBLE: - case SYMBOL_REF: - case CODE_LABEL: - case PC: - case CC0: - case SCRATCH: - /* SCRATCH must be shared because they represent distinct values. */ - case ADDRESSOF: - return orig; - - case CONST: - /* CONST can be shared if it contains a SYMBOL_REF. If it contains - a LABEL_REF, it isn't sharable. */ - if (GET_CODE (XEXP (orig, 0)) == PLUS - && GET_CODE (XEXP (XEXP (orig, 0), 0)) == SYMBOL_REF - && GET_CODE (XEXP (XEXP (orig, 0), 1)) == CONST_INT) - return orig; - break; - - /* A MEM with a constant address is not sharable. The problem is that - the constant address may need to be reloaded. If the mem is shared, - then reloading one copy of this mem will cause all copies to appear - to have been reloaded. */ - - default: - break; - } - - return really_copy_rtx (orig); -} - -/* Create a new copy of an rtx, even if it could be sharable. */ - -rtx -really_copy_rtx (orig) - rtx orig; -{ - register rtx copy; - register int i, j; - register RTX_CODE code; - register char *format_ptr; - - code = GET_CODE (orig); - - copy = rtx_alloc (code); - PUT_MODE (copy, GET_MODE (orig)); - copy->in_struct = orig->in_struct; - copy->volatil = orig->volatil; - copy->unchanging = orig->unchanging; - copy->integrated = orig->integrated; - - format_ptr = GET_RTX_FORMAT (GET_CODE (copy)); - - for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++) - { - switch (*format_ptr++) - { - case 'e': - XEXP (copy, i) = XEXP (orig, i); - if (XEXP (orig, i) != NULL) - XEXP (copy, i) = copy_rtx (XEXP (orig, i)); - break; - - case '0': - case 'u': - XEXP (copy, i) = XEXP (orig, i); - break; - - case 'E': - case 'V': - XVEC (copy, i) = XVEC (orig, i); - if (XVEC (orig, i) != NULL) - { - XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i)); - for (j = 0; j < XVECLEN (copy, i); j++) - XVECEXP (copy, i, j) = copy_rtx (XVECEXP (orig, i, j)); - } - break; - - case 'b': - { - bitmap new_bits = BITMAP_OBSTACK_ALLOC (rtl_obstack); - bitmap_copy (new_bits, XBITMAP (orig, i)); - XBITMAP (copy, i) = new_bits; - break; - } - - case 't': - XTREE (copy, i) = XTREE (orig, i); - break; - - case 'w': - XWINT (copy, i) = XWINT (orig, i); - break; - - case 'i': - XINT (copy, i) = XINT (orig, i); - break; - - case 's': - case 'S': - XSTR (copy, i) = XSTR (orig, i); - break; - - default: - abort (); - } - } - return copy; -} - -/* Similar to `copy_rtx' except that if MAY_SHARE is present, it is - placed in the result directly, rather than being copied. */ - -rtx -copy_most_rtx (orig, may_share) - register rtx orig; - register rtx may_share; -{ - register rtx copy; - register int i, j; - register RTX_CODE code; - register char *format_ptr; - - if (orig == may_share) - return orig; - - code = GET_CODE (orig); - - switch (code) - { - case REG: - case QUEUED: - case CONST_INT: - case CONST_DOUBLE: - case SYMBOL_REF: - case CODE_LABEL: - case PC: - case CC0: - return orig; - default: - break; - } - - copy = rtx_alloc (code); - PUT_MODE (copy, GET_MODE (orig)); - copy->in_struct = orig->in_struct; - copy->volatil = orig->volatil; - copy->unchanging = orig->unchanging; - copy->integrated = orig->integrated; - - format_ptr = GET_RTX_FORMAT (GET_CODE (copy)); - - for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++) - { - switch (*format_ptr++) - { - case 'e': - XEXP (copy, i) = XEXP (orig, i); - if (XEXP (orig, i) != NULL && XEXP (orig, i) != may_share) - XEXP (copy, i) = copy_most_rtx (XEXP (orig, i), may_share); - break; - - case '0': - case 'u': - XEXP (copy, i) = XEXP (orig, i); - break; - - case 'E': - case 'V': - XVEC (copy, i) = XVEC (orig, i); - if (XVEC (orig, i) != NULL) - { - XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i)); - for (j = 0; j < XVECLEN (copy, i); j++) - XVECEXP (copy, i, j) - = copy_most_rtx (XVECEXP (orig, i, j), may_share); - } - break; - - case 'w': - XWINT (copy, i) = XWINT (orig, i); - break; - - case 'n': - case 'i': - XINT (copy, i) = XINT (orig, i); - break; - - case 's': - case 'S': - XSTR (copy, i) = XSTR (orig, i); - break; - - default: - abort (); - } - } - return copy; -} - -/* Subroutines of read_rtx. */ - -/* Dump code after printing a message. Used when read_rtx finds - invalid data. */ - -static void -dump_and_abort (expected_c, actual_c, infile) - int expected_c, actual_c; - FILE *infile; -{ - int c, i; - - if (expected_c >= 0) - fprintf (stderr, - "Expected character %c. Found character %c.", - expected_c, actual_c); - fprintf (stderr, " At file position: %ld\n", ftell (infile)); - fprintf (stderr, "Following characters are:\n\t"); - for (i = 0; i < 200; i++) - { - c = getc (infile); - if (EOF == c) break; - putc (c, stderr); - } - fprintf (stderr, "Aborting.\n"); - abort (); -} - -/* Read chars from INFILE until a non-whitespace char - and return that. Comments, both Lisp style and C style, - are treated as whitespace. - Tools such as genflags use this function. */ - -int -read_skip_spaces (infile) - FILE *infile; -{ - register int c; - while ((c = getc (infile))) - { - if (c == ' ' || c == '\n' || c == '\t' || c == '\f') - ; - else if (c == ';') - { - while ((c = getc (infile)) && c != '\n' && c != EOF) - ; - } - else if (c == '/') - { - register int prevc; - c = getc (infile); - if (c != '*') - dump_and_abort ('*', c, infile); - - prevc = 0; - while ((c = getc (infile)) && c != EOF) - { - if (prevc == '*' && c == '/') - break; - prevc = c; - } - } - else break; - } - return c; -} - -/* Read an rtx code name into the buffer STR[]. - It is terminated by any of the punctuation chars of rtx printed syntax. */ - -static void -read_name (str, infile) - char *str; - FILE *infile; -{ - register char *p; - register int c; - - c = read_skip_spaces(infile); - - p = str; - while (1) - { - if (c == ' ' || c == '\n' || c == '\t' || c == '\f') - break; - if (c == ':' || c == ')' || c == ']' || c == '"' || c == '/' - || c == '(' || c == '[') - { - ungetc (c, infile); - break; - } - *p++ = c; - c = getc (infile); - } - if (p == str) - { - fprintf (stderr, "missing name or number"); - dump_and_abort (-1, -1, infile); - } - - *p = 0; -} - -/* Provide a version of a function to read a long long if the system does - not provide one. */ -#if HOST_BITS_PER_WIDE_INT > HOST_BITS_PER_LONG && !defined(HAVE_ATOLL) && !defined(HAVE_ATOQ) -HOST_WIDE_INT -atoll(p) - const char *p; -{ - int neg = 0; - HOST_WIDE_INT tmp_wide; - - while (ISSPACE(*p)) - p++; - if (*p == '-') - neg = 1, p++; - else if (*p == '+') - p++; - - tmp_wide = 0; - while (ISDIGIT(*p)) - { - HOST_WIDE_INT new_wide = tmp_wide*10 + (*p - '0'); - if (new_wide < tmp_wide) - { - /* Return INT_MAX equiv on overflow. */ - tmp_wide = (~(unsigned HOST_WIDE_INT)0) >> 1; - break; - } - tmp_wide = new_wide; - p++; - } - - if (neg) - tmp_wide = -tmp_wide; - return tmp_wide; -} -#endif - -/* Read an rtx in printed representation from INFILE - and return an actual rtx in core constructed accordingly. - read_rtx is not used in the compiler proper, but rather in - the utilities gen*.c that construct C code from machine descriptions. */ - -rtx -read_rtx (infile) - FILE *infile; -{ - register int i, j, list_counter; - RTX_CODE tmp_code; - register char *format_ptr; - /* tmp_char is a buffer used for reading decimal integers - and names of rtx types and machine modes. - Therefore, 256 must be enough. */ - char tmp_char[256]; - rtx return_rtx; - register int c; - int tmp_int; - HOST_WIDE_INT tmp_wide; - - /* Linked list structure for making RTXs: */ - struct rtx_list - { - struct rtx_list *next; - rtx value; /* Value of this node... */ - }; - - c = read_skip_spaces (infile); /* Should be open paren. */ - if (c != '(') - dump_and_abort ('(', c, infile); - - read_name (tmp_char, infile); - - tmp_code = UNKNOWN; - - for (i=0; i < NUM_RTX_CODE; i++) /* @@ might speed this search up */ - { - if (!(strcmp (tmp_char, GET_RTX_NAME (i)))) - { - tmp_code = (RTX_CODE) i; /* get value for name */ - break; - } - } - if (tmp_code == UNKNOWN) - { - fprintf (stderr, - "Unknown rtx read in rtl.read_rtx(). Code name was %s .", - tmp_char); - } - /* (NIL) stands for an expression that isn't there. */ - if (tmp_code == NIL) - { - /* Discard the closeparen. */ - while ((c = getc (infile)) && c != ')'); - return 0; - } - - return_rtx = rtx_alloc (tmp_code); /* if we end up with an insn expression - then we free this space below. */ - format_ptr = GET_RTX_FORMAT (GET_CODE (return_rtx)); - - /* If what follows is `: mode ', read it and - store the mode in the rtx. */ - - i = read_skip_spaces (infile); - if (i == ':') - { - register int k; - read_name (tmp_char, infile); - for (k = 0; k < NUM_MACHINE_MODES; k++) - if (!strcmp (GET_MODE_NAME (k), tmp_char)) - break; - - PUT_MODE (return_rtx, (enum machine_mode) k ); - } - else - ungetc (i, infile); - - for (i = 0; i < GET_RTX_LENGTH (GET_CODE (return_rtx)); i++) - switch (*format_ptr++) - { - /* 0 means a field for internal use only. - Don't expect it to be present in the input. */ - case '0': - break; - - case 'e': - case 'u': - XEXP (return_rtx, i) = read_rtx (infile); - break; - - case 'V': - /* 'V' is an optional vector: if a closeparen follows, - just store NULL for this element. */ - c = read_skip_spaces (infile); - ungetc (c, infile); - if (c == ')') - { - XVEC (return_rtx, i) = 0; - break; - } - /* Now process the vector. */ - - case 'E': - { - register struct rtx_list *next_rtx, *rtx_list_link; - struct rtx_list *list_rtx = NULL; - - c = read_skip_spaces (infile); - if (c != '[') - dump_and_abort ('[', c, infile); - - /* add expressions to a list, while keeping a count */ - next_rtx = NULL; - list_counter = 0; - while ((c = read_skip_spaces (infile)) && c != ']') - { - ungetc (c, infile); - list_counter++; - rtx_list_link = (struct rtx_list *) - alloca (sizeof (struct rtx_list)); - rtx_list_link->value = read_rtx (infile); - if (next_rtx == 0) - list_rtx = rtx_list_link; - else - next_rtx->next = rtx_list_link; - next_rtx = rtx_list_link; - rtx_list_link->next = 0; - } - /* get vector length and allocate it */ - XVEC (return_rtx, i) = (list_counter - ? rtvec_alloc (list_counter) : NULL_RTVEC); - if (list_counter > 0) - { - next_rtx = list_rtx; - for (j = 0; j < list_counter; j++, - next_rtx = next_rtx->next) - XVECEXP (return_rtx, i, j) = next_rtx->value; - } - /* close bracket gotten */ - } - break; - - case 'S': - /* 'S' is an optional string: if a closeparen follows, - just store NULL for this element. */ - c = read_skip_spaces (infile); - ungetc (c, infile); - if (c == ')') - { - XSTR (return_rtx, i) = 0; - break; - } - - case 's': - { - int saw_paren = 0; - register char *stringbuf; - - c = read_skip_spaces (infile); - if (c == '(') - { - saw_paren = 1; - c = read_skip_spaces (infile); - } - if (c != '"') - dump_and_abort ('"', c, infile); - - while (1) - { - c = getc (infile); /* Read the string */ - if (c == '\\') - { - c = getc (infile); /* Read the string */ - /* \; makes stuff for a C string constant containing - newline and tab. */ - if (c == ';') - { - obstack_grow (rtl_obstack, "\\n\\t", 4); - continue; - } - } - else if (c == '"') - break; - - obstack_1grow (rtl_obstack, c); - } - - obstack_1grow (rtl_obstack, 0); - stringbuf = (char *) obstack_finish (rtl_obstack); - - if (saw_paren) - { - c = read_skip_spaces (infile); - if (c != ')') - dump_and_abort (')', c, infile); - } - XSTR (return_rtx, i) = stringbuf; - } - break; - - case 'w': - read_name (tmp_char, infile); -#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT - tmp_wide = atoi (tmp_char); -#else -#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_LONG - tmp_wide = atol (tmp_char); -#else - /* Prefer atoll over atoq, since the former is in the ISO C9X draft. - But prefer not to use our hand-rolled function above either. */ -#if defined(HAVE_ATOLL) || !defined(HAVE_ATOQ) - tmp_wide = atoll (tmp_char); -#else - tmp_wide = atoq (tmp_char); -#endif -#endif -#endif - XWINT (return_rtx, i) = tmp_wide; - break; - - case 'i': - case 'n': - read_name (tmp_char, infile); - tmp_int = atoi (tmp_char); - XINT (return_rtx, i) = tmp_int; - break; - - default: - fprintf (stderr, - "switch format wrong in rtl.read_rtx(). format was: %c.\n", - format_ptr[-1]); - fprintf (stderr, "\tfile position: %ld\n", ftell (infile)); - abort (); - } - - c = read_skip_spaces (infile); - if (c != ')') - dump_and_abort (')', c, infile); - - return return_rtx; -} - -/* This is called once per compilation, before any rtx's are constructed. - It initializes the vector `rtx_length', the extra CC modes, if any, - and computes certain commonly-used modes. */ - -void -init_rtl () -{ - int min_class_size[(int) MAX_MODE_CLASS]; - enum machine_mode mode; - int i; - - for (i = 0; i < NUM_RTX_CODE; i++) - rtx_length[i] = strlen (rtx_format[i]); - - /* Make CONST_DOUBLE bigger, if real values are bigger than - it normally expects to have room for. - Note that REAL_VALUE_TYPE is not defined by default, - since tree.h is not included. But the default dfn as `double' - would do no harm. */ -#ifdef REAL_VALUE_TYPE - i = sizeof (REAL_VALUE_TYPE) / sizeof (rtunion) + 2; - if (rtx_length[(int) CONST_DOUBLE] < i) - { - char *s = (char *) xmalloc (i + 1); - rtx_length[(int) CONST_DOUBLE] = i; - rtx_format[(int) CONST_DOUBLE] = s; - *s++ = 'e'; - *s++ = '0'; - /* Set the GET_RTX_FORMAT of CONST_DOUBLE to a string - of as many `w's as we now have elements. Subtract two from - the size to account for the 'e' and the '0'. */ - for (i = 2; i < rtx_length[(int) CONST_DOUBLE]; i++) - *s++ = 'w'; - *s++ = 0; - } -#endif - -#ifdef EXTRA_CC_MODES - for (i = (int) CCmode + 1; i < (int) MAX_MACHINE_MODE; i++) - { - mode_class[i] = MODE_CC; - mode_mask_array[i] = mode_mask_array[(int) CCmode]; - mode_size[i] = mode_size[(int) CCmode]; - mode_unit_size[i] = mode_unit_size[(int) CCmode]; - mode_wider_mode[i - 1] = i; - mode_wider_mode[i] = (unsigned char)VOIDmode; - } -#endif - - /* Find the narrowest mode for each class. */ - - for (i = 0; i < (int) MAX_MODE_CLASS; i++) - min_class_size[i] = 1000; - - for (mode = VOIDmode; (int) mode < (int) MAX_MACHINE_MODE; - mode = (enum machine_mode) ((int) mode + 1)) - { - if (GET_MODE_SIZE (mode) < min_class_size[(int) GET_MODE_CLASS (mode)]) - { - class_narrowest_mode[(int) GET_MODE_CLASS (mode)] = mode; - min_class_size[(int) GET_MODE_CLASS (mode)] = GET_MODE_SIZE (mode); - } - } -} diff --git a/gcc/rtl_020422.h b/gcc/rtl_020422.h deleted file mode 100755 index 5e006a8..0000000 --- a/gcc/rtl_020422.h +++ /dev/null @@ -1,1569 +0,0 @@ -/* Register Transfer Language (RTL) definitions for GNU C-Compiler - Copyright (C) 1987, 91-97, 1998, 2002 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC 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 2, or (at your option) -any later version. - -GNU CC 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 GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ - -#ifndef _RTL_H -#define _RTL_H - -#include "machmode.h" - -#undef FFS /* Some systems predefine this symbol; don't let it interfere. */ -#undef FLOAT /* Likewise. */ -#undef ABS /* Likewise. */ -#undef PC /* Likewise. */ - -#ifndef TREE_CODE -union tree_node; -#endif - -/* Register Transfer Language EXPRESSIONS CODES */ - -#define RTX_CODE enum rtx_code -enum rtx_code { - -#define DEF_RTL_EXPR(ENUM, NAME, FORMAT, CLASS) ENUM , -#include "rtl.def" /* rtl expressions are documented here */ -#undef DEF_RTL_EXPR - - LAST_AND_UNUSED_RTX_CODE}; /* A convenient way to get a value for - NUM_RTX_CODE. - Assumes default enum value assignment. */ - -#define NUM_RTX_CODE ((int)LAST_AND_UNUSED_RTX_CODE) - /* The cast here, saves many elsewhere. */ - -extern int rtx_length[]; -#define GET_RTX_LENGTH(CODE) (rtx_length[(int) (CODE)]) - -extern char *rtx_name[]; -#define GET_RTX_NAME(CODE) (rtx_name[(int) (CODE)]) - -extern char *rtx_format[]; -#define GET_RTX_FORMAT(CODE) (rtx_format[(int) (CODE)]) - -extern char rtx_class[]; -#define GET_RTX_CLASS(CODE) (rtx_class[(int) (CODE)]) - -/* The flags and bitfields of an ADDR_DIFF_VEC. BASE is the base label - relative to which the offsets are calculated, as explained in rtl.def. */ -typedef struct -{ - /* Set at the start of shorten_branches - ONLY WHEN OPTIMIZING - : */ - unsigned min_align: 8; - /* Flags: */ - unsigned base_after_vec: 1; /* BASE is after the ADDR_DIFF_VEC. */ - unsigned min_after_vec: 1; /* minimum address target label is after the ADDR_DIFF_VEC. */ - unsigned max_after_vec: 1; /* maximum address target label is after the ADDR_DIFF_VEC. */ - unsigned min_after_base: 1; /* minimum address target label is after BASE. */ - unsigned max_after_base: 1; /* maximum address target label is after BASE. */ - /* Set by the actual branch shortening process - ONLY WHEN OPTIMIZING - : */ - unsigned offset_unsigned: 1; /* offsets have to be treated as unsigned. */ - unsigned : 2; - unsigned scale : 8; -} addr_diff_vec_flags; - -/* Common union for an element of an rtx. */ - -typedef union rtunion_def -{ - HOST_WIDE_INT rtwint; - int rtint; - char *rtstr; - struct rtx_def *rtx; - struct rtvec_def *rtvec; - enum machine_mode rttype; - addr_diff_vec_flags rt_addr_diff_vec_flags; - struct bitmap_head_def *rtbit; - union tree_node *rttree; -} rtunion; - -/* RTL expression ("rtx"). */ - -typedef struct rtx_def -{ -#ifdef ONLY_INT_FIELDS -#ifdef CODE_FIELD_BUG - unsigned int code : 16; -#else - unsigned short code; -#endif -#else - /* The kind of expression this is. */ - enum rtx_code code : 16; -#endif - /* The kind of value the expression has. */ -#ifdef ONLY_INT_FIELDS - int mode : 8; -#else - enum machine_mode mode : 8; -#endif - /* 1 in an INSN if it can alter flow of control - within this function. Not yet used! */ - unsigned int jump : 1; - /* 1 in an INSN if it can call another function. Not yet used! */ - unsigned int call : 1; - /* 1 in a MEM or REG if value of this expression will never change - during the current function, even though it is not - manifestly constant. - 1 in a SUBREG if it is from a promoted variable that is unsigned. - 1 in a SYMBOL_REF if it addresses something in the per-function - constants pool. - 1 in a CALL_INSN if it is a const call. - 1 in a JUMP_INSN if it is a branch that should be annulled. Valid from - reorg until end of compilation; cleared before used. */ - unsigned int unchanging : 1; - /* 1 in a MEM expression if contents of memory are volatile. - 1 in an INSN, CALL_INSN, JUMP_INSN, CODE_LABEL or BARRIER - if it is deleted. - 1 in a REG expression if corresponds to a variable declared by the user. - 0 for an internally generated temporary. - In a SYMBOL_REF, this flag is used for machine-specific purposes. - In a LABEL_REF or in a REG_LABEL note, this is LABEL_REF_NONLOCAL_P. */ - unsigned int volatil : 1; - /* 1 in a MEM referring to a field of an aggregate. - 0 if the MEM was a variable or the result of a * operator in C; - 1 if it was the result of a . or -> operator (on a struct) in C. - 1 in a REG if the register is used only in exit code a loop. - 1 in a SUBREG expression if was generated from a variable with a - promoted mode. - 1 in a CODE_LABEL if the label is used for nonlocal gotos - and must not be deleted even if its count is zero. - 1 in a LABEL_REF if this is a reference to a label outside the - current loop. - 1 in an INSN, JUMP_INSN, or CALL_INSN if this insn must be scheduled - together with the preceding insn. Valid only within sched. - 1 in an INSN, JUMP_INSN, or CALL_INSN if insn is in a delay slot and - from the target of a branch. Valid from reorg until end of compilation; - cleared before used. */ - unsigned int in_struct : 1; - /* 1 if this rtx is used. This is used for copying shared structure. - See `unshare_all_rtl'. - In a REG, this is not needed for that purpose, and used instead - in `leaf_renumber_regs_insn'. - In a SYMBOL_REF, means that emit_library_call - has used it as the function. */ - unsigned int used : 1; - /* Nonzero if this rtx came from procedure integration. - In a REG, nonzero means this reg refers to the return value - of the current function. - CYGNUS LOCAL unaligned-pointers - In a MEM, nonzero means that this address may be unaligned. - END CYGNUS LOCAL - */ - unsigned integrated : 1; - /* 1 in an INSN if this rtx is related to the call frame, - either changing how we compute the frame address or saving and - restoring registers in the prologue and epilogue. - 1 in a MEM if the MEM refers to a scalar, rather than a member of - an aggregate. */ - unsigned frame_related : 1; - /* The first element of the operands of this rtx. - The number of operands and their types are controlled - by the `code' field, according to rtl.def. */ - rtunion fld[1]; -} *rtx; - -#define NULL_RTX (rtx) 0 - -/* Define macros to access the `code' field of the rtx. */ - -#ifdef SHORT_ENUM_BUG -#define GET_CODE(RTX) ((enum rtx_code) ((RTX)->code)) -#define PUT_CODE(RTX, CODE) ((RTX)->code = ((short) (CODE))) -#else -#define GET_CODE(RTX) ((RTX)->code) -#define PUT_CODE(RTX, CODE) ((RTX)->code = (CODE)) -#endif - -#define GET_MODE(RTX) ((RTX)->mode) -#define PUT_MODE(RTX, MODE) ((RTX)->mode = (MODE)) - -#define RTX_INTEGRATED_P(RTX) ((RTX)->integrated) -#define RTX_UNCHANGING_P(RTX) ((RTX)->unchanging) -#define RTX_FRAME_RELATED_P(RTX) ((RTX)->frame_related) - -/* RTL vector. These appear inside RTX's when there is a need - for a variable number of things. The principle use is inside - PARALLEL expressions. */ - -typedef struct rtvec_def{ - int num_elem; /* number of elements */ - rtunion elem[1]; -} *rtvec; - -#define NULL_RTVEC (rtvec) 0 - -#define GET_NUM_ELEM(RTVEC) ((RTVEC)->num_elem) -#define PUT_NUM_ELEM(RTVEC, NUM) ((RTVEC)->num_elem = (NUM)) - -#define RTVEC_ELT(RTVEC, I) ((RTVEC)->elem[(I)].rtx) - -/* 1 if X is a REG. */ - -#define REG_P(X) (GET_CODE (X) == REG) - -/* 1 if X is a constant value that is an integer. */ - -#define CONSTANT_P(X) \ - (GET_CODE (X) == LABEL_REF || GET_CODE (X) == SYMBOL_REF \ - || GET_CODE (X) == CONST_INT || GET_CODE (X) == CONST_DOUBLE \ - || GET_CODE (X) == CONST || GET_CODE (X) == HIGH) - -/* General accessor macros for accessing the fields of an rtx. */ - -#define XEXP(RTX, N) ((RTX)->fld[N].rtx) -#define XINT(RTX, N) ((RTX)->fld[N].rtint) -#define XWINT(RTX, N) ((RTX)->fld[N].rtwint) -#define XSTR(RTX, N) ((RTX)->fld[N].rtstr) -#define XVEC(RTX, N) ((RTX)->fld[N].rtvec) -#define XVECLEN(RTX, N) ((RTX)->fld[N].rtvec->num_elem) -#define XVECEXP(RTX,N,M)((RTX)->fld[N].rtvec->elem[M].rtx) -#define XBITMAP(RTX, N) ((RTX)->fld[N].rtbit) -#define XTREE(RTX, N) ((RTX)->fld[N].rttree) - - -/* ACCESS MACROS for particular fields of insns. */ - -/* Holds a unique number for each insn. - These are not necessarily sequentially increasing. */ -#define INSN_UID(INSN) ((INSN)->fld[0].rtint) - -/* Chain insns together in sequence. */ -#define PREV_INSN(INSN) ((INSN)->fld[1].rtx) -#define NEXT_INSN(INSN) ((INSN)->fld[2].rtx) - -/* The body of an insn. */ -#define PATTERN(INSN) ((INSN)->fld[3].rtx) - -/* Code number of instruction, from when it was recognized. - -1 means this instruction has not been recognized yet. */ -#define INSN_CODE(INSN) ((INSN)->fld[4].rtint) - -/* Set up in flow.c; empty before then. - Holds a chain of INSN_LIST rtx's whose first operands point at - previous insns with direct data-flow connections to this one. - That means that those insns set variables whose next use is in this insn. - They are always in the same basic block as this insn. */ -#define LOG_LINKS(INSN) ((INSN)->fld[5].rtx) - -/* 1 if insn has been deleted. */ -#define INSN_DELETED_P(INSN) ((INSN)->volatil) - -/* 1 if insn is a call to a const function. */ -#define CONST_CALL_P(INSN) ((INSN)->unchanging) - -/* 1 if insn is a branch that should not unconditionally execute its - delay slots, i.e., it is an annulled branch. */ -#define INSN_ANNULLED_BRANCH_P(INSN) ((INSN)->unchanging) - -/* 1 if insn is in a delay slot and is from the target of the branch. If - the branch insn has INSN_ANNULLED_BRANCH_P set, this insn should only be - executed if the branch is taken. For annulled branches with this bit - clear, the insn should be executed only if the branch is not taken. */ -#define INSN_FROM_TARGET_P(INSN) ((INSN)->in_struct) - -/* Holds a list of notes on what this insn does to various REGs. - It is a chain of EXPR_LIST rtx's, where the second operand - is the chain pointer and the first operand is the REG being described. - The mode field of the EXPR_LIST contains not a real machine mode - but a value that says what this note says about the REG: - REG_DEAD means that the value in REG dies in this insn (i.e., it is - not needed past this insn). If REG is set in this insn, the REG_DEAD - note may, but need not, be omitted. - REG_INC means that the REG is autoincremented or autodecremented. - REG_EQUIV describes the insn as a whole; it says that the insn - sets a register to a constant value or to be equivalent to a memory - address. If the register is spilled to the stack then the constant - value should be substituted for it. The contents of the REG_EQUIV - is the constant value or memory address, which may be different - from the source of the SET although it has the same value. A - REG_EQUIV note may also appear on an insn which copies a register - parameter to a pseudo-register, if there is a memory address which - could be used to hold that pseudo-register throughout the function. - REG_EQUAL is like REG_EQUIV except that the destination - is only momentarily equal to the specified rtx. Therefore, it - cannot be used for substitution; but it can be used for cse. - REG_RETVAL means that this insn copies the return-value of - a library call out of the hard reg for return values. This note - is actually an INSN_LIST and it points to the first insn involved - in setting up arguments for the call. flow.c uses this to delete - the entire library call when its result is dead. - REG_LIBCALL is the inverse of REG_RETVAL: it goes on the first insn - of the library call and points at the one that has the REG_RETVAL. - REG_WAS_0 says that the register set in this insn held 0 before the insn. - The contents of the note is the insn that stored the 0. - If that insn is deleted or patched to a NOTE, the REG_WAS_0 is inoperative. - The REG_WAS_0 note is actually an INSN_LIST, not an EXPR_LIST. - REG_NONNEG means that the register is always nonnegative during - the containing loop. This is used in branches so that decrement and - branch instructions terminating on zero can be matched. There must be - an insn pattern in the md file named `decrement_and_branch_until_zero' - or else this will never be added to any instructions. - REG_NO_CONFLICT means there is no conflict *after this insn* - between the register in the note and the destination of this insn. - REG_UNUSED identifies a register set in this insn and never used. - REG_CC_SETTER and REG_CC_USER link a pair of insns that set and use - CC0, respectively. Normally, these are required to be consecutive insns, - but we permit putting a cc0-setting insn in the delay slot of a branch - as long as only one copy of the insn exists. In that case, these notes - point from one to the other to allow code generation to determine what - any require information and to properly update CC_STATUS. - REG_LABEL points to a CODE_LABEL. Used by non-JUMP_INSNs to - say that the CODE_LABEL contained in the REG_LABEL note is used - by the insn. - REG_DEP_ANTI is used in LOG_LINKS which represent anti (write after read) - dependencies. REG_DEP_OUTPUT is used in LOG_LINKS which represent output - (write after write) dependencies. Data dependencies, which are the only - type of LOG_LINK created by flow, are represented by a 0 reg note kind. */ -/* REG_SAVE_AREA is used to optimize rtl generated by dynamic stack - allocations for targets where SETJMP_VIA_SAVE_AREA is true. - REG_BR_PRED is attached to JUMP_INSNs only, it holds the branch prediction - flags computed by get_jump_flags() after dbr scheduling is complete. - REG_FRAME_RELATED_EXPR is attached to insns that are RTX_FRAME_RELATED_P, - but are too complex for DWARF to interpret what they imply. The attached - rtx is used instead of intuition. */ -/* REG_EH_REGION is used to indicate what exception region an INSN - belongs in. This can be used to indicate what region a call may throw - to. a REGION of 0 indicates that a call cannot throw at all. - REG_EH_RETHROW is used to indicate what that a call is actually a - call to rethrow, and specifies which region the rethrow is targetting. - This provides a way to generate the non standard flow edges required - for a rethrow. */ - - -#define REG_NOTES(INSN) ((INSN)->fld[6].rtx) - -#define ADDR_DIFF_VEC_FLAGS(RTX) ((RTX)->fld[4].rt_addr_diff_vec_flags) - -/* Don't forget to change reg_note_name in rtl.c. */ -enum reg_note { REG_DEAD = 1, REG_INC = 2, REG_EQUIV = 3, REG_WAS_0 = 4, - REG_EQUAL = 5, REG_RETVAL = 6, REG_LIBCALL = 7, - REG_NONNEG = 8, REG_NO_CONFLICT = 9, REG_UNUSED = 10, - REG_CC_SETTER = 11, REG_CC_USER = 12, REG_LABEL = 13, - REG_DEP_ANTI = 14, REG_DEP_OUTPUT = 15, - REG_NOALIAS = 16, REG_SAVE_AREA = 17, - REG_BR_PRED = 18, REG_EH_CONTEXT = 19, - REG_FRAME_RELATED_EXPR = 20, REG_EH_REGION = 21, - REG_EH_RETHROW = 22 }; - -/* Define macros to extract and insert the reg-note kind in an EXPR_LIST. */ -#define REG_NOTE_KIND(LINK) ((enum reg_note) GET_MODE (LINK)) -#define PUT_REG_NOTE_KIND(LINK,KIND) PUT_MODE(LINK, (enum machine_mode) (KIND)) - -/* Names for REG_NOTE's in EXPR_LIST insn's. */ - -extern char *reg_note_name[]; -#define GET_REG_NOTE_NAME(MODE) (reg_note_name[(int) (MODE)]) - -/* This field is only present on CALL_INSNs. It holds a chain of EXPR_LIST of - USE and CLOBBER expressions. - USE expressions list the registers filled with arguments that - are passed to the function. - CLOBBER expressions document the registers explicitly clobbered - by this CALL_INSN. - Pseudo registers can not be mentioned in this list. */ -#define CALL_INSN_FUNCTION_USAGE(INSN) ((INSN)->fld[7].rtx) - -/* The label-number of a code-label. The assembler label - is made from `L' and the label-number printed in decimal. - Label numbers are unique in a compilation. */ -#define CODE_LABEL_NUMBER(INSN) ((INSN)->fld[3].rtint) - -#define LINE_NUMBER NOTE - -/* In a NOTE that is a line number, this is a string for the file name that the - line is in. We use the same field to record block numbers temporarily in - NOTE_INSN_BLOCK_BEG and NOTE_INSN_BLOCK_END notes. (We avoid lots of casts - between ints and pointers if we use a different macro for the block number.) - The NOTE_INSN_RANGE_{START,END} and NOTE_INSN_LIVE notes record their - information as a rtx in the field. */ - -#define NOTE_SOURCE_FILE(INSN) ((INSN)->fld[3].rtstr) -#define NOTE_BLOCK_NUMBER(INSN) ((INSN)->fld[3].rtint) -#define NOTE_RANGE_INFO(INSN) ((INSN)->fld[3].rtx) -#define NOTE_LIVE_INFO(INSN) ((INSN)->fld[3].rtx) - -/* If the NOTE_BLOCK_NUMBER field gets a -1, it means create a new - block node for a live range block. */ -#define NOTE_BLOCK_LIVE_RANGE_BLOCK -1 - -/* In a NOTE that is a line number, this is the line number. - Other kinds of NOTEs are identified by negative numbers here. */ -#define NOTE_LINE_NUMBER(INSN) ((INSN)->fld[4].rtint) - -/* Codes that appear in the NOTE_LINE_NUMBER field - for kinds of notes that are not line numbers. - - Notice that we do not try to use zero here for any of - the special note codes because sometimes the source line - actually can be zero! This happens (for example) when we - are generating code for the per-translation-unit constructor - and destructor routines for some C++ translation unit. - - If you should change any of the following values, or if you - should add a new value here, don't forget to change the - note_insn_name array in rtl.c. */ - -/* This note is used to get rid of an insn - when it isn't safe to patch the insn out of the chain. */ -#define NOTE_INSN_DELETED -1 -#define NOTE_INSN_BLOCK_BEG -2 -#define NOTE_INSN_BLOCK_END -3 -#define NOTE_INSN_LOOP_BEG -4 -#define NOTE_INSN_LOOP_END -5 -/* This kind of note is generated at the end of the function body, - just before the return insn or return label. - In an optimizing compilation it is deleted by the first jump optimization, - after enabling that optimizer to determine whether control can fall - off the end of the function body without a return statement. */ -#define NOTE_INSN_FUNCTION_END -6 -/* This kind of note is generated just after each call to `setjmp', et al. */ -#define NOTE_INSN_SETJMP -7 -/* Generated at the place in a loop that `continue' jumps to. */ -#define NOTE_INSN_LOOP_CONT -8 -/* Generated at the start of a duplicated exit test. */ -#define NOTE_INSN_LOOP_VTOP -9 -/* This marks the point immediately after the last prologue insn. */ -#define NOTE_INSN_PROLOGUE_END -10 -/* This marks the point immediately prior to the first epilogue insn. */ -#define NOTE_INSN_EPILOGUE_BEG -11 -/* Generated in place of user-declared labels when they are deleted. */ -#define NOTE_INSN_DELETED_LABEL -12 -/* This note indicates the start of the real body of the function, - i.e. the point just after all of the parms have been moved into - their homes, etc. */ -#define NOTE_INSN_FUNCTION_BEG -13 -/* These note where exception handling regions begin and end. */ -#define NOTE_INSN_EH_REGION_BEG -14 -#define NOTE_INSN_EH_REGION_END -15 -/* Generated whenever a duplicate line number note is output. For example, - one is output after the end of an inline function, in order to prevent - the line containing the inline call from being counted twice in gcov. */ -#define NOTE_REPEATED_LINE_NUMBER -16 - -/* Start/end of a live range region, where pseudos allocated on the stack can - be allocated to temporary registers. */ -#define NOTE_INSN_RANGE_START -17 -#define NOTE_INSN_RANGE_END -18 -/* Record which registers are currently live. */ -#define NOTE_INSN_LIVE -19 - -#if 0 /* These are not used, and I don't know what they were for. --rms. */ -#define NOTE_DECL_NAME(INSN) ((INSN)->fld[3].rtstr) -#define NOTE_DECL_CODE(INSN) ((INSN)->fld[4].rtint) -#define NOTE_DECL_RTL(INSN) ((INSN)->fld[5].rtx) -#define NOTE_DECL_IDENTIFIER(INSN) ((INSN)->fld[6].rtint) -#define NOTE_DECL_TYPE(INSN) ((INSN)->fld[7].rtint) -#endif /* 0 */ - -/* Names for NOTE insn's other than line numbers. */ - -extern char *note_insn_name[]; -#define GET_NOTE_INSN_NAME(NOTE_CODE) (note_insn_name[-(NOTE_CODE)]) - -/* The name of a label, in case it corresponds to an explicit label - in the input source code. */ -#define LABEL_NAME(LABEL) ((LABEL)->fld[4].rtstr) - -/* In jump.c, each label contains a count of the number - of LABEL_REFs that point at it, so unused labels can be deleted. */ -#define LABEL_NUSES(LABEL) ((LABEL)->fld[5].rtint) - -/* The original regno this ADDRESSOF was built for. */ -#define ADDRESSOF_REGNO(RTX) ((RTX)->fld[1].rtint) - -/* The variable in the register we took the address of. */ -#define ADDRESSOF_DECL(X) ((tree) XEXP ((X), 2)) -#define SET_ADDRESSOF_DECL(X, T) (XEXP ((X), 2) = (rtx) (T)) - -/* In jump.c, each JUMP_INSN can point to a label that it can jump to, - so that if the JUMP_INSN is deleted, the label's LABEL_NUSES can - be decremented and possibly the label can be deleted. */ -#define JUMP_LABEL(INSN) ((INSN)->fld[7].rtx) - -/* Once basic blocks are found in flow.c, - each CODE_LABEL starts a chain that goes through - all the LABEL_REFs that jump to that label. - The chain eventually winds up at the CODE_LABEL; it is circular. */ -#define LABEL_REFS(LABEL) ((LABEL)->fld[6].rtx) - -/* This is the field in the LABEL_REF through which the circular chain - of references to a particular label is linked. - This chain is set up in flow.c. */ - -#define LABEL_NEXTREF(REF) ((REF)->fld[1].rtx) - -/* Once basic blocks are found in flow.c, - Each LABEL_REF points to its containing instruction with this field. */ - -#define CONTAINING_INSN(RTX) ((RTX)->fld[2].rtx) - -/* For a REG rtx, REGNO extracts the register number. */ - -#define REGNO(RTX) ((RTX)->fld[0].rtint) - -/* For a REG rtx, REG_FUNCTION_VALUE_P is nonzero if the reg - is the current function's return value. */ - -#define REG_FUNCTION_VALUE_P(RTX) ((RTX)->integrated) - -/* 1 in a REG rtx if it corresponds to a variable declared by the user. */ -#define REG_USERVAR_P(RTX) ((RTX)->volatil) - -/* For a CONST_INT rtx, INTVAL extracts the integer. */ - -#define INTVAL(RTX) ((RTX)->fld[0].rtwint) - -/* For a SUBREG rtx, SUBREG_REG extracts the value we want a subreg of. - SUBREG_WORD extracts the word-number. */ - -#define SUBREG_REG(RTX) ((RTX)->fld[0].rtx) -#define SUBREG_WORD(RTX) ((RTX)->fld[1].rtint) - -/* 1 if the REG contained in SUBREG_REG is already known to be - sign- or zero-extended from the mode of the SUBREG to the mode of - the reg. SUBREG_PROMOTED_UNSIGNED_P gives the signedness of the - extension. - - When used as a LHS, is means that this extension must be done - when assigning to SUBREG_REG. */ - -#define SUBREG_PROMOTED_VAR_P(RTX) ((RTX)->in_struct) -#define SUBREG_PROMOTED_UNSIGNED_P(RTX) ((RTX)->unchanging) - -/* Access various components of an ASM_OPERANDS rtx. */ - -#define ASM_OPERANDS_TEMPLATE(RTX) XSTR ((RTX), 0) -#define ASM_OPERANDS_OUTPUT_CONSTRAINT(RTX) XSTR ((RTX), 1) -#define ASM_OPERANDS_OUTPUT_IDX(RTX) XINT ((RTX), 2) -#define ASM_OPERANDS_INPUT_VEC(RTX) XVEC ((RTX), 3) -#define ASM_OPERANDS_INPUT_CONSTRAINT_VEC(RTX) XVEC ((RTX), 4) -#define ASM_OPERANDS_INPUT(RTX, N) XVECEXP ((RTX), 3, (N)) -#define ASM_OPERANDS_INPUT_LENGTH(RTX) XVECLEN ((RTX), 3) -#define ASM_OPERANDS_INPUT_CONSTRAINT(RTX, N) XSTR (XVECEXP ((RTX), 4, (N)), 0) -#define ASM_OPERANDS_INPUT_MODE(RTX, N) GET_MODE (XVECEXP ((RTX), 4, (N))) -#define ASM_OPERANDS_SOURCE_FILE(RTX) XSTR ((RTX), 5) -#define ASM_OPERANDS_SOURCE_LINE(RTX) XINT ((RTX), 6) - -/* For a MEM rtx, 1 if it's a volatile reference. - Also in an ASM_OPERANDS rtx. */ -#define MEM_VOLATILE_P(RTX) ((RTX)->volatil) - -/* For a MEM rtx, 1 if it refers to a field of an aggregate. If zero, - RTX may or may not refer to a field of an aggregate. */ -#define MEM_IN_STRUCT_P(RTX) ((RTX)->in_struct) - -/* For a MEM rtx, 1 if it refers to a scalar. If zero, RTX may or may - not refer to a scalar.*/ -#define MEM_SCALAR_P(RTX) ((RTX)->frame_related) - -/* Copy the MEM_VOLATILE_P, MEM_IN_STRUCT_P, and MEM_SCALAR_P - attributes from RHS to LHS. */ -#define MEM_COPY_ATTRIBUTES(LHS, RHS) \ - (MEM_VOLATILE_P (LHS) = MEM_VOLATILE_P (RHS), \ - MEM_IN_STRUCT_P (LHS) = MEM_IN_STRUCT_P (RHS), \ - MEM_SCALAR_P (LHS) = MEM_SCALAR_P (RHS)) \ - -/* If VAL is non-zero, set MEM_IN_STRUCT_P and clear MEM_SCALAR_P in - RTX. Otherwise, vice versa. Use this macro only when you are - *sure* that you know that the MEM is in a structure, or is a - scalar. VAL is evaluated only once. */ -#define MEM_SET_IN_STRUCT_P(RTX, VAL) \ - ((VAL) ? (MEM_IN_STRUCT_P (RTX) = 1, MEM_SCALAR_P (RTX) = 0) \ - : (MEM_IN_STRUCT_P (RTX) = 0, MEM_SCALAR_P (RTX) = 1)) - -/* CYGNUS LOCAL unaligned-pointers */ -/* For a MEM rtx, 1 if it may be an unaligned address. */ -#define MEM_UNALIGNED_P(RTX) ((RTX)->integrated) -/* END CYGNUS LOCAL */ - -/* For a MEM rtx, the alias set. If 0, this MEM is not in any alias - set, and may alias anything. Otherwise, the MEM can only alias - MEMs in the same alias set. This value is set in a - language-dependent manner in the front-end, and should not be - altered in the back-end. These set numbers are tested for zero, - and compared for equality; they have no other significance. In - some front-ends, these numbers may correspond in some way to types, - or other language-level entities, but they need not, and the - back-end makes no such assumptions. */ -#define MEM_ALIAS_SET(RTX) (XINT (RTX, 1)) - -/* For a LABEL_REF, 1 means that this reference is to a label outside the - loop containing the reference. */ -#define LABEL_OUTSIDE_LOOP_P(RTX) ((RTX)->in_struct) - -/* For a LABEL_REF, 1 means it is for a nonlocal label. */ -/* Likewise in an EXPR_LIST for a REG_LABEL note. */ -#define LABEL_REF_NONLOCAL_P(RTX) ((RTX)->volatil) - -/* For a CODE_LABEL, 1 means always consider this label to be needed. */ -#define LABEL_PRESERVE_P(RTX) ((RTX)->in_struct) - -/* For a REG, 1 means the register is used only in an exit test of a loop. */ -#define REG_LOOP_TEST_P(RTX) ((RTX)->in_struct) - -/* During sched, for an insn, 1 means that the insn must be scheduled together - with the preceding insn. */ -#define SCHED_GROUP_P(INSN) ((INSN)->in_struct) - -/* During sched, for the LOG_LINKS of an insn, these cache the adjusted - cost of the dependence link. The cost of executing an instruction - may vary based on how the results are used. LINK_COST_ZERO is 1 when - the cost through the link varies and is unchanged (i.e., the link has - zero additional cost). LINK_COST_FREE is 1 when the cost through the - link is zero (i.e., the link makes the cost free). In other cases, - the adjustment to the cost is recomputed each time it is needed. */ -#define LINK_COST_ZERO(X) ((X)->jump) -#define LINK_COST_FREE(X) ((X)->call) - -/* For a SET rtx, SET_DEST is the place that is set - and SET_SRC is the value it is set to. */ -#define SET_DEST(RTX) ((RTX)->fld[0].rtx) -#define SET_SRC(RTX) ((RTX)->fld[1].rtx) - -/* For a TRAP_IF rtx, TRAP_CONDITION is an expression. */ -#define TRAP_CONDITION(RTX) ((RTX)->fld[0].rtx) -#define TRAP_CODE(RTX) (RTX)->fld[1].rtx - -/* 1 in a SYMBOL_REF if it addresses this function's constants pool. */ -#define CONSTANT_POOL_ADDRESS_P(RTX) ((RTX)->unchanging) - -/* Flag in a SYMBOL_REF for machine-specific purposes. */ -#define SYMBOL_REF_FLAG(RTX) ((RTX)->volatil) - -/* 1 in a SYMBOL_REF if it represents a symbol which might have to change - if its inlined or unrolled. */ -#define SYMBOL_REF_NEED_ADJUST(RTX) ((RTX)->in_struct) - -/* 1 means a SYMBOL_REF has been the library function in emit_library_call. */ -#define SYMBOL_REF_USED(RTX) ((RTX)->used) - -/* For an INLINE_HEADER rtx, FIRST_FUNCTION_INSN is the first insn - of the function that is not involved in copying parameters to - pseudo-registers. FIRST_PARM_INSN is the very first insn of - the function, including the parameter copying. - We keep this around in case we must splice - this function into the assembly code at the end of the file. - FIRST_LABELNO is the first label number used by the function (inclusive). - LAST_LABELNO is the last label used by the function (exclusive). - MAX_REGNUM is the largest pseudo-register used by that function. - FUNCTION_ARGS_SIZE is the size of the argument block in the stack. - POPS_ARGS is the number of bytes of input arguments popped by the function - STACK_SLOT_LIST is the list of stack slots. - FORCED_LABELS is the list of labels whose address was taken. - FUNCTION_FLAGS are where single-bit flags are saved. - OUTGOING_ARGS_SIZE is the size of the largest outgoing stack parameter list. - ORIGINAL_ARG_VECTOR is a vector of the original DECL_RTX values - for the function arguments. - ORIGINAL_DECL_INITIAL is a pointer to the original DECL_INITIAL for the - function. - INLINE_REGNO_REG_RTX, INLINE_REGNO_POINTER_FLAG, and - INLINE_REGNO_POINTER_ALIGN are pointers to the corresponding arrays. - - We want this to lay down like an INSN. The PREV_INSN field - is always NULL. The NEXT_INSN field always points to the - first function insn of the function being squirreled away. */ - -#define FIRST_FUNCTION_INSN(RTX) ((RTX)->fld[2].rtx) -#define FIRST_PARM_INSN(RTX) ((RTX)->fld[3].rtx) -#define FIRST_LABELNO(RTX) ((RTX)->fld[4].rtint) -#define LAST_LABELNO(RTX) ((RTX)->fld[5].rtint) -#define MAX_PARMREG(RTX) ((RTX)->fld[6].rtint) -#define MAX_REGNUM(RTX) ((RTX)->fld[7].rtint) -#define FUNCTION_ARGS_SIZE(RTX) ((RTX)->fld[8].rtint) -#define POPS_ARGS(RTX) ((RTX)->fld[9].rtint) -#define STACK_SLOT_LIST(RTX) ((RTX)->fld[10].rtx) -#define FORCED_LABELS(RTX) ((RTX)->fld[11].rtx) -#define FUNCTION_FLAGS(RTX) ((RTX)->fld[12].rtint) -#define OUTGOING_ARGS_SIZE(RTX) ((RTX)->fld[13].rtint) -#define ORIGINAL_ARG_VECTOR(RTX) ((RTX)->fld[14].rtvec) -#define ORIGINAL_DECL_INITIAL(RTX) ((RTX)->fld[15].rtx) -#define INLINE_REGNO_REG_RTX(RTX) ((RTX)->fld[16].rtvec) -#define INLINE_REGNO_POINTER_FLAG(RTX) ((RTX)->fld[17].rtstr) -#define INLINE_REGNO_POINTER_ALIGN(RTX) ((RTX)->fld[18].rtstr) -#define PARMREG_STACK_LOC(RTX) ((RTX)->fld[19].rtvec) - -/* In FUNCTION_FLAGS we save some variables computed when emitting the code - for the function and which must be `or'ed into the current flag values when - insns from that function are being inlined. */ - -/* These ought to be an enum, but non-ANSI compilers don't like that. */ -#define FUNCTION_FLAGS_CALLS_ALLOCA 01 -#define FUNCTION_FLAGS_CALLS_SETJMP 02 -#define FUNCTION_FLAGS_RETURNS_STRUCT 04 -#define FUNCTION_FLAGS_RETURNS_PCC_STRUCT 010 -#define FUNCTION_FLAGS_NEEDS_CONTEXT 020 -#define FUNCTION_FLAGS_HAS_NONLOCAL_LABEL 040 -#define FUNCTION_FLAGS_RETURNS_POINTER 0100 -#define FUNCTION_FLAGS_USES_CONST_POOL 0200 -#define FUNCTION_FLAGS_CALLS_LONGJMP 0400 -#define FUNCTION_FLAGS_USES_PIC_OFFSET_TABLE 01000 - -/* Define a macro to look for REG_INC notes, - but save time on machines where they never exist. */ - -/* Don't continue this line--convex cc version 4.1 would lose. */ -#if (defined (HAVE_PRE_INCREMENT) || defined (HAVE_PRE_DECREMENT) || defined (HAVE_POST_INCREMENT) || defined (HAVE_POST_DECREMENT)) -#define FIND_REG_INC_NOTE(insn, reg) (find_reg_note ((insn), REG_INC, (reg))) -#else -#define FIND_REG_INC_NOTE(insn, reg) 0 -#endif - -/* Indicate whether the machine has any sort of auto increment addressing. - If not, we can avoid checking for REG_INC notes. */ - -/* Don't continue this line--convex cc version 4.1 would lose. */ -#if (defined (HAVE_PRE_INCREMENT) || defined (HAVE_PRE_DECREMENT) || defined (HAVE_POST_INCREMENT) || defined (HAVE_POST_DECREMENT)) -#define AUTO_INC_DEC -#endif - -#ifndef HAVE_PRE_INCREMENT -#define HAVE_PRE_INCREMENT 0 -#endif - -#ifndef HAVE_PRE_DECREMENT -#define HAVE_PRE_DECREMENT 0 -#endif - -#ifndef HAVE_POST_INCREMENT -#define HAVE_POST_INCREMENT 0 -#endif - -#ifndef HAVE_POST_DECREMENT -#define HAVE_POST_DECREMENT 0 -#endif - -/* Accessors for RANGE_INFO. */ -/* For RANGE_{START,END} notes return the RANGE_START note. */ -#define RANGE_INFO_NOTE_START(INSN) (XEXP (INSN, 0)) - -/* For RANGE_{START,END} notes return the RANGE_START note. */ -#define RANGE_INFO_NOTE_END(INSN) (XEXP (INSN, 1)) - -/* For RANGE_{START,END} notes, return the vector containing the registers used - in the range. */ -#define RANGE_INFO_REGS(INSN) (XVEC (INSN, 2)) -#define RANGE_INFO_REGS_REG(INSN, N) (XVECEXP (INSN, 2, N)) -#define RANGE_INFO_NUM_REGS(INSN) (XVECLEN (INSN, 2)) - -/* For RANGE_{START,END} notes, the number of calls within the range. */ -#define RANGE_INFO_NCALLS(INSN) (XINT (INSN, 3)) - -/* For RANGE_{START,END} notes, the number of insns within the range. */ -#define RANGE_INFO_NINSNS(INSN) (XINT (INSN, 4)) - -/* For RANGE_{START,END} notes, a unique # to identify this range. */ -#define RANGE_INFO_UNIQUE(INSN) (XINT (INSN, 5)) - -/* For RANGE_{START,END} notes, the basic block # the range starts with. */ -#define RANGE_INFO_BB_START(INSN) (XINT (INSN, 6)) - -/* For RANGE_{START,END} notes, the basic block # the range ends with. */ -#define RANGE_INFO_BB_END(INSN) (XINT (INSN, 7)) - -/* For RANGE_{START,END} notes, the loop depth the range is in. */ -#define RANGE_INFO_LOOP_DEPTH(INSN) (XINT (INSN, 8)) - -/* For RANGE_{START,END} notes, the bitmap of live registers at the start - of the range. */ -#define RANGE_INFO_LIVE_START(INSN) (XBITMAP (INSN, 9)) - -/* For RANGE_{START,END} notes, the bitmap of live registers at the end - of the range. */ -#define RANGE_INFO_LIVE_END(INSN) (XBITMAP (INSN, 10)) - -/* For RANGE_START notes, the marker # of the start of the range. */ -#define RANGE_INFO_MARKER_START(INSN) (XINT (INSN, 11)) - -/* For RANGE_START notes, the marker # of the end of the range. */ -#define RANGE_INFO_MARKER_END(INSN) (XINT (INSN, 12)) - -/* Original pseudo register # for a live range note. */ -#define RANGE_REG_PSEUDO(INSN,N) (XINT (XVECEXP (INSN, 2, N), 0)) - -/* Pseudo register # original register is copied into or -1. */ -#define RANGE_REG_COPY(INSN,N) (XINT (XVECEXP (INSN, 2, N), 1)) - -/* How many times a register in a live range note was referenced. */ -#define RANGE_REG_REFS(INSN,N) (XINT (XVECEXP (INSN, 2, N), 2)) - -/* How many times a register in a live range note was set. */ -#define RANGE_REG_SETS(INSN,N) (XINT (XVECEXP (INSN, 2, N), 3)) - -/* How many times a register in a live range note died. */ -#define RANGE_REG_DEATHS(INSN,N) (XINT (XVECEXP (INSN, 2, N), 4)) - -/* Whether the original value is needed to be copied into the range register at - the start of the range. */ -#define RANGE_REG_COPY_FLAGS(INSN,N) (XINT (XVECEXP (INSN, 2, N), 5)) - -/* # of insns the register copy is live over. */ -#define RANGE_REG_LIVE_LENGTH(INSN,N) (XINT (XVECEXP (INSN, 2, N), 6)) - -/* # of calls the register copy is live over. */ -#define RANGE_REG_N_CALLS(INSN,N) (XINT (XVECEXP (INSN, 2, N), 7)) - -/* DECL_NODE pointer of the declaration if the register is a user defined - variable. */ -#define RANGE_REG_SYMBOL_NODE(INSN,N) (XTREE (XVECEXP (INSN, 2, N), 8)) - -/* BLOCK_NODE pointer to the block the variable is declared in if the - register is a user defined variable. */ -#define RANGE_REG_BLOCK_NODE(INSN,N) (XTREE (XVECEXP (INSN, 2, N), 9)) - -/* EXPR_LIST of the distinct ranges a variable is in. */ -#define RANGE_VAR_LIST(INSN) (XEXP (INSN, 0)) - -/* Block a variable is declared in. */ -#define RANGE_VAR_BLOCK(INSN) (XTREE (INSN, 1)) - -/* # of distinct ranges a variable is in. */ -#define RANGE_VAR_NUM(INSN) (XINT (INSN, 2)) - -/* For a NOTE_INSN_LIVE note, the registers which are currently live. */ -#define RANGE_LIVE_BITMAP(INSN) (XBITMAP (INSN, 0)) - -/* For a NOTE_INSN_LIVE note, the original basic block number. */ -#define RANGE_LIVE_ORIG_BLOCK(INSN) (XINT (INSN, 1)) - -/* Generally useful functions. */ - -/* The following functions accept a wide integer argument. Rather than - having to cast on every function call, we use a macro instead, that is - defined here and in tree.h. */ - -#ifndef exact_log2 -#define exact_log2(N) exact_log2_wide ((unsigned HOST_WIDE_INT) (N)) -#define floor_log2(N) floor_log2_wide ((unsigned HOST_WIDE_INT) (N)) -#endif -extern int exact_log2_wide PROTO((unsigned HOST_WIDE_INT)); -extern int floor_log2_wide PROTO((unsigned HOST_WIDE_INT)); - -/* In expmed.c */ -extern int ceil_log2 PROTO((unsigned HOST_WIDE_INT)); - -#define plus_constant(X,C) plus_constant_wide (X, (HOST_WIDE_INT) (C)) - -#define plus_constant_for_output(X,C) \ - plus_constant_for_output_wide (X, (HOST_WIDE_INT) (C)) - -/* In explow.c */ -extern rtx plus_constant_wide PROTO((rtx, HOST_WIDE_INT)); -extern rtx plus_constant_for_output_wide PROTO((rtx, HOST_WIDE_INT)); -extern void optimize_save_area_alloca PROTO((rtx)); - -extern rtx gen_rtx PVPROTO((enum rtx_code, - enum machine_mode, ...)); -extern rtvec gen_rtvec PVPROTO((int, ...)); - -#ifdef BUFSIZ -extern rtx read_rtx PROTO((FILE *)); -#endif - -extern char *oballoc PROTO((int)); -extern char *permalloc PROTO((int)); -extern rtx rtx_alloc PROTO((RTX_CODE)); -extern rtvec rtvec_alloc PROTO((int)); -extern rtx copy_rtx PROTO((rtx)); -extern rtx really_copy_rtx PROTO((rtx)); -extern rtx copy_rtx_if_shared PROTO((rtx)); -extern rtx copy_most_rtx PROTO((rtx, rtx)); -extern rtvec gen_rtvec_v PROTO((int, rtx *)); -extern rtvec gen_rtvec_vv PROTO((int, rtunion *)); -extern rtx gen_reg_rtx PROTO((enum machine_mode)); -extern rtx gen_label_rtx PROTO((void)); -extern rtx gen_inline_header_rtx PROTO((rtx, rtx, int, int, int, int, - int, int, rtx, rtx, int, int, - rtvec, rtx, - rtvec, char *, char *, rtvec)); -extern rtx gen_lowpart_common PROTO((enum machine_mode, rtx)); -extern rtx gen_lowpart PROTO((enum machine_mode, rtx)); -extern rtx gen_lowpart_if_possible PROTO((enum machine_mode, rtx)); -extern rtx gen_highpart PROTO((enum machine_mode, rtx)); -extern rtx gen_realpart PROTO((enum machine_mode, rtx)); -extern rtx gen_imagpart PROTO((enum machine_mode, rtx)); -extern rtx operand_subword PROTO((rtx, int, int, enum machine_mode)); -extern rtx operand_subword_force PROTO((rtx, int, enum machine_mode)); -extern int subreg_lowpart_p PROTO((rtx)); -extern rtx make_safe_from PROTO((rtx, rtx)); -extern rtx convert_memory_address PROTO((enum machine_mode, rtx)); -extern rtx memory_address PROTO((enum machine_mode, rtx)); -extern rtx get_insns PROTO((void)); -extern rtx get_last_insn PROTO((void)); -extern rtx get_last_insn_anywhere PROTO((void)); -extern void start_sequence PROTO((void)); -extern void push_to_sequence PROTO((rtx)); -extern void end_sequence PROTO((void)); -extern rtx gen_sequence PROTO((void)); -extern rtx immed_double_const PROTO((HOST_WIDE_INT, HOST_WIDE_INT, enum machine_mode)); -extern rtx force_const_mem PROTO((enum machine_mode, rtx)); -extern rtx force_reg PROTO((enum machine_mode, rtx)); -extern rtx get_pool_constant PROTO((rtx)); -extern enum machine_mode get_pool_mode PROTO((rtx)); -extern int get_pool_offset PROTO((rtx)); -extern rtx simplify_subtraction PROTO((rtx)); -extern rtx assign_stack_local PROTO((enum machine_mode, - HOST_WIDE_INT, int)); -extern rtx assign_stack_temp PROTO((enum machine_mode, - HOST_WIDE_INT, int)); -extern rtx assign_temp PROTO((union tree_node *, - int, int, int)); -extern rtx protect_from_queue PROTO((rtx, int)); -extern void emit_queue PROTO((void)); -extern rtx emit_move_insn PROTO((rtx, rtx)); -extern rtx emit_insn_before PROTO((rtx, rtx)); -extern rtx emit_jump_insn_before PROTO((rtx, rtx)); -extern rtx emit_call_insn_before PROTO((rtx, rtx)); -extern rtx emit_barrier_before PROTO((rtx)); -extern rtx emit_note_before PROTO((int, rtx)); -extern rtx emit_insn_after PROTO((rtx, rtx)); -extern rtx emit_jump_insn_after PROTO((rtx, rtx)); -extern rtx emit_barrier_after PROTO((rtx)); -extern rtx emit_label_after PROTO((rtx, rtx)); -extern rtx emit_note_after PROTO((int, rtx)); -extern rtx emit_line_note_after PROTO((char *, int, rtx)); -extern rtx emit_insn PROTO((rtx)); -extern rtx emit_insns PROTO((rtx)); -extern rtx emit_insns_before PROTO((rtx, rtx)); -extern rtx emit_insns_after PROTO((rtx, rtx)); -extern rtx emit_jump_insn PROTO((rtx)); -extern rtx emit_call_insn PROTO((rtx)); -extern rtx emit_label PROTO((rtx)); -extern rtx emit_barrier PROTO((void)); -extern rtx emit_line_note PROTO((char *, int)); -extern rtx emit_note PROTO((char *, int)); -extern rtx emit_line_note_force PROTO((char *, int)); -extern rtx make_insn_raw PROTO((rtx)); -extern rtx previous_insn PROTO((rtx)); -extern rtx next_insn PROTO((rtx)); -extern rtx prev_nonnote_insn PROTO((rtx)); -extern rtx next_nonnote_insn PROTO((rtx)); -extern rtx prev_real_insn PROTO((rtx)); -extern rtx next_real_insn PROTO((rtx)); -extern rtx prev_active_insn PROTO((rtx)); -extern rtx next_active_insn PROTO((rtx)); -extern rtx prev_label PROTO((rtx)); -extern rtx next_label PROTO((rtx)); -extern rtx next_cc0_user PROTO((rtx)); -extern rtx prev_cc0_setter PROTO((rtx)); -extern rtx next_nondeleted_insn PROTO((rtx)); -extern enum rtx_code reverse_condition PROTO((enum rtx_code)); -extern enum rtx_code swap_condition PROTO((enum rtx_code)); -extern enum rtx_code unsigned_condition PROTO((enum rtx_code)); -extern enum rtx_code signed_condition PROTO((enum rtx_code)); -extern rtx find_equiv_reg PROTO((rtx, rtx, enum reg_class, int, short *, int, enum machine_mode)); -extern rtx squeeze_notes PROTO((rtx, rtx)); -extern rtx delete_insn PROTO((rtx)); -extern void delete_jump PROTO((rtx)); -extern rtx get_label_before PROTO((rtx)); -extern rtx get_label_after PROTO((rtx)); -extern rtx follow_jumps PROTO((rtx)); -extern rtx adj_offsettable_operand PROTO((rtx, int)); -extern rtx try_split PROTO((rtx, rtx, int)); -extern rtx split_insns PROTO((rtx, rtx)); -extern rtx simplify_unary_operation PROTO((enum rtx_code, enum machine_mode, rtx, enum machine_mode)); -extern rtx simplify_binary_operation PROTO((enum rtx_code, enum machine_mode, rtx, rtx)); -extern rtx simplify_ternary_operation PROTO((enum rtx_code, enum machine_mode, enum machine_mode, rtx, rtx, rtx)); -extern rtx simplify_relational_operation PROTO((enum rtx_code, enum machine_mode, rtx, rtx)); -extern rtx nonlocal_label_rtx_list PROTO((void)); -extern rtx gen_move_insn PROTO((rtx, rtx)); -extern rtx gen_jump PROTO((rtx)); -extern rtx gen_beq PROTO((rtx)); -extern rtx gen_bge PROTO((rtx)); -extern rtx gen_ble PROTO((rtx)); -extern rtx gen_mem_addressof PROTO((rtx, union tree_node *)); -extern rtx eliminate_constant_term PROTO((rtx, rtx *)); -extern rtx expand_complex_abs PROTO((enum machine_mode, rtx, rtx, int)); -extern enum machine_mode choose_hard_reg_mode PROTO((int, int)); - -/* Functions in rtlanal.c */ - -extern int rtx_unstable_p PROTO((rtx)); -extern int rtx_varies_p PROTO((rtx)); -extern int rtx_addr_varies_p PROTO((rtx)); -extern HOST_WIDE_INT get_integer_term PROTO((rtx)); -extern rtx get_related_value PROTO((rtx)); -extern int reg_mentioned_p PROTO((rtx, rtx)); -extern int reg_referenced_p PROTO((rtx, rtx)); -extern int reg_used_between_p PROTO((rtx, rtx, rtx)); -extern int reg_referenced_between_p PROTO((rtx, rtx, rtx)); -extern int reg_set_between_p PROTO((rtx, rtx, rtx)); -extern int regs_set_between_p PROTO((rtx, rtx, rtx)); -extern int modified_between_p PROTO((rtx, rtx, rtx)); -extern int no_labels_between_p PROTO((rtx, rtx)); -extern int no_jumps_between_p PROTO((rtx, rtx)); -extern int modified_in_p PROTO((rtx, rtx)); -extern int reg_set_p PROTO((rtx, rtx)); -extern rtx single_set PROTO((rtx)); -extern int multiple_sets PROTO((rtx)); -extern rtx find_last_value PROTO((rtx, rtx *, rtx)); -extern int refers_to_regno_p PROTO((int, int, rtx, rtx *)); -extern int reg_overlap_mentioned_p PROTO((rtx, rtx)); -extern void note_stores PROTO((rtx, void (*)())); -extern rtx reg_set_last PROTO((rtx, rtx)); -extern int rtx_equal_p PROTO((rtx, rtx)); -extern int dead_or_set_p PROTO((rtx, rtx)); -extern int dead_or_set_regno_p PROTO((rtx, int)); -extern rtx find_reg_note PROTO((rtx, enum reg_note, rtx)); -extern rtx find_regno_note PROTO((rtx, enum reg_note, int)); -extern int find_reg_fusage PROTO((rtx, enum rtx_code, rtx)); -extern int find_regno_fusage PROTO((rtx, enum rtx_code, int)); -extern void remove_note PROTO((rtx, rtx)); -extern int side_effects_p PROTO((rtx)); -extern int volatile_refs_p PROTO((rtx)); -extern int volatile_insn_p PROTO((rtx)); -extern int may_trap_p PROTO((rtx)); -extern int inequality_comparisons_p PROTO ((rtx)); -extern rtx replace_rtx PROTO((rtx, rtx, rtx)); -extern rtx replace_regs PROTO((rtx, rtx *, int, int)); -extern int computed_jump_p PROTO((rtx)); -typedef int (*rtx_function) PROTO((rtx *, void *)); -extern int for_each_rtx PROTO((rtx *, rtx_function, void *)); -extern int insn_first_p PROTO((rtx, rtx)); -extern rtx regno_use_in PROTO((int, rtx)); - -/* flow.c */ - -extern rtx find_use_as_address PROTO((rtx, rtx, HOST_WIDE_INT)); - -/* regclass.c */ - -/* Maximum number of parallel sets and clobbers in any insn in this fn. - Always at least 3, since the combiner could put that many togetherm - and we want this to remain correct for all the remaining passes. */ - -extern int max_parallel; - -/* Free up register info memory. */ -extern void free_reg_info PROTO((void)); - -/* recog.c */ -extern int asm_noperands PROTO((rtx)); -extern char *decode_asm_operands PROTO((rtx, rtx *, rtx **, char **, enum machine_mode *)); - -extern enum reg_class reg_preferred_class PROTO((int)); -extern enum reg_class reg_alternate_class PROTO((int)); - -extern rtx get_first_nonparm_insn PROTO((void)); - -extern void split_block_insns PROTO((int, int)); -extern void update_flow_info PROTO((rtx, rtx, rtx, rtx)); - -/* Standard pieces of rtx, to be substituted directly into things. */ -#define pc_rtx (&global_rtl.pc_val) -#define cc0_rtx (&global_rtl.cc0_val) - -#define MAX_SAVED_CONST_INT 64 -extern struct rtx_def const_int_rtx[MAX_SAVED_CONST_INT * 2 + 1]; - -#define const0_rtx (&const_int_rtx[MAX_SAVED_CONST_INT]) -#define const1_rtx (&const_int_rtx[MAX_SAVED_CONST_INT+1]) -#define const2_rtx (&const_int_rtx[MAX_SAVED_CONST_INT+2]) -#define constm1_rtx (&const_int_rtx[MAX_SAVED_CONST_INT-1]) -extern rtx const_true_rtx; - -extern rtx const_tiny_rtx[3][(int) MAX_MACHINE_MODE]; - -/* Returns a constant 0 rtx in mode MODE. Integer modes are treated the - same as VOIDmode. */ - -#define CONST0_RTX(MODE) (const_tiny_rtx[0][(int) (MODE)]) - -/* Likewise, for the constants 1 and 2. */ - -#define CONST1_RTX(MODE) (const_tiny_rtx[1][(int) (MODE)]) -#define CONST2_RTX(MODE) (const_tiny_rtx[2][(int) (MODE)]) - -extern struct _global_rtl -{ - struct rtx_def pc_val, cc0_val; - struct rtx_def stack_pointer_val, frame_pointer_val; - struct rtx_def hard_frame_pointer_val; - struct rtx_def arg_pointer_val; - struct rtx_def virtual_incoming_args_val; - struct rtx_def virtual_stack_vars_val; - struct rtx_def virtual_stack_dynamic_val; - struct rtx_def virtual_outgoing_args_val; - struct rtx_def virtual_cfa_val; -} global_rtl; - -/* All references to certain hard regs, except those created - by allocating pseudo regs into them (when that's possible), - go through these unique rtx objects. */ -#define stack_pointer_rtx (&global_rtl.stack_pointer_val) -#define frame_pointer_rtx (&global_rtl.frame_pointer_val) - -extern rtx pic_offset_table_rtx; -extern rtx struct_value_rtx; -extern rtx struct_value_incoming_rtx; -extern rtx static_chain_rtx; -extern rtx static_chain_incoming_rtx; -extern rtx return_address_pointer_rtx; - -/* Include the RTL generation functions. */ - -#ifndef NO_GENRTL_H -#include "genrtl.h" -#endif - -/* There are some RTL codes that require special attention; the - generation functions included above do the raw handling. If you - add to this list, modify special_rtx in gengenrtl.c as well. You - should also modify gen_rtx to use the special function. */ - -extern rtx gen_rtx_CONST_INT PROTO((enum machine_mode, HOST_WIDE_INT)); -extern rtx gen_rtx_REG PROTO((enum machine_mode, int)); -extern rtx gen_rtx_MEM PROTO((enum machine_mode, rtx)); - -/* We need the cast here to ensure that we get the same result both with - and without prototypes. */ -#define GEN_INT(N) gen_rtx_CONST_INT (VOIDmode, (HOST_WIDE_INT) (N)) - - -/* If HARD_FRAME_POINTER_REGNUM is defined, then a special dummy reg - is used to represent the frame pointer. This is because the - hard frame pointer and the automatic variables are separated by an amount - that cannot be determined until after register allocation. We can assume - that in this case ELIMINABLE_REGS will be defined, one action of which - will be to eliminate FRAME_POINTER_REGNUM into HARD_FRAME_POINTER_REGNUM. */ -#ifndef HARD_FRAME_POINTER_REGNUM -#define HARD_FRAME_POINTER_REGNUM FRAME_POINTER_REGNUM -#endif - -/* For register elimination to work properly these hard_frame_pointer_rtx, - frame_pointer_rtx, and arg_pointer_rtx must be the same if they refer to - the same register. */ -#if HARD_FRAME_POINTER_REGNUM == FRAME_POINTER_REGNUM -#define hard_frame_pointer_rtx (&global_rtl.frame_pointer_val) -#else -#define hard_frame_pointer_rtx (&global_rtl.hard_frame_pointer_val) -#endif - -#if FRAME_POINTER_REGNUM == ARG_POINTER_REGNUM -#define arg_pointer_rtx (&global_rtl.frame_pointer_val) -#else -#if HARD_FRAME_POINTER_REGNUM == ARG_POINTER_REGNUM -#define arg_pointer_rtx (&global_rtl.hard_frame_pointer_val) -#else -#define arg_pointer_rtx (&global_rtl.arg_pointer_val) -#endif -#endif - -/* Virtual registers are used during RTL generation to refer to locations into - the stack frame when the actual location isn't known until RTL generation - is complete. The routine instantiate_virtual_regs replaces these with - the proper value, which is normally {frame,arg,stack}_pointer_rtx plus - a constant. */ - -#define FIRST_VIRTUAL_REGISTER (FIRST_PSEUDO_REGISTER) - -/* This points to the first word of the incoming arguments passed on the stack, - either by the caller or by the callee when pretending it was passed by the - caller. */ - -#define virtual_incoming_args_rtx (&global_rtl.virtual_incoming_args_val) - -#define VIRTUAL_INCOMING_ARGS_REGNUM (FIRST_VIRTUAL_REGISTER) - -/* If FRAME_GROWS_DOWNWARD, this points to immediately above the first - variable on the stack. Otherwise, it points to the first variable on - the stack. */ - -#define virtual_stack_vars_rtx (&global_rtl.virtual_stack_vars_val) - -#define VIRTUAL_STACK_VARS_REGNUM ((FIRST_VIRTUAL_REGISTER) + 1) - -/* This points to the location of dynamically-allocated memory on the stack - immediately after the stack pointer has been adjusted by the amount - desired. */ - -#define virtual_stack_dynamic_rtx (&global_rtl.virtual_stack_dynamic_val) - -#define VIRTUAL_STACK_DYNAMIC_REGNUM ((FIRST_VIRTUAL_REGISTER) + 2) - -/* This points to the location in the stack at which outgoing arguments should - be written when the stack is pre-pushed (arguments pushed using push - insns always use sp). */ - -#define virtual_outgoing_args_rtx (&global_rtl.virtual_outgoing_args_val) - -#define VIRTUAL_OUTGOING_ARGS_REGNUM ((FIRST_VIRTUAL_REGISTER) + 3) - -/* This points to the Canonical Frame Address of the function. This - should corrospond to the CFA produced by INCOMING_FRAME_SP_OFFSET, - but is calculated relative to the arg pointer for simplicity; the - frame pointer nor stack pointer are necessarily fixed relative to - the CFA until after reload. */ - -#define virtual_cfa_rtx (&global_rtl.virtual_cfa_val) - -#define VIRTUAL_CFA_REGNUM ((FIRST_VIRTUAL_REGISTER) + 4) - -#define LAST_VIRTUAL_REGISTER ((FIRST_VIRTUAL_REGISTER) + 4) - -extern rtx find_next_ref PROTO((rtx, rtx)); -extern rtx *find_single_use PROTO((rtx, rtx, rtx *)); - -extern rtx output_constant_def PROTO((union tree_node *)); -extern rtx immed_real_const PROTO((union tree_node *)); -extern union tree_node *make_tree PROTO((union tree_node *, rtx)); - -/* Define a default value for STORE_FLAG_VALUE. */ - -#ifndef STORE_FLAG_VALUE -#define STORE_FLAG_VALUE 1 -#endif - -/* Nonzero after the second flow pass has completed. - Set to 1 or 0 by toplev.c */ -extern int flow2_completed; - -/* Nonzero after end of reload pass. - Set to 1 or 0 by reload1.c. */ - -extern int reload_completed; - -/* Set to 1 while reload_as_needed is operating. - Required by some machines to handle any generated moves differently. */ - -extern int reload_in_progress; - -/* If this is nonzero, we do not bother generating VOLATILE - around volatile memory references, and we are willing to - output indirect addresses. If cse is to follow, we reject - indirect addresses so a useful potential cse is generated; - if it is used only once, instruction combination will produce - the same indirect address eventually. */ -extern int cse_not_expected; - -/* Set to nonzero before life analysis to indicate that it is unsafe to - generate any new pseudo registers. */ -extern int no_new_pseudos; - -/* Indexed by pseudo register number, gives the rtx for that pseudo. - Allocated in parallel with regno_pointer_flag. */ -extern rtx *regno_reg_rtx; - -/* Vector indexed by regno; contain the alignment in bytes and type - pointed to for a register that contains a pointer, if known. */ -extern char *regno_pointer_align; -#define REGNO_POINTER_ALIGN(REGNO) regno_pointer_align[REGNO] - -/* Translates rtx code to tree code, for those codes needed by - REAL_ARITHMETIC. The function returns an int because the caller may not - know what `enum tree_code' means. */ - -extern int rtx_to_tree_code PROTO((enum rtx_code)); - -/* In tree.c */ -extern void obfree PROTO ((char *)); -struct obstack; -extern void gcc_obstack_init PROTO ((struct obstack *)); -extern void pop_obstacks PROTO ((void)); -extern void push_obstacks PROTO ((struct obstack *, - struct obstack *)); -/* CYGNUS LOCAL SH4-OPT */ -/* Save the current set of obstacks, but don't change them. */ -extern void push_obstacks_nochange PROTO((void)); -extern void end_temporary_allocation PROTO((void)); -/* END CYGNUS LOCAL */ -#ifdef BUFSIZ -extern int read_skip_spaces PROTO ((FILE *)); -#endif - -/* In cse.c */ -struct cse_basic_block_data; -extern int rtx_cost PROTO ((rtx, enum rtx_code)); -extern void delete_trivially_dead_insns PROTO ((rtx, int)); -#ifdef BUFSIZ -extern int cse_main PROTO ((rtx, int, int, FILE *)); -#endif -extern void cse_end_of_basic_block PROTO ((rtx, - struct cse_basic_block_data *, - int, int, int)); - -/* In jump.c */ -extern int comparison_dominates_p PROTO ((enum rtx_code, enum rtx_code)); -extern int condjump_p PROTO ((rtx)); -extern rtx condjump_label PROTO ((rtx)); -extern int simplejump_p PROTO ((rtx)); -extern int sets_cc0_p PROTO ((rtx)); -extern int invert_jump PROTO ((rtx, rtx)); -extern int rtx_renumbered_equal_p PROTO ((rtx, rtx)); -extern int true_regnum PROTO ((rtx)); -extern int redirect_jump PROTO ((rtx, rtx)); -extern void jump_optimize PROTO ((rtx, int, int, int)); -extern void thread_jumps PROTO ((rtx, int, int)); -extern int redirect_exp PROTO ((rtx *, rtx, rtx, rtx)); -extern int rtx_equal_for_thread_p PROTO ((rtx, rtx, rtx)); -extern int invert_exp PROTO ((rtx, rtx)); -extern int can_reverse_comparison_p PROTO ((rtx, rtx)); -extern void delete_for_peephole PROTO ((rtx, rtx)); -extern int condjump_in_parallel_p PROTO ((rtx)); - -/* Flags for jump_optimize() */ -#define JUMP_CROSS_JUMP 1 -#define JUMP_NOOP_MOVES 1 -#define JUMP_AFTER_REGSCAN 1 - -/* In emit-rtl.c. */ -extern int max_reg_num PROTO ((void)); -extern int max_label_num PROTO ((void)); -extern int get_first_label_num PROTO ((void)); -extern void delete_insns_since PROTO ((rtx)); -extern void mark_reg_pointer PROTO ((rtx, int)); -extern void mark_user_reg PROTO ((rtx)); -extern void reset_used_flags PROTO ((rtx)); -extern void reorder_insns PROTO ((rtx, rtx, rtx)); -extern int get_max_uid PROTO ((void)); -extern int in_sequence_p PROTO ((void)); -extern void force_next_line_note PROTO ((void)); -extern void init_emit PROTO ((void)); -extern void init_emit_once PROTO ((int)); -extern void push_topmost_sequence PROTO ((void)); -extern void pop_topmost_sequence PROTO ((void)); -extern int subreg_realpart_p PROTO ((rtx)); -extern void reverse_comparison PROTO ((rtx)); -extern void set_new_first_and_last_insn PROTO ((rtx, rtx)); -extern void set_new_first_and_last_label_num PROTO ((int, int)); -extern void unshare_all_rtl PROTO ((rtx)); -extern void set_last_insn PROTO ((rtx)); -extern void link_cc0_insns PROTO ((rtx)); -extern void add_insn PROTO ((rtx)); -extern void add_insn_before PROTO ((rtx, rtx)); -extern void add_insn_after PROTO ((rtx, rtx)); -extern void remove_insn PROTO ((rtx)); -extern void reorder_insns_with_line_notes PROTO ((rtx, rtx, rtx)); -extern void emit_insn_after_with_line_notes PROTO ((rtx, rtx, rtx)); -extern enum rtx_code classify_insn PROTO ((rtx)); -extern void init_virtual_regs PROTO ((void)); -extern rtx emit PROTO ((rtx)); -/* Query and clear/ restore no_line_numbers. This is used by the - switch / case handling in stmt.c to give proper line numbers in - warnings about unreachable code. */ -int force_line_numbers PROTO((void)); -void restore_line_number_status PROTO((int old_value)); - -/* In insn-emit.c */ -extern void add_clobbers PROTO ((rtx, int)); - -/* In combine.c */ -extern void combine_instructions PROTO ((rtx, int)); -extern int extended_count PROTO ((rtx, enum machine_mode, int)); -extern rtx remove_death PROTO ((int, rtx)); -#ifdef BUFSIZ -extern void dump_combine_stats PROTO ((FILE *)); -extern void dump_combine_total_stats PROTO ((FILE *)); -#endif - -/* In sched.c. */ -#ifdef BUFSIZ -extern void schedule_insns PROTO ((FILE *)); -#endif -#ifdef HAIFA -extern void fix_sched_param PROTO ((char *, char *)); -#endif - -/* In print-rtl.c */ -extern void debug_rtx PROTO ((rtx)); -extern void debug_rtx_list PROTO ((rtx, int)); -extern rtx debug_rtx_find PROTO ((rtx, int)); -#ifdef BUFSIZ -extern void print_rtl PROTO ((FILE *, rtx)); -extern int print_rtl_single PROTO ((FILE *, rtx)); -extern void print_inline_rtx PROTO ((FILE *, rtx, int)); -#endif - -/* In loop.c */ -extern void init_loop PROTO ((void)); -#ifdef BUFSIZ -extern void loop_optimize PROTO ((rtx, FILE *, int, int)); -#endif -extern void record_excess_regs PROTO ((rtx, rtx, rtx *)); - -/* In function.c */ -extern void reposition_prologue_and_epilogue_notes PROTO ((rtx)); -extern void thread_prologue_and_epilogue_insns PROTO ((rtx)); -extern void use_variable PROTO ((rtx)); -extern HOST_WIDE_INT get_frame_size PROTO ((void)); -extern void preserve_rtl_expr_result PROTO ((rtx)); -extern void mark_temp_addr_taken PROTO ((rtx)); -extern void update_temp_slot_address PROTO ((rtx, rtx)); -extern void use_variable_after PROTO ((rtx, rtx)); -extern void purge_addressof PROTO ((rtx)); - -/* In reload.c */ -extern int operands_match_p PROTO ((rtx, rtx)); -extern int safe_from_earlyclobber PROTO ((rtx, rtx)); - -/* In stmt.c */ -extern void expand_null_return PROTO((void)); -extern void emit_jump PROTO ((rtx)); -extern int preserve_subexpressions_p PROTO ((void)); - -/* In expr.c */ -extern void init_expr_once PROTO ((void)); -extern void move_by_pieces PROTO ((rtx, rtx, int, int)); - - -/* In stupid.c */ -#ifdef BUFSIZ -extern void stupid_life_analysis PROTO ((rtx, int, FILE *)); -#endif - -/* In flow.c */ -extern void allocate_for_life_analysis PROTO ((void)); -extern void recompute_reg_usage PROTO ((rtx, int)); -#ifdef BUFSIZ -extern void dump_flow_info PROTO ((FILE *)); -#endif -extern void free_bb_memory PROTO ((void)); - -/* In expmed.c */ -extern void init_expmed PROTO ((void)); -extern void expand_inc PROTO ((rtx, rtx)); -extern void expand_dec PROTO ((rtx, rtx)); -extern rtx expand_mult_highpart PROTO ((enum machine_mode, rtx, - unsigned HOST_WIDE_INT, rtx, - int, int)); - -/* In gcse.c */ -#ifdef BUFSIZ -/* CYGNUS LOCAL gcse/law */ -extern int gcse_main PROTO ((rtx, FILE *)); -/* END CYGNUS LOCAL */ -#endif - -/* In global.c */ -extern void mark_elimination PROTO ((int, int)); -#ifdef BUFSIZ -extern int global_alloc PROTO ((FILE *)); -extern void dump_global_regs PROTO ((FILE *)); -#endif -#ifdef HARD_CONST -extern void retry_global_alloc PROTO ((int, HARD_REG_SET)); -#endif - -/* In regclass.c */ -extern int reg_classes_intersect_p PROTO ((enum reg_class, enum reg_class)); -extern int reg_class_subset_p PROTO ((enum reg_class, enum reg_class)); -extern void globalize_reg PROTO ((int)); -extern void init_regs PROTO ((void)); -extern void init_reg_sets PROTO ((void)); -extern void regset_release_memory PROTO ((void)); -extern void regclass_init PROTO ((void)); -extern void regclass PROTO ((rtx, int)); -extern void reg_scan PROTO ((rtx, int, int)); -extern void reg_scan_update PROTO ((rtx, rtx, int)); -extern void fix_register PROTO ((char *, int, int)); - -/* In regmove.c */ -#ifdef BUFSIZ -extern void regmove_optimize PROTO ((rtx, int, FILE *)); -#endif - -/* In reorg.c */ -#ifdef BUFSIZ -extern void dbr_schedule PROTO ((rtx, FILE *)); -#endif - -/* In optabs.c */ -extern void init_optabs PROTO ((void)); - -/* In local-alloc.c */ -#ifdef BUFSIZ -extern void dump_local_alloc PROTO ((FILE *)); -#endif -extern void local_alloc PROTO ((void)); -extern int function_invariant_p PROTO ((rtx)); - -/* In reload1.c */ -extern void reload_cse_regs PROTO ((rtx)); -extern void init_reload PROTO ((void)); -extern void mark_home_live PROTO ((int)); -#ifdef BUFSIZ -extern int reload PROTO ((rtx, int, FILE *)); -#endif - -/* In caller-save.c */ -extern void init_caller_save PROTO ((void)); - -/* In reg-stack.c */ -#ifdef BUFSIZ -extern void reg_to_stack PROTO ((rtx, FILE *)); -#endif -extern int stack_regs_mentioned_p PROTO ((rtx)); - -/* In fold-const.c */ -extern int add_double PROTO ((HOST_WIDE_INT, HOST_WIDE_INT, - HOST_WIDE_INT, HOST_WIDE_INT, - HOST_WIDE_INT *, HOST_WIDE_INT *)); -extern int neg_double PROTO ((HOST_WIDE_INT, HOST_WIDE_INT, - HOST_WIDE_INT *, HOST_WIDE_INT *)); -extern int mul_double PROTO ((HOST_WIDE_INT, HOST_WIDE_INT, - HOST_WIDE_INT, HOST_WIDE_INT, - HOST_WIDE_INT *, HOST_WIDE_INT *)); -extern void lshift_double PROTO ((HOST_WIDE_INT, HOST_WIDE_INT, - HOST_WIDE_INT, int, HOST_WIDE_INT *, - HOST_WIDE_INT *, int)); -extern void rshift_double PROTO ((HOST_WIDE_INT, HOST_WIDE_INT, - HOST_WIDE_INT, int, - HOST_WIDE_INT *, HOST_WIDE_INT *, int)); -extern void lrotate_double PROTO ((HOST_WIDE_INT, HOST_WIDE_INT, - HOST_WIDE_INT, int, HOST_WIDE_INT *, - HOST_WIDE_INT *)); -extern void rrotate_double PROTO ((HOST_WIDE_INT, HOST_WIDE_INT, - HOST_WIDE_INT, int, HOST_WIDE_INT *, - HOST_WIDE_INT *)); - -/* In calls.c */ -/* Emit library call. */ -extern void emit_library_call PVPROTO ((rtx, int, enum machine_mode, - int, ...)); -extern rtx emit_library_call_value PVPROTO((rtx, rtx, int, - enum machine_mode, - int, ...)); - -/* In unroll.c */ -extern int set_dominates_use PROTO ((int, int, int, rtx, rtx)); - -/* In varasm.c */ -extern void bss_section PROTO ((void)); -extern int in_data_section PROTO ((void)); -extern int supports_one_only PROTO ((void)); - -/* In rtl.c */ -extern void init_rtl PROTO ((void)); -extern void rtx_free PROTO ((rtx)); - -/* In alias.c */ -extern int true_dependence PROTO ((rtx, enum machine_mode, rtx, - int (*)(rtx))); -extern int read_dependence PROTO ((rtx, rtx)); -extern int anti_dependence PROTO ((rtx, rtx)); -extern int output_dependence PROTO ((rtx, rtx)); -extern void init_alias_once PROTO ((void)); -extern void init_alias_analysis PROTO ((void)); -extern void end_alias_analysis PROTO ((void)); - -extern void record_base_value PROTO ((int, rtx, int)); -extern void record_alias_subset PROTO ((int, int)); -extern rtx addr_side_effect_eval PROTO ((rtx, int, int)); - -#endif /* _RTL_H */ diff --git a/gcc/unroll_991002.c b/gcc/unroll_991002.c deleted file mode 100755 index 431c0dc..0000000 --- a/gcc/unroll_991002.c +++ /dev/null @@ -1,4045 +0,0 @@ -/* Try to unroll loops, and split induction variables. - Copyright (C) 1992, 93, 94, 95, 97, 98, 1999 Free Software Foundation, Inc. - Contributed by James E. Wilson, Cygnus Support/UC Berkeley. - -This file is part of GNU CC. - -GNU CC 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 2, or (at your option) -any later version. - -GNU CC 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 GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ - -/* Try to unroll a loop, and split induction variables. - - Loops for which the number of iterations can be calculated exactly are - handled specially. If the number of iterations times the insn_count is - less than MAX_UNROLLED_INSNS, then the loop is unrolled completely. - Otherwise, we try to unroll the loop a number of times modulo the number - of iterations, so that only one exit test will be needed. It is unrolled - a number of times approximately equal to MAX_UNROLLED_INSNS divided by - the insn count. - - Otherwise, if the number of iterations can be calculated exactly at - run time, and the loop is always entered at the top, then we try to - precondition the loop. That is, at run time, calculate how many times - the loop will execute, and then execute the loop body a few times so - that the remaining iterations will be some multiple of 4 (or 2 if the - loop is large). Then fall through to a loop unrolled 4 (or 2) times, - with only one exit test needed at the end of the loop. - - Otherwise, if the number of iterations can not be calculated exactly, - not even at run time, then we still unroll the loop a number of times - approximately equal to MAX_UNROLLED_INSNS divided by the insn count, - but there must be an exit test after each copy of the loop body. - - For each induction variable, which is dead outside the loop (replaceable) - or for which we can easily calculate the final value, if we can easily - calculate its value at each place where it is set as a function of the - current loop unroll count and the variable's value at loop entry, then - the induction variable is split into `N' different variables, one for - each copy of the loop body. One variable is live across the backward - branch, and the others are all calculated as a function of this variable. - This helps eliminate data dependencies, and leads to further opportunities - for cse. */ - -/* Possible improvements follow: */ - -/* ??? Add an extra pass somewhere to determine whether unrolling will - give any benefit. E.g. after generating all unrolled insns, compute the - cost of all insns and compare against cost of insns in rolled loop. - - - On traditional architectures, unrolling a non-constant bound loop - is a win if there is a giv whose only use is in memory addresses, the - memory addresses can be split, and hence giv increments can be - eliminated. - - It is also a win if the loop is executed many times, and preconditioning - can be performed for the loop. - Add code to check for these and similar cases. */ - -/* ??? Improve control of which loops get unrolled. Could use profiling - info to only unroll the most commonly executed loops. Perhaps have - a user specifyable option to control the amount of code expansion, - or the percent of loops to consider for unrolling. Etc. */ - -/* ??? Look at the register copies inside the loop to see if they form a - simple permutation. If so, iterate the permutation until it gets back to - the start state. This is how many times we should unroll the loop, for - best results, because then all register copies can be eliminated. - For example, the lisp nreverse function should be unrolled 3 times - while (this) - { - next = this->cdr; - this->cdr = prev; - prev = this; - this = next; - } - - ??? The number of times to unroll the loop may also be based on data - references in the loop. For example, if we have a loop that references - x[i-1], x[i], and x[i+1], we should unroll it a multiple of 3 times. */ - -/* ??? Add some simple linear equation solving capability so that we can - determine the number of loop iterations for more complex loops. - For example, consider this loop from gdb - #define SWAP_TARGET_AND_HOST(buffer,len) - { - char tmp; - char *p = (char *) buffer; - char *q = ((char *) buffer) + len - 1; - int iterations = (len + 1) >> 1; - int i; - for (p; p < q; p++, q--;) - { - tmp = *q; - *q = *p; - *p = tmp; - } - } - Note that: - start value = p = &buffer + current_iteration - end value = q = &buffer + len - 1 - current_iteration - Given the loop exit test of "p < q", then there must be "q - p" iterations, - set equal to zero and solve for number of iterations: - q - p = len - 1 - 2*current_iteration = 0 - current_iteration = (len - 1) / 2 - Hence, there are (len - 1) / 2 (rounded up to the nearest integer) - iterations of this loop. */ - -/* ??? Currently, no labels are marked as loop invariant when doing loop - unrolling. This is because an insn inside the loop, that loads the address - of a label inside the loop into a register, could be moved outside the loop - by the invariant code motion pass if labels were invariant. If the loop - is subsequently unrolled, the code will be wrong because each unrolled - body of the loop will use the same address, whereas each actually needs a - different address. A case where this happens is when a loop containing - a switch statement is unrolled. - - It would be better to let labels be considered invariant. When we - unroll loops here, check to see if any insns using a label local to the - loop were moved before the loop. If so, then correct the problem, by - moving the insn back into the loop, or perhaps replicate the insn before - the loop, one copy for each time the loop is unrolled. */ - -/* The prime factors looked for when trying to unroll a loop by some - number which is modulo the total number of iterations. Just checking - for these 4 prime factors will find at least one factor for 75% of - all numbers theoretically. Practically speaking, this will succeed - almost all of the time since loops are generally a multiple of 2 - and/or 5. */ - -#define NUM_FACTORS 4 - -struct _factor { int factor, count; } factors[NUM_FACTORS] - = { {2, 0}, {3, 0}, {5, 0}, {7, 0}}; - -/* Describes the different types of loop unrolling performed. */ - -enum unroll_types { UNROLL_COMPLETELY, UNROLL_MODULO, UNROLL_NAIVE }; - -#include "config.h" -#include "system.h" -#include "rtl.h" -#include "insn-config.h" -#include "integrate.h" -#include "regs.h" -#include "recog.h" -#include "flags.h" -#include "expr.h" -#include "loop.h" -#include "toplev.h" - -/* This controls which loops are unrolled, and by how much we unroll - them. */ - -#ifndef MAX_UNROLLED_INSNS -#define MAX_UNROLLED_INSNS 100 -#endif - -/* Indexed by register number, if non-zero, then it contains a pointer - to a struct induction for a DEST_REG giv which has been combined with - one of more address givs. This is needed because whenever such a DEST_REG - giv is modified, we must modify the value of all split address givs - that were combined with this DEST_REG giv. */ - -static struct induction **addr_combined_regs; - -/* Indexed by register number, if this is a splittable induction variable, - then this will hold the current value of the register, which depends on the - iteration number. */ - -static rtx *splittable_regs; - -/* Indexed by register number, if this is a splittable induction variable, - this indicates if it was made from a derived giv. */ -static char *derived_regs; - -/* Indexed by register number, if this is a splittable induction variable, - then this will hold the number of instructions in the loop that modify - the induction variable. Used to ensure that only the last insn modifying - a split iv will update the original iv of the dest. */ - -static int *splittable_regs_updates; - -/* Forward declarations. */ - -static void init_reg_map PROTO((struct inline_remap *, int)); -static rtx calculate_giv_inc PROTO((rtx, rtx, int)); -static rtx initial_reg_note_copy PROTO((rtx, struct inline_remap *)); -static void final_reg_note_copy PROTO((rtx, struct inline_remap *)); -static void copy_loop_body PROTO((rtx, rtx, struct inline_remap *, rtx, int, - enum unroll_types, rtx, rtx, rtx, rtx)); -static void iteration_info PROTO((rtx, rtx *, rtx *, rtx, rtx)); -static int find_splittable_regs PROTO((enum unroll_types, rtx, rtx, rtx, int, - unsigned HOST_WIDE_INT)); -static int find_splittable_givs PROTO((struct iv_class *, enum unroll_types, - rtx, rtx, rtx, int)); -static int reg_dead_after_loop PROTO((rtx, rtx, rtx)); -static rtx fold_rtx_mult_add PROTO((rtx, rtx, rtx, enum machine_mode)); -static int verify_addresses PROTO((struct induction *, rtx, int)); -static rtx remap_split_bivs PROTO((rtx)); - -/* Try to unroll one loop and split induction variables in the loop. - - The loop is described by the arguments LOOP_END, INSN_COUNT, and - LOOP_START. END_INSERT_BEFORE indicates where insns should be added - which need to be executed when the loop falls through. STRENGTH_REDUCTION_P - indicates whether information generated in the strength reduction pass - is available. - - This function is intended to be called from within `strength_reduce' - in loop.c. */ - -void -unroll_loop (loop_end, insn_count, loop_start, end_insert_before, - loop_info, strength_reduce_p) - rtx loop_end; - int insn_count; - rtx loop_start; - rtx end_insert_before; - struct loop_info *loop_info; - int strength_reduce_p; -{ - int i, j, temp; - int unroll_number = 1; - rtx copy_start, copy_end; - rtx insn, sequence, pattern, tem; - int max_labelno, max_insnno; - rtx insert_before; - struct inline_remap *map; - char *local_label; - char *local_regno; - int maxregnum; - int new_maxregnum; - rtx exit_label = 0; - rtx start_label; - struct iv_class *bl; - int splitting_not_safe = 0; - enum unroll_types unroll_type; - int loop_preconditioned = 0; - rtx safety_label; - /* This points to the last real insn in the loop, which should be either - a JUMP_INSN (for conditional jumps) or a BARRIER (for unconditional - jumps). */ - rtx last_loop_insn; - - /* Don't bother unrolling huge loops. Since the minimum factor is - two, loops greater than one half of MAX_UNROLLED_INSNS will never - be unrolled. */ - if (insn_count > MAX_UNROLLED_INSNS / 2) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, "Unrolling failure: Loop too big.\n"); - return; - } - - /* When emitting debugger info, we can't unroll loops with unequal numbers - of block_beg and block_end notes, because that would unbalance the block - structure of the function. This can happen as a result of the - "if (foo) bar; else break;" optimization in jump.c. */ - /* ??? Gcc has a general policy that -g is never supposed to change the code - that the compiler emits, so we must disable this optimization always, - even if debug info is not being output. This is rare, so this should - not be a significant performance problem. */ - - if (1 /* write_symbols != NO_DEBUG */) - { - int block_begins = 0; - int block_ends = 0; - - for (insn = loop_start; insn != loop_end; insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) == NOTE) - { - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG) - block_begins++; - else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END) - block_ends++; - } - } - - if (block_begins != block_ends) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Unrolling failure: Unbalanced block notes.\n"); - return; - } - } - - /* Determine type of unroll to perform. Depends on the number of iterations - and the size of the loop. */ - - /* If there is no strength reduce info, then set - loop_info->n_iterations to zero. This can happen if - strength_reduce can't find any bivs in the loop. A value of zero - indicates that the number of iterations could not be calculated. */ - - if (! strength_reduce_p) - loop_info->n_iterations = 0; - - if (loop_dump_stream && loop_info->n_iterations > 0) - { - fputs ("Loop unrolling: ", loop_dump_stream); - fprintf (loop_dump_stream, HOST_WIDE_INT_PRINT_DEC, - loop_info->n_iterations); - fputs (" iterations.\n", loop_dump_stream); - } - - /* Find and save a pointer to the last nonnote insn in the loop. */ - - last_loop_insn = prev_nonnote_insn (loop_end); - - /* Calculate how many times to unroll the loop. Indicate whether or - not the loop is being completely unrolled. */ - - if (loop_info->n_iterations == 1) - { - /* If number of iterations is exactly 1, then eliminate the compare and - branch at the end of the loop since they will never be taken. - Then return, since no other action is needed here. */ - - /* If the last instruction is not a BARRIER or a JUMP_INSN, then - don't do anything. */ - - if (GET_CODE (last_loop_insn) == BARRIER) - { - /* Delete the jump insn. This will delete the barrier also. */ - delete_insn (PREV_INSN (last_loop_insn)); - } - else if (GET_CODE (last_loop_insn) == JUMP_INSN) - { -#ifdef HAVE_cc0 - /* The immediately preceding insn is a compare which must be - deleted. */ - delete_insn (last_loop_insn); - delete_insn (PREV_INSN (last_loop_insn)); -#else - /* The immediately preceding insn may not be the compare, so don't - delete it. */ - delete_insn (last_loop_insn); -#endif - } - return; - } - else if (loop_info->n_iterations > 0 - && loop_info->n_iterations * insn_count < MAX_UNROLLED_INSNS) - { - unroll_number = loop_info->n_iterations; - unroll_type = UNROLL_COMPLETELY; - } - else if (loop_info->n_iterations > 0) - { - /* Try to factor the number of iterations. Don't bother with the - general case, only using 2, 3, 5, and 7 will get 75% of all - numbers theoretically, and almost all in practice. */ - - for (i = 0; i < NUM_FACTORS; i++) - factors[i].count = 0; - - temp = loop_info->n_iterations; - for (i = NUM_FACTORS - 1; i >= 0; i--) - while (temp % factors[i].factor == 0) - { - factors[i].count++; - temp = temp / factors[i].factor; - } - - /* Start with the larger factors first so that we generally - get lots of unrolling. */ - - unroll_number = 1; - temp = insn_count; - for (i = 3; i >= 0; i--) - while (factors[i].count--) - { - if (temp * factors[i].factor < MAX_UNROLLED_INSNS) - { - unroll_number *= factors[i].factor; - temp *= factors[i].factor; - } - else - break; - } - - /* If we couldn't find any factors, then unroll as in the normal - case. */ - if (unroll_number == 1) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Loop unrolling: No factors found.\n"); - } - else - unroll_type = UNROLL_MODULO; - } - - - /* Default case, calculate number of times to unroll loop based on its - size. */ - if (unroll_number == 1) - { - if (8 * insn_count < MAX_UNROLLED_INSNS) - unroll_number = 8; - else if (4 * insn_count < MAX_UNROLLED_INSNS) - unroll_number = 4; - else - unroll_number = 2; - - unroll_type = UNROLL_NAIVE; - } - - /* Now we know how many times to unroll the loop. */ - - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Unrolling loop %d times.\n", unroll_number); - - - if (unroll_type == UNROLL_COMPLETELY || unroll_type == UNROLL_MODULO) - { - /* Loops of these types can start with jump down to the exit condition - in rare circumstances. - - Consider a pair of nested loops where the inner loop is part - of the exit code for the outer loop. - - In this case jump.c will not duplicate the exit test for the outer - loop, so it will start with a jump to the exit code. - - Then consider if the inner loop turns out to iterate once and - only once. We will end up deleting the jumps associated with - the inner loop. However, the loop notes are not removed from - the instruction stream. - - And finally assume that we can compute the number of iterations - for the outer loop. - - In this case unroll may want to unroll the outer loop even though - it starts with a jump to the outer loop's exit code. - - We could try to optimize this case, but it hardly seems worth it. - Just return without unrolling the loop in such cases. */ - - insn = loop_start; - while (GET_CODE (insn) != CODE_LABEL && GET_CODE (insn) != JUMP_INSN) - insn = NEXT_INSN (insn); - if (GET_CODE (insn) == JUMP_INSN) - return; - } - - if (unroll_type == UNROLL_COMPLETELY) - { - /* Completely unrolling the loop: Delete the compare and branch at - the end (the last two instructions). This delete must done at the - very end of loop unrolling, to avoid problems with calls to - back_branch_in_range_p, which is called by find_splittable_regs. - All increments of splittable bivs/givs are changed to load constant - instructions. */ - - copy_start = loop_start; - - /* Set insert_before to the instruction immediately after the JUMP_INSN - (or BARRIER), so that any NOTEs between the JUMP_INSN and the end of - the loop will be correctly handled by copy_loop_body. */ - insert_before = NEXT_INSN (last_loop_insn); - - /* Set copy_end to the insn before the jump at the end of the loop. */ - if (GET_CODE (last_loop_insn) == BARRIER) - copy_end = PREV_INSN (PREV_INSN (last_loop_insn)); - else if (GET_CODE (last_loop_insn) == JUMP_INSN) - { -#ifdef HAVE_cc0 - /* The instruction immediately before the JUMP_INSN is a compare - instruction which we do not want to copy. */ - copy_end = PREV_INSN (PREV_INSN (last_loop_insn)); -#else - /* The instruction immediately before the JUMP_INSN may not be the - compare, so we must copy it. */ - copy_end = PREV_INSN (last_loop_insn); -#endif - } - else - { - /* We currently can't unroll a loop if it doesn't end with a - JUMP_INSN. There would need to be a mechanism that recognizes - this case, and then inserts a jump after each loop body, which - jumps to after the last loop body. */ - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Unrolling failure: loop does not end with a JUMP_INSN.\n"); - return; - } - } - else if (unroll_type == UNROLL_MODULO) - { - /* Partially unrolling the loop: The compare and branch at the end - (the last two instructions) must remain. Don't copy the compare - and branch instructions at the end of the loop. Insert the unrolled - code immediately before the compare/branch at the end so that the - code will fall through to them as before. */ - - copy_start = loop_start; - - /* Set insert_before to the jump insn at the end of the loop. - Set copy_end to before the jump insn at the end of the loop. */ - if (GET_CODE (last_loop_insn) == BARRIER) - { - insert_before = PREV_INSN (last_loop_insn); - copy_end = PREV_INSN (insert_before); - } - else if (GET_CODE (last_loop_insn) == JUMP_INSN) - { -#ifdef HAVE_cc0 - /* The instruction immediately before the JUMP_INSN is a compare - instruction which we do not want to copy or delete. */ - insert_before = PREV_INSN (last_loop_insn); - copy_end = PREV_INSN (insert_before); -#else - /* The instruction immediately before the JUMP_INSN may not be the - compare, so we must copy it. */ - insert_before = last_loop_insn; - copy_end = PREV_INSN (last_loop_insn); -#endif - } - else - { - /* We currently can't unroll a loop if it doesn't end with a - JUMP_INSN. There would need to be a mechanism that recognizes - this case, and then inserts a jump after each loop body, which - jumps to after the last loop body. */ - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Unrolling failure: loop does not end with a JUMP_INSN.\n"); - return; - } - } - else - { - /* Normal case: Must copy the compare and branch instructions at the - end of the loop. */ - - if (GET_CODE (last_loop_insn) == BARRIER) - { - /* Loop ends with an unconditional jump and a barrier. - Handle this like above, don't copy jump and barrier. - This is not strictly necessary, but doing so prevents generating - unconditional jumps to an immediately following label. - - This will be corrected below if the target of this jump is - not the start_label. */ - - insert_before = PREV_INSN (last_loop_insn); - copy_end = PREV_INSN (insert_before); - } - else if (GET_CODE (last_loop_insn) == JUMP_INSN) - { - /* Set insert_before to immediately after the JUMP_INSN, so that - NOTEs at the end of the loop will be correctly handled by - copy_loop_body. */ - insert_before = NEXT_INSN (last_loop_insn); - copy_end = last_loop_insn; - } - else - { - /* We currently can't unroll a loop if it doesn't end with a - JUMP_INSN. There would need to be a mechanism that recognizes - this case, and then inserts a jump after each loop body, which - jumps to after the last loop body. */ - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Unrolling failure: loop does not end with a JUMP_INSN.\n"); - return; - } - - /* If copying exit test branches because they can not be eliminated, - then must convert the fall through case of the branch to a jump past - the end of the loop. Create a label to emit after the loop and save - it for later use. Do not use the label after the loop, if any, since - it might be used by insns outside the loop, or there might be insns - added before it later by final_[bg]iv_value which must be after - the real exit label. */ - exit_label = gen_label_rtx (); - - insn = loop_start; - while (GET_CODE (insn) != CODE_LABEL && GET_CODE (insn) != JUMP_INSN) - insn = NEXT_INSN (insn); - - if (GET_CODE (insn) == JUMP_INSN) - { - /* The loop starts with a jump down to the exit condition test. - Start copying the loop after the barrier following this - jump insn. */ - copy_start = NEXT_INSN (insn); - - /* Splitting induction variables doesn't work when the loop is - entered via a jump to the bottom, because then we end up doing - a comparison against a new register for a split variable, but - we did not execute the set insn for the new register because - it was skipped over. */ - splitting_not_safe = 1; - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Splitting not safe, because loop not entered at top.\n"); - } - else - copy_start = loop_start; - } - - /* This should always be the first label in the loop. */ - start_label = NEXT_INSN (copy_start); - /* There may be a line number note and/or a loop continue note here. */ - while (GET_CODE (start_label) == NOTE) - start_label = NEXT_INSN (start_label); - if (GET_CODE (start_label) != CODE_LABEL) - { - /* This can happen as a result of jump threading. If the first insns in - the loop test the same condition as the loop's backward jump, or the - opposite condition, then the backward jump will be modified to point - to elsewhere, and the loop's start label is deleted. - - This case currently can not be handled by the loop unrolling code. */ - - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Unrolling failure: unknown insns between BEG note and loop label.\n"); - return; - } - if (LABEL_NAME (start_label)) - { - /* The jump optimization pass must have combined the original start label - with a named label for a goto. We can't unroll this case because - jumps which go to the named label must be handled differently than - jumps to the loop start, and it is impossible to differentiate them - in this case. */ - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Unrolling failure: loop start label is gone\n"); - return; - } - - if (unroll_type == UNROLL_NAIVE - && GET_CODE (last_loop_insn) == BARRIER - && start_label != JUMP_LABEL (PREV_INSN (last_loop_insn))) - { - /* In this case, we must copy the jump and barrier, because they will - not be converted to jumps to an immediately following label. */ - - insert_before = NEXT_INSN (last_loop_insn); - copy_end = last_loop_insn; - } - - if (unroll_type == UNROLL_NAIVE - && GET_CODE (last_loop_insn) == JUMP_INSN - && start_label != JUMP_LABEL (last_loop_insn)) - { - /* ??? The loop ends with a conditional branch that does not branch back - to the loop start label. In this case, we must emit an unconditional - branch to the loop exit after emitting the final branch. - copy_loop_body does not have support for this currently, so we - give up. It doesn't seem worthwhile to unroll anyways since - unrolling would increase the number of branch instructions - executed. */ - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Unrolling failure: final conditional branch not to loop start\n"); - return; - } - - /* Allocate a translation table for the labels and insn numbers. - They will be filled in as we copy the insns in the loop. */ - - max_labelno = max_label_num (); - max_insnno = get_max_uid (); - - map = (struct inline_remap *) alloca (sizeof (struct inline_remap)); - - map->integrating = 0; - - /* Allocate the label map. */ - - if (max_labelno > 0) - { - map->label_map = (rtx *) alloca (max_labelno * sizeof (rtx)); - - local_label = (char *) alloca (max_labelno); - zero_memory (local_label, max_labelno); - } - else - map->label_map = 0; - - /* Search the loop and mark all local labels, i.e. the ones which have to - be distinct labels when copied. For all labels which might be - non-local, set their label_map entries to point to themselves. - If they happen to be local their label_map entries will be overwritten - before the loop body is copied. The label_map entries for local labels - will be set to a different value each time the loop body is copied. */ - - for (insn = copy_start; insn != loop_end; insn = NEXT_INSN (insn)) - { - rtx note; - - if (GET_CODE (insn) == CODE_LABEL) - local_label[CODE_LABEL_NUMBER (insn)] = 1; - else if (GET_CODE (insn) == JUMP_INSN) - { - if (JUMP_LABEL (insn)) - set_label_in_map (map, - CODE_LABEL_NUMBER (JUMP_LABEL (insn)), - JUMP_LABEL (insn)); - else if (GET_CODE (PATTERN (insn)) == ADDR_VEC - || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC) - { - rtx pat = PATTERN (insn); - int diff_vec_p = GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC; - int len = XVECLEN (pat, diff_vec_p); - rtx label; - - for (i = 0; i < len; i++) - { - label = XEXP (XVECEXP (pat, diff_vec_p, i), 0); - set_label_in_map (map, - CODE_LABEL_NUMBER (label), - label); - } - } - } - else if ((note = find_reg_note (insn, REG_LABEL, NULL_RTX))) - set_label_in_map (map, CODE_LABEL_NUMBER (XEXP (note, 0)), - XEXP (note, 0)); - } - - /* Allocate space for the insn map. */ - - map->insn_map = (rtx *) alloca (max_insnno * sizeof (rtx)); - - /* Set this to zero, to indicate that we are doing loop unrolling, - not function inlining. */ - map->inline_target = 0; - - /* The register and constant maps depend on the number of registers - present, so the final maps can't be created until after - find_splittable_regs is called. However, they are needed for - preconditioning, so we create temporary maps when preconditioning - is performed. */ - - /* The preconditioning code may allocate two new pseudo registers. */ - maxregnum = max_reg_num (); - - /* Allocate and zero out the splittable_regs and addr_combined_regs - arrays. These must be zeroed here because they will be used if - loop preconditioning is performed, and must be zero for that case. - - It is safe to do this here, since the extra registers created by the - preconditioning code and find_splittable_regs will never be used - to access the splittable_regs[] and addr_combined_regs[] arrays. */ - - splittable_regs = (rtx *) alloca (maxregnum * sizeof (rtx)); - zero_memory ((char *) splittable_regs, maxregnum * sizeof (rtx)); - derived_regs = alloca (maxregnum); - zero_memory (derived_regs, maxregnum); - splittable_regs_updates = (int *) alloca (maxregnum * sizeof (int)); - zero_memory ((char *) splittable_regs_updates, maxregnum * sizeof (int)); - addr_combined_regs - = (struct induction **) alloca (maxregnum * sizeof (struct induction *)); - zero_memory ((char *) addr_combined_regs, maxregnum * sizeof (struct induction *)); - local_regno = (char *) alloca (maxregnum); - zero_memory (local_regno, maxregnum); - - /* Mark all local registers, i.e. the ones which are referenced only - inside the loop. */ - if (INSN_UID (copy_end) < max_uid_for_loop) - { - int copy_start_luid = INSN_LUID (copy_start); - int copy_end_luid = INSN_LUID (copy_end); - - /* If a register is used in the jump insn, we must not duplicate it - since it will also be used outside the loop. */ - if (GET_CODE (copy_end) == JUMP_INSN) - copy_end_luid--; - /* If copy_start points to the NOTE that starts the loop, then we must - use the next luid, because invariant pseudo-regs moved out of the loop - have their lifetimes modified to start here, but they are not safe - to duplicate. */ - if (copy_start == loop_start) - copy_start_luid++; - - /* If a pseudo's lifetime is entirely contained within this loop, then we - can use a different pseudo in each unrolled copy of the loop. This - results in better code. */ - /* We must limit the generic test to max_reg_before_loop, because only - these pseudo registers have valid regno_first_uid info. */ - for (j = FIRST_PSEUDO_REGISTER; j < max_reg_before_loop; ++j) - if (REGNO_FIRST_UID (j) > 0 && REGNO_FIRST_UID (j) <= max_uid_for_loop - && uid_luid[REGNO_FIRST_UID (j)] >= copy_start_luid - && REGNO_LAST_UID (j) > 0 && REGNO_LAST_UID (j) <= max_uid_for_loop - && uid_luid[REGNO_LAST_UID (j)] <= copy_end_luid) - { - /* However, we must also check for loop-carried dependencies. - If the value the pseudo has at the end of iteration X is - used by iteration X+1, then we can not use a different pseudo - for each unrolled copy of the loop. */ - /* A pseudo is safe if regno_first_uid is a set, and this - set dominates all instructions from regno_first_uid to - regno_last_uid. */ - /* ??? This check is simplistic. We would get better code if - this check was more sophisticated. */ - if (set_dominates_use (j, REGNO_FIRST_UID (j), REGNO_LAST_UID (j), - copy_start, copy_end)) - local_regno[j] = 1; - - if (loop_dump_stream) - { - if (local_regno[j]) - fprintf (loop_dump_stream, "Marked reg %d as local\n", j); - else - fprintf (loop_dump_stream, "Did not mark reg %d as local\n", - j); - } - } - /* Givs that have been created from multiple biv increments always have - local registers. */ - for (j = first_increment_giv; j <= last_increment_giv; j++) - { - local_regno[j] = 1; - if (loop_dump_stream) - fprintf (loop_dump_stream, "Marked reg %d as local\n", j); - } - } - - /* If this loop requires exit tests when unrolled, check to see if we - can precondition the loop so as to make the exit tests unnecessary. - Just like variable splitting, this is not safe if the loop is entered - via a jump to the bottom. Also, can not do this if no strength - reduce info, because precondition_loop_p uses this info. */ - - /* Must copy the loop body for preconditioning before the following - find_splittable_regs call since that will emit insns which need to - be after the preconditioned loop copies, but immediately before the - unrolled loop copies. */ - - /* Also, it is not safe to split induction variables for the preconditioned - copies of the loop body. If we split induction variables, then the code - assumes that each induction variable can be represented as a function - of its initial value and the loop iteration number. This is not true - in this case, because the last preconditioned copy of the loop body - could be any iteration from the first up to the `unroll_number-1'th, - depending on the initial value of the iteration variable. Therefore - we can not split induction variables here, because we can not calculate - their value. Hence, this code must occur before find_splittable_regs - is called. */ - - if (unroll_type == UNROLL_NAIVE && ! splitting_not_safe && strength_reduce_p) - { - rtx initial_value, final_value, increment; - enum machine_mode mode; - - if (precondition_loop_p (loop_start, loop_info, - &initial_value, &final_value, &increment, - &mode)) - { - register rtx diff ; - rtx *labels; - int abs_inc, neg_inc; - - map->reg_map = (rtx *) alloca (maxregnum * sizeof (rtx)); - - map->const_equiv_map = (rtx *) alloca (maxregnum * sizeof (rtx)); - map->const_age_map = (unsigned *) alloca (maxregnum - * sizeof (unsigned)); - map->const_equiv_map_size = maxregnum; - global_const_equiv_map = map->const_equiv_map; - global_const_equiv_map_size = maxregnum; - - init_reg_map (map, maxregnum); - - /* Limit loop unrolling to 4, since this will make 7 copies of - the loop body. */ - if (unroll_number > 4) - unroll_number = 4; - - /* Save the absolute value of the increment, and also whether or - not it is negative. */ - neg_inc = 0; - abs_inc = INTVAL (increment); - if (abs_inc < 0) - { - abs_inc = - abs_inc; - neg_inc = 1; - } - - start_sequence (); - - /* Calculate the difference between the final and initial values. - Final value may be a (plus (reg x) (const_int 1)) rtx. - Let the following cse pass simplify this if initial value is - a constant. - - We must copy the final and initial values here to avoid - improperly shared rtl. */ - - diff = expand_binop (mode, sub_optab, copy_rtx (final_value), - copy_rtx (initial_value), NULL_RTX, 0, - OPTAB_LIB_WIDEN); - - /* Now calculate (diff % (unroll * abs (increment))) by using an - and instruction. */ - diff = expand_binop (GET_MODE (diff), and_optab, diff, - GEN_INT (unroll_number * abs_inc - 1), - NULL_RTX, 0, OPTAB_LIB_WIDEN); - - /* Now emit a sequence of branches to jump to the proper precond - loop entry point. */ - - labels = (rtx *) alloca (sizeof (rtx) * unroll_number); - for (i = 0; i < unroll_number; i++) - labels[i] = gen_label_rtx (); - - /* Check for the case where the initial value is greater than or - equal to the final value. In that case, we want to execute - exactly one loop iteration. The code below will fail for this - case. This check does not apply if the loop has a NE - comparison at the end. */ - - if (loop_info->comparison_code != NE) - { - emit_cmp_and_jump_insns (initial_value, final_value, - neg_inc ? LE : GE, - NULL_RTX, mode, 0, 0, labels[1]); - JUMP_LABEL (get_last_insn ()) = labels[1]; - LABEL_NUSES (labels[1])++; - } - - /* Assuming the unroll_number is 4, and the increment is 2, then - for a negative increment: for a positive increment: - diff = 0,1 precond 0 diff = 0,7 precond 0 - diff = 2,3 precond 3 diff = 1,2 precond 1 - diff = 4,5 precond 2 diff = 3,4 precond 2 - diff = 6,7 precond 1 diff = 5,6 precond 3 */ - - /* We only need to emit (unroll_number - 1) branches here, the - last case just falls through to the following code. */ - - /* ??? This would give better code if we emitted a tree of branches - instead of the current linear list of branches. */ - - for (i = 0; i < unroll_number - 1; i++) - { - int cmp_const; - enum rtx_code cmp_code; - - /* For negative increments, must invert the constant compared - against, except when comparing against zero. */ - if (i == 0) - { - cmp_const = 0; - cmp_code = EQ; - } - else if (neg_inc) - { - cmp_const = unroll_number - i; - cmp_code = GE; - } - else - { - cmp_const = i; - cmp_code = LE; - } - - emit_cmp_and_jump_insns (diff, GEN_INT (abs_inc * cmp_const), - cmp_code, NULL_RTX, mode, 0, 0, - labels[i]); - JUMP_LABEL (get_last_insn ()) = labels[i]; - LABEL_NUSES (labels[i])++; - } - - /* If the increment is greater than one, then we need another branch, - to handle other cases equivalent to 0. */ - - /* ??? This should be merged into the code above somehow to help - simplify the code here, and reduce the number of branches emitted. - For the negative increment case, the branch here could easily - be merged with the `0' case branch above. For the positive - increment case, it is not clear how this can be simplified. */ - - if (abs_inc != 1) - { - int cmp_const; - enum rtx_code cmp_code; - - if (neg_inc) - { - cmp_const = abs_inc - 1; - cmp_code = LE; - } - else - { - cmp_const = abs_inc * (unroll_number - 1) + 1; - cmp_code = GE; - } - - emit_cmp_and_jump_insns (diff, GEN_INT (cmp_const), cmp_code, - NULL_RTX, mode, 0, 0, labels[0]); - JUMP_LABEL (get_last_insn ()) = labels[0]; - LABEL_NUSES (labels[0])++; - } - - sequence = gen_sequence (); - end_sequence (); - emit_insn_before (sequence, loop_start); - - /* Only the last copy of the loop body here needs the exit - test, so set copy_end to exclude the compare/branch here, - and then reset it inside the loop when get to the last - copy. */ - - if (GET_CODE (last_loop_insn) == BARRIER) - copy_end = PREV_INSN (PREV_INSN (last_loop_insn)); - else if (GET_CODE (last_loop_insn) == JUMP_INSN) - { -#ifdef HAVE_cc0 - /* The immediately preceding insn is a compare which we do not - want to copy. */ - copy_end = PREV_INSN (PREV_INSN (last_loop_insn)); -#else - /* The immediately preceding insn may not be a compare, so we - must copy it. */ - copy_end = PREV_INSN (last_loop_insn); -#endif - } - else - abort (); - - for (i = 1; i < unroll_number; i++) - { - emit_label_after (labels[unroll_number - i], - PREV_INSN (loop_start)); - - zero_memory ((char *) map->insn_map, max_insnno * sizeof (rtx)); - zero_memory ((char *) map->const_equiv_map, maxregnum * sizeof (rtx)); - zero_memory ((char *) map->const_age_map, - maxregnum * sizeof (unsigned)); - map->const_age = 0; - - for (j = 0; j < max_labelno; j++) - if (local_label[j]) - set_label_in_map (map, j, gen_label_rtx ()); - - for (j = FIRST_PSEUDO_REGISTER; j < maxregnum; j++) - if (local_regno[j]) - { - map->reg_map[j] = gen_reg_rtx (GET_MODE (regno_reg_rtx[j])); - record_base_value (REGNO (map->reg_map[j]), - regno_reg_rtx[j], 0); - } - /* The last copy needs the compare/branch insns at the end, - so reset copy_end here if the loop ends with a conditional - branch. */ - - if (i == unroll_number - 1) - { - if (GET_CODE (last_loop_insn) == BARRIER) - copy_end = PREV_INSN (PREV_INSN (last_loop_insn)); - else - copy_end = last_loop_insn; - } - - /* None of the copies are the `last_iteration', so just - pass zero for that parameter. */ - copy_loop_body (copy_start, copy_end, map, exit_label, 0, - unroll_type, start_label, loop_end, - loop_start, copy_end); - } - emit_label_after (labels[0], PREV_INSN (loop_start)); - - if (GET_CODE (last_loop_insn) == BARRIER) - { - insert_before = PREV_INSN (last_loop_insn); - copy_end = PREV_INSN (insert_before); - } - else - { -#ifdef HAVE_cc0 - /* The immediately preceding insn is a compare which we do not - want to copy. */ - insert_before = PREV_INSN (last_loop_insn); - copy_end = PREV_INSN (insert_before); -#else - /* The immediately preceding insn may not be a compare, so we - must copy it. */ - insert_before = last_loop_insn; - copy_end = PREV_INSN (last_loop_insn); -#endif - } - - /* Set unroll type to MODULO now. */ - unroll_type = UNROLL_MODULO; - loop_preconditioned = 1; - } - } - - /* If reach here, and the loop type is UNROLL_NAIVE, then don't unroll - the loop unless all loops are being unrolled. */ - if (unroll_type == UNROLL_NAIVE && ! flag_unroll_all_loops) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, "Unrolling failure: Naive unrolling not being done.\n"); - return; - } - - /* At this point, we are guaranteed to unroll the loop. */ - - /* Keep track of the unroll factor for the loop. */ - if (unroll_type == UNROLL_COMPLETELY) - loop_info->unroll_number = -1; - else - loop_info->unroll_number = unroll_number; - - - /* For each biv and giv, determine whether it can be safely split into - a different variable for each unrolled copy of the loop body. - We precalculate and save this info here, since computing it is - expensive. - - Do this before deleting any instructions from the loop, so that - back_branch_in_range_p will work correctly. */ - - if (splitting_not_safe) - temp = 0; - else - temp = find_splittable_regs (unroll_type, loop_start, loop_end, - end_insert_before, unroll_number, - loop_info->n_iterations); - - /* find_splittable_regs may have created some new registers, so must - reallocate the reg_map with the new larger size, and must realloc - the constant maps also. */ - - maxregnum = max_reg_num (); - map->reg_map = (rtx *) alloca (maxregnum * sizeof (rtx)); - - init_reg_map (map, maxregnum); - - /* Space is needed in some of the map for new registers, so new_maxregnum - is an (over)estimate of how many registers will exist at the end. */ - new_maxregnum = maxregnum + (temp * unroll_number * 2); - - /* Must realloc space for the constant maps, because the number of registers - may have changed. */ - - map->const_equiv_map = (rtx *) alloca (new_maxregnum * sizeof (rtx)); - map->const_age_map = (unsigned *) alloca (new_maxregnum * sizeof (unsigned)); - - map->const_equiv_map_size = new_maxregnum; - global_const_equiv_map = map->const_equiv_map; - global_const_equiv_map_size = new_maxregnum; - - /* Search the list of bivs and givs to find ones which need to be remapped - when split, and set their reg_map entry appropriately. */ - - for (bl = loop_iv_list; bl; bl = bl->next) - { - if (REGNO (bl->biv->src_reg) != bl->regno) - map->reg_map[bl->regno] = bl->biv->src_reg; -#if 0 - /* Currently, non-reduced/final-value givs are never split. */ - for (v = bl->giv; v; v = v->next_iv) - if (REGNO (v->src_reg) != bl->regno) - map->reg_map[REGNO (v->dest_reg)] = v->src_reg; -#endif - } - - /* Use our current register alignment and pointer flags. */ - map->regno_pointer_flag = regno_pointer_flag; - map->regno_pointer_align = regno_pointer_align; - - /* If the loop is being partially unrolled, and the iteration variables - are being split, and are being renamed for the split, then must fix up - the compare/jump instruction at the end of the loop to refer to the new - registers. This compare isn't copied, so the registers used in it - will never be replaced if it isn't done here. */ - - if (unroll_type == UNROLL_MODULO) - { - insn = NEXT_INSN (copy_end); - if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN) - PATTERN (insn) = remap_split_bivs (PATTERN (insn)); - } - - /* For unroll_number times, make a copy of each instruction - between copy_start and copy_end, and insert these new instructions - before the end of the loop. */ - - for (i = 0; i < unroll_number; i++) - { - zero_memory ((char *) map->insn_map, max_insnno * sizeof (rtx)); - zero_memory ((char *) map->const_equiv_map, new_maxregnum * sizeof (rtx)); - zero_memory ((char *) map->const_age_map, new_maxregnum * sizeof (unsigned)); - map->const_age = 0; - - for (j = 0; j < max_labelno; j++) - if (local_label[j]) - set_label_in_map (map, j, gen_label_rtx ()); - - for (j = FIRST_PSEUDO_REGISTER; j < maxregnum; j++) - if (local_regno[j]) - { - map->reg_map[j] = gen_reg_rtx (GET_MODE (regno_reg_rtx[j])); - record_base_value (REGNO (map->reg_map[j]), - regno_reg_rtx[j], 0); - } - - /* If loop starts with a branch to the test, then fix it so that - it points to the test of the first unrolled copy of the loop. */ - if (i == 0 && loop_start != copy_start) - { - insn = PREV_INSN (copy_start); - pattern = PATTERN (insn); - - tem = get_label_from_map (map, - CODE_LABEL_NUMBER - (XEXP (SET_SRC (pattern), 0))); - SET_SRC (pattern) = gen_rtx_LABEL_REF (VOIDmode, tem); - - /* Set the jump label so that it can be used by later loop unrolling - passes. */ - JUMP_LABEL (insn) = tem; - LABEL_NUSES (tem)++; - } - - copy_loop_body (copy_start, copy_end, map, exit_label, - i == unroll_number - 1, unroll_type, start_label, - loop_end, insert_before, insert_before); - } - - /* Before deleting any insns, emit a CODE_LABEL immediately after the last - insn to be deleted. This prevents any runaway delete_insn call from - more insns that it should, as it always stops at a CODE_LABEL. */ - - /* Delete the compare and branch at the end of the loop if completely - unrolling the loop. Deleting the backward branch at the end also - deletes the code label at the start of the loop. This is done at - the very end to avoid problems with back_branch_in_range_p. */ - - if (unroll_type == UNROLL_COMPLETELY) - safety_label = emit_label_after (gen_label_rtx (), last_loop_insn); - else - safety_label = emit_label_after (gen_label_rtx (), copy_end); - - /* Delete all of the original loop instructions. Don't delete the - LOOP_BEG note, or the first code label in the loop. */ - - insn = NEXT_INSN (copy_start); - while (insn != safety_label) - { - if (insn != start_label) - insn = delete_insn (insn); - else - insn = NEXT_INSN (insn); - } - - /* Can now delete the 'safety' label emitted to protect us from runaway - delete_insn calls. */ - if (INSN_DELETED_P (safety_label)) - abort (); - delete_insn (safety_label); - - /* If exit_label exists, emit it after the loop. Doing the emit here - forces it to have a higher INSN_UID than any insn in the unrolled loop. - This is needed so that mostly_true_jump in reorg.c will treat jumps - to this loop end label correctly, i.e. predict that they are usually - not taken. */ - if (exit_label) - emit_label_after (exit_label, loop_end); -} - -/* Return true if the loop can be safely, and profitably, preconditioned - so that the unrolled copies of the loop body don't need exit tests. - - This only works if final_value, initial_value and increment can be - determined, and if increment is a constant power of 2. - If increment is not a power of 2, then the preconditioning modulo - operation would require a real modulo instead of a boolean AND, and this - is not considered `profitable'. */ - -/* ??? If the loop is known to be executed very many times, or the machine - has a very cheap divide instruction, then preconditioning is a win even - when the increment is not a power of 2. Use RTX_COST to compute - whether divide is cheap. - ??? A divide by constant doesn't actually need a divide, look at - expand_divmod. The reduced cost of this optimized modulo is not - reflected in RTX_COST. */ - -int -precondition_loop_p (loop_start, loop_info, - initial_value, final_value, increment, mode) - rtx loop_start; - struct loop_info *loop_info; - rtx *initial_value, *final_value, *increment; - enum machine_mode *mode; -{ - - if (loop_info->n_iterations > 0) - { - *initial_value = const0_rtx; - *increment = const1_rtx; - *final_value = GEN_INT (loop_info->n_iterations); - *mode = word_mode; - - if (loop_dump_stream) - { - fputs ("Preconditioning: Success, number of iterations known, ", - loop_dump_stream); - fprintf (loop_dump_stream, HOST_WIDE_INT_PRINT_DEC, - loop_info->n_iterations); - fputs (".\n", loop_dump_stream); - } - return 1; - } - - if (loop_info->initial_value == 0) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Preconditioning: Could not find initial value.\n"); - return 0; - } - else if (loop_info->increment == 0) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Preconditioning: Could not find increment value.\n"); - return 0; - } - else if (GET_CODE (loop_info->increment) != CONST_INT) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Preconditioning: Increment not a constant.\n"); - return 0; - } - else if ((exact_log2 (INTVAL (loop_info->increment)) < 0) - && (exact_log2 (- INTVAL (loop_info->increment)) < 0)) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Preconditioning: Increment not a constant power of 2.\n"); - return 0; - } - - /* Unsigned_compare and compare_dir can be ignored here, since they do - not matter for preconditioning. */ - - if (loop_info->final_value == 0) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Preconditioning: EQ comparison loop.\n"); - return 0; - } - - /* Must ensure that final_value is invariant, so call invariant_p to - check. Before doing so, must check regno against max_reg_before_loop - to make sure that the register is in the range covered by invariant_p. - If it isn't, then it is most likely a biv/giv which by definition are - not invariant. */ - if ((GET_CODE (loop_info->final_value) == REG - && REGNO (loop_info->final_value) >= max_reg_before_loop) - || (GET_CODE (loop_info->final_value) == PLUS - && REGNO (XEXP (loop_info->final_value, 0)) >= max_reg_before_loop) - || ! invariant_p (loop_info->final_value)) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Preconditioning: Final value not invariant.\n"); - return 0; - } - - /* Fail for floating point values, since the caller of this function - does not have code to deal with them. */ - if (GET_MODE_CLASS (GET_MODE (loop_info->final_value)) == MODE_FLOAT - || GET_MODE_CLASS (GET_MODE (loop_info->initial_value)) == MODE_FLOAT) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Preconditioning: Floating point final or initial value.\n"); - return 0; - } - - /* Fail if loop_info->iteration_var is not live before loop_start, - since we need to test its value in the preconditioning code. */ - - if (uid_luid[REGNO_FIRST_UID (REGNO (loop_info->iteration_var))] - > INSN_LUID (loop_start)) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Preconditioning: Iteration var not live before loop start.\n"); - return 0; - } - - /* ??? Note that if iteration_info is modifed to allow GIV iterators - such as "while (i-- > 0)", the initial value will be one too small. - In this case, loop_iteration_var could be used to determine - the correct initial value, provided the loop has not been reversed. - - Also note that the absolute values of initial_value and - final_value are unimportant as only their difference is used for - calculating the number of loop iterations. */ - *initial_value = loop_info->initial_value; - *increment = loop_info->increment; - *final_value = loop_info->final_value; - - /* Decide what mode to do these calculations in. Choose the larger - of final_value's mode and initial_value's mode, or a full-word if - both are constants. */ - *mode = GET_MODE (*final_value); - if (*mode == VOIDmode) - { - *mode = GET_MODE (*initial_value); - if (*mode == VOIDmode) - *mode = word_mode; - } - else if (*mode != GET_MODE (*initial_value) - && (GET_MODE_SIZE (*mode) - < GET_MODE_SIZE (GET_MODE (*initial_value)))) - *mode = GET_MODE (*initial_value); - - /* Success! */ - if (loop_dump_stream) - fprintf (loop_dump_stream, "Preconditioning: Successful.\n"); - return 1; -} - - -/* All pseudo-registers must be mapped to themselves. Two hard registers - must be mapped, VIRTUAL_STACK_VARS_REGNUM and VIRTUAL_INCOMING_ARGS_ - REGNUM, to avoid function-inlining specific conversions of these - registers. All other hard regs can not be mapped because they may be - used with different - modes. */ - -static void -init_reg_map (map, maxregnum) - struct inline_remap *map; - int maxregnum; -{ - int i; - - for (i = maxregnum - 1; i > LAST_VIRTUAL_REGISTER; i--) - map->reg_map[i] = regno_reg_rtx[i]; - /* Just clear the rest of the entries. */ - for (i = LAST_VIRTUAL_REGISTER; i >= 0; i--) - map->reg_map[i] = 0; - - map->reg_map[VIRTUAL_STACK_VARS_REGNUM] - = regno_reg_rtx[VIRTUAL_STACK_VARS_REGNUM]; - map->reg_map[VIRTUAL_INCOMING_ARGS_REGNUM] - = regno_reg_rtx[VIRTUAL_INCOMING_ARGS_REGNUM]; -} - -/* Strength-reduction will often emit code for optimized biv/givs which - calculates their value in a temporary register, and then copies the result - to the iv. This procedure reconstructs the pattern computing the iv; - verifying that all operands are of the proper form. - - PATTERN must be the result of single_set. - The return value is the amount that the giv is incremented by. */ - -static rtx -calculate_giv_inc (pattern, src_insn, regno) - rtx pattern, src_insn; - int regno; -{ - rtx increment; - rtx increment_total = 0; - int tries = 0; - - retry: - /* Verify that we have an increment insn here. First check for a plus - as the set source. */ - if (GET_CODE (SET_SRC (pattern)) != PLUS) - { - /* SR sometimes computes the new giv value in a temp, then copies it - to the new_reg. */ - src_insn = PREV_INSN (src_insn); - pattern = PATTERN (src_insn); - if (GET_CODE (SET_SRC (pattern)) != PLUS) - abort (); - - /* The last insn emitted is not needed, so delete it to avoid confusing - the second cse pass. This insn sets the giv unnecessarily. */ - delete_insn (get_last_insn ()); - } - - /* Verify that we have a constant as the second operand of the plus. */ - increment = XEXP (SET_SRC (pattern), 1); - if (GET_CODE (increment) != CONST_INT) - { - /* SR sometimes puts the constant in a register, especially if it is - too big to be an add immed operand. */ - src_insn = PREV_INSN (src_insn); - increment = SET_SRC (PATTERN (src_insn)); - - /* SR may have used LO_SUM to compute the constant if it is too large - for a load immed operand. In this case, the constant is in operand - one of the LO_SUM rtx. */ - if (GET_CODE (increment) == LO_SUM) - increment = XEXP (increment, 1); - - /* Some ports store large constants in memory and add a REG_EQUAL - note to the store insn. */ - else if (GET_CODE (increment) == MEM) - { - rtx note = find_reg_note (src_insn, REG_EQUAL, 0); - if (note) - increment = XEXP (note, 0); - } - - else if (GET_CODE (increment) == IOR - || GET_CODE (increment) == ASHIFT - || GET_CODE (increment) == PLUS) - { - /* The rs6000 port loads some constants with IOR. - The alpha port loads some constants with ASHIFT and PLUS. */ - rtx second_part = XEXP (increment, 1); - enum rtx_code code = GET_CODE (increment); - - src_insn = PREV_INSN (src_insn); - increment = SET_SRC (PATTERN (src_insn)); - /* Don't need the last insn anymore. */ - delete_insn (get_last_insn ()); - - if (GET_CODE (second_part) != CONST_INT - || GET_CODE (increment) != CONST_INT) - abort (); - - if (code == IOR) - increment = GEN_INT (INTVAL (increment) | INTVAL (second_part)); - else if (code == PLUS) - increment = GEN_INT (INTVAL (increment) + INTVAL (second_part)); - else - increment = GEN_INT (INTVAL (increment) << INTVAL (second_part)); - } - - if (GET_CODE (increment) != CONST_INT) - abort (); - - /* The insn loading the constant into a register is no longer needed, - so delete it. */ - delete_insn (get_last_insn ()); - } - - if (increment_total) - increment_total = GEN_INT (INTVAL (increment_total) + INTVAL (increment)); - else - increment_total = increment; - - /* Check that the source register is the same as the register we expected - to see as the source. If not, something is seriously wrong. */ - if (GET_CODE (XEXP (SET_SRC (pattern), 0)) != REG - || REGNO (XEXP (SET_SRC (pattern), 0)) != regno) - { - /* Some machines (e.g. the romp), may emit two add instructions for - certain constants, so lets try looking for another add immediately - before this one if we have only seen one add insn so far. */ - - if (tries == 0) - { - tries++; - - src_insn = PREV_INSN (src_insn); - pattern = PATTERN (src_insn); - - delete_insn (get_last_insn ()); - - goto retry; - } - - abort (); - } - - return increment_total; -} - -/* Copy REG_NOTES, except for insn references, because not all insn_map - entries are valid yet. We do need to copy registers now though, because - the reg_map entries can change during copying. */ - -static rtx -initial_reg_note_copy (notes, map) - rtx notes; - struct inline_remap *map; -{ - rtx copy; - - if (notes == 0) - return 0; - - copy = rtx_alloc (GET_CODE (notes)); - PUT_MODE (copy, GET_MODE (notes)); - - if (GET_CODE (notes) == EXPR_LIST) - XEXP (copy, 0) = copy_rtx_and_substitute (XEXP (notes, 0), map); - else if (GET_CODE (notes) == INSN_LIST) - /* Don't substitute for these yet. */ - XEXP (copy, 0) = XEXP (notes, 0); - else - abort (); - - XEXP (copy, 1) = initial_reg_note_copy (XEXP (notes, 1), map); - - return copy; -} - -/* Fixup insn references in copied REG_NOTES. */ - -static void -final_reg_note_copy (notes, map) - rtx notes; - struct inline_remap *map; -{ - rtx note; - - for (note = notes; note; note = XEXP (note, 1)) - if (GET_CODE (note) == INSN_LIST) - XEXP (note, 0) = map->insn_map[INSN_UID (XEXP (note, 0))]; -} - -/* Copy each instruction in the loop, substituting from map as appropriate. - This is very similar to a loop in expand_inline_function. */ - -static void -copy_loop_body (copy_start, copy_end, map, exit_label, last_iteration, - unroll_type, start_label, loop_end, insert_before, - copy_notes_from) - rtx copy_start, copy_end; - struct inline_remap *map; - rtx exit_label; - int last_iteration; - enum unroll_types unroll_type; - rtx start_label, loop_end, insert_before, copy_notes_from; -{ - rtx insn, pattern; - rtx set, tem, copy; - int dest_reg_was_split, i; -#ifdef HAVE_cc0 - rtx cc0_insn = 0; -#endif - rtx final_label = 0; - rtx giv_inc, giv_dest_reg, giv_src_reg; - - /* If this isn't the last iteration, then map any references to the - start_label to final_label. Final label will then be emitted immediately - after the end of this loop body if it was ever used. - - If this is the last iteration, then map references to the start_label - to itself. */ - if (! last_iteration) - { - final_label = gen_label_rtx (); - set_label_in_map (map, CODE_LABEL_NUMBER (start_label), - final_label); - } - else - set_label_in_map (map, CODE_LABEL_NUMBER (start_label), start_label); - - start_sequence (); - - /* Emit a NOTE_INSN_DELETED to force at least two insns onto the sequence. - Else gen_sequence could return a raw pattern for a jump which we pass - off to emit_insn_before (instead of emit_jump_insn_before) which causes - a variety of losing behaviors later. */ - emit_note (0, NOTE_INSN_DELETED); - - insn = copy_start; - do - { - insn = NEXT_INSN (insn); - - map->orig_asm_operands_vector = 0; - - switch (GET_CODE (insn)) - { - case INSN: - pattern = PATTERN (insn); - copy = 0; - giv_inc = 0; - - /* Check to see if this is a giv that has been combined with - some split address givs. (Combined in the sense that - `combine_givs' in loop.c has put two givs in the same register.) - In this case, we must search all givs based on the same biv to - find the address givs. Then split the address givs. - Do this before splitting the giv, since that may map the - SET_DEST to a new register. */ - - if ((set = single_set (insn)) - && GET_CODE (SET_DEST (set)) == REG - && addr_combined_regs[REGNO (SET_DEST (set))]) - { - struct iv_class *bl; - struct induction *v, *tv; - int regno = REGNO (SET_DEST (set)); - - v = addr_combined_regs[REGNO (SET_DEST (set))]; - bl = reg_biv_class[REGNO (v->src_reg)]; - - /* Although the giv_inc amount is not needed here, we must call - calculate_giv_inc here since it might try to delete the - last insn emitted. If we wait until later to call it, - we might accidentally delete insns generated immediately - below by emit_unrolled_add. */ - - if (! derived_regs[regno]) - giv_inc = calculate_giv_inc (set, insn, regno); - - /* Now find all address giv's that were combined with this - giv 'v'. */ - for (tv = bl->giv; tv; tv = tv->next_iv) - if (tv->giv_type == DEST_ADDR && tv->same == v) - { - int this_giv_inc; - - /* If this DEST_ADDR giv was not split, then ignore it. */ - if (*tv->location != tv->dest_reg) - continue; - - /* Scale this_giv_inc if the multiplicative factors of - the two givs are different. */ - this_giv_inc = INTVAL (giv_inc); - if (tv->mult_val != v->mult_val) - this_giv_inc = (this_giv_inc / INTVAL (v->mult_val) - * INTVAL (tv->mult_val)); - - tv->dest_reg = plus_constant (tv->dest_reg, this_giv_inc); - *tv->location = tv->dest_reg; - - if (last_iteration && unroll_type != UNROLL_COMPLETELY) - { - /* Must emit an insn to increment the split address - giv. Add in the const_adjust field in case there - was a constant eliminated from the address. */ - rtx value, dest_reg; - - /* tv->dest_reg will be either a bare register, - or else a register plus a constant. */ - if (GET_CODE (tv->dest_reg) == REG) - dest_reg = tv->dest_reg; - else - dest_reg = XEXP (tv->dest_reg, 0); - - /* Check for shared address givs, and avoid - incrementing the shared pseudo reg more than - once. */ - if (! tv->same_insn && ! tv->shared) - { - /* tv->dest_reg may actually be a (PLUS (REG) - (CONST)) here, so we must call plus_constant - to add the const_adjust amount before calling - emit_unrolled_add below. */ - value = plus_constant (tv->dest_reg, - tv->const_adjust); - - /* The constant could be too large for an add - immediate, so can't directly emit an insn - here. */ - emit_unrolled_add (dest_reg, XEXP (value, 0), - XEXP (value, 1)); - } - - /* Reset the giv to be just the register again, in case - it is used after the set we have just emitted. - We must subtract the const_adjust factor added in - above. */ - tv->dest_reg = plus_constant (dest_reg, - - tv->const_adjust); - *tv->location = tv->dest_reg; - } - } - } - - /* If this is a setting of a splittable variable, then determine - how to split the variable, create a new set based on this split, - and set up the reg_map so that later uses of the variable will - use the new split variable. */ - - dest_reg_was_split = 0; - - if ((set = single_set (insn)) - && GET_CODE (SET_DEST (set)) == REG - && splittable_regs[REGNO (SET_DEST (set))]) - { - int regno = REGNO (SET_DEST (set)); - int src_regno; - - dest_reg_was_split = 1; - - giv_dest_reg = SET_DEST (set); - if (derived_regs[regno]) - { - /* ??? This relies on SET_SRC (SET) to be of - the form (plus (reg) (const_int)), and thus - forces recombine_givs to restrict the kind - of giv derivations it does before unrolling. */ - giv_src_reg = XEXP (SET_SRC (set), 0); - giv_inc = XEXP (SET_SRC (set), 1); - } - else - { - giv_src_reg = giv_dest_reg; - /* Compute the increment value for the giv, if it wasn't - already computed above. */ - if (giv_inc == 0) - giv_inc = calculate_giv_inc (set, insn, regno); - } - src_regno = REGNO (giv_src_reg); - - if (unroll_type == UNROLL_COMPLETELY) - { - /* Completely unrolling the loop. Set the induction - variable to a known constant value. */ - - /* The value in splittable_regs may be an invariant - value, so we must use plus_constant here. */ - splittable_regs[regno] - = plus_constant (splittable_regs[src_regno], - INTVAL (giv_inc)); - - if (GET_CODE (splittable_regs[regno]) == PLUS) - { - giv_src_reg = XEXP (splittable_regs[regno], 0); - giv_inc = XEXP (splittable_regs[regno], 1); - } - else - { - /* The splittable_regs value must be a REG or a - CONST_INT, so put the entire value in the giv_src_reg - variable. */ - giv_src_reg = splittable_regs[regno]; - giv_inc = const0_rtx; - } - } - else - { - /* Partially unrolling loop. Create a new pseudo - register for the iteration variable, and set it to - be a constant plus the original register. Except - on the last iteration, when the result has to - go back into the original iteration var register. */ - - /* Handle bivs which must be mapped to a new register - when split. This happens for bivs which need their - final value set before loop entry. The new register - for the biv was stored in the biv's first struct - induction entry by find_splittable_regs. */ - - if (regno < max_reg_before_loop - && REG_IV_TYPE (regno) == BASIC_INDUCT) - { - giv_src_reg = reg_biv_class[regno]->biv->src_reg; - giv_dest_reg = giv_src_reg; - } - -#if 0 - /* If non-reduced/final-value givs were split, then - this would have to remap those givs also. See - find_splittable_regs. */ -#endif - - splittable_regs[regno] - = GEN_INT (INTVAL (giv_inc) - + INTVAL (splittable_regs[src_regno])); - giv_inc = splittable_regs[regno]; - - /* Now split the induction variable by changing the dest - of this insn to a new register, and setting its - reg_map entry to point to this new register. - - If this is the last iteration, and this is the last insn - that will update the iv, then reuse the original dest, - to ensure that the iv will have the proper value when - the loop exits or repeats. - - Using splittable_regs_updates here like this is safe, - because it can only be greater than one if all - instructions modifying the iv are always executed in - order. */ - - if (! last_iteration - || (splittable_regs_updates[regno]-- != 1)) - { - tem = gen_reg_rtx (GET_MODE (giv_src_reg)); - giv_dest_reg = tem; - map->reg_map[regno] = tem; - record_base_value (REGNO (tem), - giv_inc == const0_rtx - ? giv_src_reg - : gen_rtx_PLUS (GET_MODE (giv_src_reg), - giv_src_reg, giv_inc), - 1); - } - else - map->reg_map[regno] = giv_src_reg; - } - - /* The constant being added could be too large for an add - immediate, so can't directly emit an insn here. */ - emit_unrolled_add (giv_dest_reg, giv_src_reg, giv_inc); - copy = get_last_insn (); - pattern = PATTERN (copy); - } - else - { - pattern = copy_rtx_and_substitute (pattern, map); - copy = emit_insn (pattern); - } - REG_NOTES (copy) = initial_reg_note_copy (REG_NOTES (insn), map); - -#ifdef HAVE_cc0 - /* If this insn is setting CC0, it may need to look at - the insn that uses CC0 to see what type of insn it is. - In that case, the call to recog via validate_change will - fail. So don't substitute constants here. Instead, - do it when we emit the following insn. - - For example, see the pyr.md file. That machine has signed and - unsigned compares. The compare patterns must check the - following branch insn to see which what kind of compare to - emit. - - If the previous insn set CC0, substitute constants on it as - well. */ - if (sets_cc0_p (PATTERN (copy)) != 0) - cc0_insn = copy; - else - { - if (cc0_insn) - try_constants (cc0_insn, map); - cc0_insn = 0; - try_constants (copy, map); - } -#else - try_constants (copy, map); -#endif - - /* Make split induction variable constants `permanent' since we - know there are no backward branches across iteration variable - settings which would invalidate this. */ - if (dest_reg_was_split) - { - int regno = REGNO (SET_DEST (pattern)); - - if (regno < map->const_equiv_map_size - && map->const_age_map[regno] == map->const_age) - map->const_age_map[regno] = -1; - } - break; - - case JUMP_INSN: - pattern = copy_rtx_and_substitute (PATTERN (insn), map); - copy = emit_jump_insn (pattern); - REG_NOTES (copy) = initial_reg_note_copy (REG_NOTES (insn), map); - - if (JUMP_LABEL (insn) == start_label && insn == copy_end - && ! last_iteration) - { - /* This is a branch to the beginning of the loop; this is the - last insn being copied; and this is not the last iteration. - In this case, we want to change the original fall through - case to be a branch past the end of the loop, and the - original jump label case to fall_through. */ - - if (invert_exp (pattern, copy)) - { - if (! redirect_exp (&pattern, - get_label_from_map (map, - CODE_LABEL_NUMBER - (JUMP_LABEL (insn))), - exit_label, copy)) - abort (); - } - else - { - rtx jmp; - rtx lab = gen_label_rtx (); - /* Can't do it by reversing the jump (probably because we - couldn't reverse the conditions), so emit a new - jump_insn after COPY, and redirect the jump around - that. */ - jmp = emit_jump_insn_after (gen_jump (exit_label), copy); - jmp = emit_barrier_after (jmp); - emit_label_after (lab, jmp); - LABEL_NUSES (lab) = 0; - if (! redirect_exp (&pattern, - get_label_from_map (map, - CODE_LABEL_NUMBER - (JUMP_LABEL (insn))), - lab, copy)) - abort (); - } - } - -#ifdef HAVE_cc0 - if (cc0_insn) - try_constants (cc0_insn, map); - cc0_insn = 0; -#endif - try_constants (copy, map); - - /* Set the jump label of COPY correctly to avoid problems with - later passes of unroll_loop, if INSN had jump label set. */ - if (JUMP_LABEL (insn)) - { - rtx label = 0; - - /* Can't use the label_map for every insn, since this may be - the backward branch, and hence the label was not mapped. */ - if ((set = single_set (copy))) - { - tem = SET_SRC (set); - if (GET_CODE (tem) == LABEL_REF) - label = XEXP (tem, 0); - else if (GET_CODE (tem) == IF_THEN_ELSE) - { - if (XEXP (tem, 1) != pc_rtx) - label = XEXP (XEXP (tem, 1), 0); - else - label = XEXP (XEXP (tem, 2), 0); - } - } - - if (label && GET_CODE (label) == CODE_LABEL) - JUMP_LABEL (copy) = label; - else - { - /* An unrecognizable jump insn, probably the entry jump - for a switch statement. This label must have been mapped, - so just use the label_map to get the new jump label. */ - JUMP_LABEL (copy) - = get_label_from_map (map, - CODE_LABEL_NUMBER (JUMP_LABEL (insn))); - } - - /* If this is a non-local jump, then must increase the label - use count so that the label will not be deleted when the - original jump is deleted. */ - LABEL_NUSES (JUMP_LABEL (copy))++; - } - else if (GET_CODE (PATTERN (copy)) == ADDR_VEC - || GET_CODE (PATTERN (copy)) == ADDR_DIFF_VEC) - { - rtx pat = PATTERN (copy); - int diff_vec_p = GET_CODE (pat) == ADDR_DIFF_VEC; - int len = XVECLEN (pat, diff_vec_p); - int i; - - for (i = 0; i < len; i++) - LABEL_NUSES (XEXP (XVECEXP (pat, diff_vec_p, i), 0))++; - } - - /* If this used to be a conditional jump insn but whose branch - direction is now known, we must do something special. */ - if (condjump_p (insn) && !simplejump_p (insn) && map->last_pc_value) - { -#ifdef HAVE_cc0 - /* The previous insn set cc0 for us. So delete it. */ - delete_insn (PREV_INSN (copy)); -#endif - - /* If this is now a no-op, delete it. */ - if (map->last_pc_value == pc_rtx) - { - /* Don't let delete_insn delete the label referenced here, - because we might possibly need it later for some other - instruction in the loop. */ - if (JUMP_LABEL (copy)) - LABEL_NUSES (JUMP_LABEL (copy))++; - delete_insn (copy); - if (JUMP_LABEL (copy)) - LABEL_NUSES (JUMP_LABEL (copy))--; - copy = 0; - } - else - /* Otherwise, this is unconditional jump so we must put a - BARRIER after it. We could do some dead code elimination - here, but jump.c will do it just as well. */ - emit_barrier (); - } - break; - - case CALL_INSN: - pattern = copy_rtx_and_substitute (PATTERN (insn), map); - copy = emit_call_insn (pattern); - REG_NOTES (copy) = initial_reg_note_copy (REG_NOTES (insn), map); - - /* Because the USAGE information potentially contains objects other - than hard registers, we need to copy it. */ - CALL_INSN_FUNCTION_USAGE (copy) - = copy_rtx_and_substitute (CALL_INSN_FUNCTION_USAGE (insn), map); - -#ifdef HAVE_cc0 - if (cc0_insn) - try_constants (cc0_insn, map); - cc0_insn = 0; -#endif - try_constants (copy, map); - - /* Be lazy and assume CALL_INSNs clobber all hard registers. */ - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - map->const_equiv_map[i] = 0; - break; - - case CODE_LABEL: - /* If this is the loop start label, then we don't need to emit a - copy of this label since no one will use it. */ - - if (insn != start_label) - { - copy = emit_label (get_label_from_map (map, - CODE_LABEL_NUMBER (insn))); - map->const_age++; - } - break; - - case BARRIER: - copy = emit_barrier (); - break; - - case NOTE: - /* VTOP and CONT notes are valid only before the loop exit test. - If placed anywhere else, loop may generate bad code. */ - - if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_DELETED - && ((NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_VTOP - && NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_CONT) - || (last_iteration && unroll_type != UNROLL_COMPLETELY))) - copy = emit_note (NOTE_SOURCE_FILE (insn), - NOTE_LINE_NUMBER (insn)); - else - copy = 0; - break; - - default: - abort (); - break; - } - - map->insn_map[INSN_UID (insn)] = copy; - } - while (insn != copy_end); - - /* Now finish coping the REG_NOTES. */ - insn = copy_start; - do - { - insn = NEXT_INSN (insn); - if ((GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN - || GET_CODE (insn) == CALL_INSN) - && map->insn_map[INSN_UID (insn)]) - final_reg_note_copy (REG_NOTES (map->insn_map[INSN_UID (insn)]), map); - } - while (insn != copy_end); - - /* There may be notes between copy_notes_from and loop_end. Emit a copy of - each of these notes here, since there may be some important ones, such as - NOTE_INSN_BLOCK_END notes, in this group. We don't do this on the last - iteration, because the original notes won't be deleted. - - We can't use insert_before here, because when from preconditioning, - insert_before points before the loop. We can't use copy_end, because - there may be insns already inserted after it (which we don't want to - copy) when not from preconditioning code. */ - - if (! last_iteration) - { - for (insn = copy_notes_from; insn != loop_end; insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) == NOTE - && NOTE_LINE_NUMBER (insn) != NOTE_INSN_DELETED) - emit_note (NOTE_SOURCE_FILE (insn), NOTE_LINE_NUMBER (insn)); - } - } - - if (final_label && LABEL_NUSES (final_label) > 0) - emit_label (final_label); - - tem = gen_sequence (); - end_sequence (); - emit_insn_before (tem, insert_before); -} - -/* Emit an insn, using the expand_binop to ensure that a valid insn is - emitted. This will correctly handle the case where the increment value - won't fit in the immediate field of a PLUS insns. */ - -void -emit_unrolled_add (dest_reg, src_reg, increment) - rtx dest_reg, src_reg, increment; -{ - rtx result; - - result = expand_binop (GET_MODE (dest_reg), add_optab, src_reg, increment, - dest_reg, 0, OPTAB_LIB_WIDEN); - - if (dest_reg != result) - emit_move_insn (dest_reg, result); -} - -/* Searches the insns between INSN and LOOP_END. Returns 1 if there - is a backward branch in that range that branches to somewhere between - LOOP_START and INSN. Returns 0 otherwise. */ - -/* ??? This is quadratic algorithm. Could be rewritten to be linear. - In practice, this is not a problem, because this function is seldom called, - and uses a negligible amount of CPU time on average. */ - -int -back_branch_in_range_p (insn, loop_start, loop_end) - rtx insn; - rtx loop_start, loop_end; -{ - rtx p, q, target_insn; - rtx orig_loop_end = loop_end; - - /* Stop before we get to the backward branch at the end of the loop. */ - loop_end = prev_nonnote_insn (loop_end); - if (GET_CODE (loop_end) == BARRIER) - loop_end = PREV_INSN (loop_end); - - /* Check in case insn has been deleted, search forward for first non - deleted insn following it. */ - while (INSN_DELETED_P (insn)) - insn = NEXT_INSN (insn); - - /* Check for the case where insn is the last insn in the loop. Deal - with the case where INSN was a deleted loop test insn, in which case - it will now be the NOTE_LOOP_END. */ - if (insn == loop_end || insn == orig_loop_end) - return 0; - - for (p = NEXT_INSN (insn); p != loop_end; p = NEXT_INSN (p)) - { - if (GET_CODE (p) == JUMP_INSN) - { - target_insn = JUMP_LABEL (p); - - /* Search from loop_start to insn, to see if one of them is - the target_insn. We can't use INSN_LUID comparisons here, - since insn may not have an LUID entry. */ - for (q = loop_start; q != insn; q = NEXT_INSN (q)) - if (q == target_insn) - return 1; - } - } - - return 0; -} - -/* Try to generate the simplest rtx for the expression - (PLUS (MULT mult1 mult2) add1). This is used to calculate the initial - value of giv's. */ - -static rtx -fold_rtx_mult_add (mult1, mult2, add1, mode) - rtx mult1, mult2, add1; - enum machine_mode mode; -{ - rtx temp, mult_res; - rtx result; - - /* The modes must all be the same. This should always be true. For now, - check to make sure. */ - if ((GET_MODE (mult1) != mode && GET_MODE (mult1) != VOIDmode) - || (GET_MODE (mult2) != mode && GET_MODE (mult2) != VOIDmode) - || (GET_MODE (add1) != mode && GET_MODE (add1) != VOIDmode)) - abort (); - - /* Ensure that if at least one of mult1/mult2 are constant, then mult2 - will be a constant. */ - if (GET_CODE (mult1) == CONST_INT) - { - temp = mult2; - mult2 = mult1; - mult1 = temp; - } - - mult_res = simplify_binary_operation (MULT, mode, mult1, mult2); - if (! mult_res) - mult_res = gen_rtx_MULT (mode, mult1, mult2); - - /* Again, put the constant second. */ - if (GET_CODE (add1) == CONST_INT) - { - temp = add1; - add1 = mult_res; - mult_res = temp; - } - - result = simplify_binary_operation (PLUS, mode, add1, mult_res); - if (! result) - result = gen_rtx_PLUS (mode, add1, mult_res); - - return result; -} - -/* Searches the list of induction struct's for the biv BL, to try to calculate - the total increment value for one iteration of the loop as a constant. - - Returns the increment value as an rtx, simplified as much as possible, - if it can be calculated. Otherwise, returns 0. */ - -rtx -biv_total_increment (bl, loop_start, loop_end) - struct iv_class *bl; - rtx loop_start, loop_end; -{ - struct induction *v; - rtx result; - - /* For increment, must check every instruction that sets it. Each - instruction must be executed only once each time through the loop. - To verify this, we check that the insn is always executed, and that - there are no backward branches after the insn that branch to before it. - Also, the insn must have a mult_val of one (to make sure it really is - an increment). */ - - result = const0_rtx; - for (v = bl->biv; v; v = v->next_iv) - { - if (v->always_computable && v->mult_val == const1_rtx - && ! v->maybe_multiple) - result = fold_rtx_mult_add (result, const1_rtx, v->add_val, v->mode); - else - return 0; - } - - return result; -} - -/* Determine the initial value of the iteration variable, and the amount - that it is incremented each loop. Use the tables constructed by - the strength reduction pass to calculate these values. - - Initial_value and/or increment are set to zero if their values could not - be calculated. */ - -static void -iteration_info (iteration_var, initial_value, increment, loop_start, loop_end) - rtx iteration_var, *initial_value, *increment; - rtx loop_start, loop_end; -{ - struct iv_class *bl; -#if 0 - struct induction *v; -#endif - - /* Clear the result values, in case no answer can be found. */ - *initial_value = 0; - *increment = 0; - - /* The iteration variable can be either a giv or a biv. Check to see - which it is, and compute the variable's initial value, and increment - value if possible. */ - - /* If this is a new register, can't handle it since we don't have any - reg_iv_type entry for it. */ - if ((unsigned) REGNO (iteration_var) > reg_iv_type->num_elements) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Loop unrolling: No reg_iv_type entry for iteration var.\n"); - return; - } - - /* Reject iteration variables larger than the host wide int size, since they - could result in a number of iterations greater than the range of our - `unsigned HOST_WIDE_INT' variable loop_info->n_iterations. */ - else if ((GET_MODE_BITSIZE (GET_MODE (iteration_var)) - > HOST_BITS_PER_WIDE_INT)) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Loop unrolling: Iteration var rejected because mode too large.\n"); - return; - } - else if (GET_MODE_CLASS (GET_MODE (iteration_var)) != MODE_INT) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Loop unrolling: Iteration var not an integer.\n"); - return; - } - else if (REG_IV_TYPE (REGNO (iteration_var)) == BASIC_INDUCT) - { - /* Grab initial value, only useful if it is a constant. */ - bl = reg_biv_class[REGNO (iteration_var)]; - *initial_value = bl->initial_value; - - *increment = biv_total_increment (bl, loop_start, loop_end); - } - else if (REG_IV_TYPE (REGNO (iteration_var)) == GENERAL_INDUCT) - { -#if 1 - /* ??? The code below does not work because the incorrect number of - iterations is calculated when the biv is incremented after the giv - is set (which is the usual case). This can probably be accounted - for by biasing the initial_value by subtracting the amount of the - increment that occurs between the giv set and the giv test. However, - a giv as an iterator is very rare, so it does not seem worthwhile - to handle this. */ - /* ??? An example failure is: i = 6; do {;} while (i++ < 9). */ - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Loop unrolling: Giv iterators are not handled.\n"); - return; -#else - /* Initial value is mult_val times the biv's initial value plus - add_val. Only useful if it is a constant. */ - v = REG_IV_INFO (REGNO (iteration_var)); - bl = reg_biv_class[REGNO (v->src_reg)]; - *initial_value = fold_rtx_mult_add (v->mult_val, bl->initial_value, - v->add_val, v->mode); - - /* Increment value is mult_val times the increment value of the biv. */ - - *increment = biv_total_increment (bl, loop_start, loop_end); - if (*increment) - *increment = fold_rtx_mult_add (v->mult_val, *increment, const0_rtx, - v->mode); -#endif - } - else - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Loop unrolling: Not basic or general induction var.\n"); - return; - } -} - - -/* For each biv and giv, determine whether it can be safely split into - a different variable for each unrolled copy of the loop body. If it - is safe to split, then indicate that by saving some useful info - in the splittable_regs array. - - If the loop is being completely unrolled, then splittable_regs will hold - the current value of the induction variable while the loop is unrolled. - It must be set to the initial value of the induction variable here. - Otherwise, splittable_regs will hold the difference between the current - value of the induction variable and the value the induction variable had - at the top of the loop. It must be set to the value 0 here. - - Returns the total number of instructions that set registers that are - splittable. */ - -/* ?? If the loop is only unrolled twice, then most of the restrictions to - constant values are unnecessary, since we can easily calculate increment - values in this case even if nothing is constant. The increment value - should not involve a multiply however. */ - -/* ?? Even if the biv/giv increment values aren't constant, it may still - be beneficial to split the variable if the loop is only unrolled a few - times, since multiplies by small integers (1,2,3,4) are very cheap. */ - -static int -find_splittable_regs (unroll_type, loop_start, loop_end, end_insert_before, - unroll_number, n_iterations) - enum unroll_types unroll_type; - rtx loop_start, loop_end; - rtx end_insert_before; - int unroll_number; - unsigned HOST_WIDE_INT n_iterations; -{ - struct iv_class *bl; - struct induction *v; - rtx increment, tem; - rtx biv_final_value; - int biv_splittable; - int result = 0; - - for (bl = loop_iv_list; bl; bl = bl->next) - { - /* Biv_total_increment must return a constant value, - otherwise we can not calculate the split values. */ - - increment = biv_total_increment (bl, loop_start, loop_end); - if (! increment || GET_CODE (increment) != CONST_INT) - continue; - - /* The loop must be unrolled completely, or else have a known number - of iterations and only one exit, or else the biv must be dead - outside the loop, or else the final value must be known. Otherwise, - it is unsafe to split the biv since it may not have the proper - value on loop exit. */ - - /* loop_number_exit_count is non-zero if the loop has an exit other than - a fall through at the end. */ - - biv_splittable = 1; - biv_final_value = 0; - if (unroll_type != UNROLL_COMPLETELY - && (loop_number_exit_count[uid_loop_num[INSN_UID (loop_start)]] - || unroll_type == UNROLL_NAIVE) - && (uid_luid[REGNO_LAST_UID (bl->regno)] >= INSN_LUID (loop_end) - || ! bl->init_insn - || INSN_UID (bl->init_insn) >= max_uid_for_loop - || (uid_luid[REGNO_FIRST_UID (bl->regno)] - < INSN_LUID (bl->init_insn)) - || reg_mentioned_p (bl->biv->dest_reg, SET_SRC (bl->init_set))) - && ! (biv_final_value = final_biv_value (bl, loop_start, loop_end, - n_iterations))) - biv_splittable = 0; - - /* If any of the insns setting the BIV don't do so with a simple - PLUS, we don't know how to split it. */ - for (v = bl->biv; biv_splittable && v; v = v->next_iv) - if ((tem = single_set (v->insn)) == 0 - || GET_CODE (SET_DEST (tem)) != REG - || REGNO (SET_DEST (tem)) != bl->regno - || GET_CODE (SET_SRC (tem)) != PLUS) - biv_splittable = 0; - - /* If final value is non-zero, then must emit an instruction which sets - the value of the biv to the proper value. This is done after - handling all of the givs, since some of them may need to use the - biv's value in their initialization code. */ - - /* This biv is splittable. If completely unrolling the loop, save - the biv's initial value. Otherwise, save the constant zero. */ - - if (biv_splittable == 1) - { - if (unroll_type == UNROLL_COMPLETELY) - { - /* If the initial value of the biv is itself (i.e. it is too - complicated for strength_reduce to compute), or is a hard - register, or it isn't invariant, then we must create a new - pseudo reg to hold the initial value of the biv. */ - - if (GET_CODE (bl->initial_value) == REG - && (REGNO (bl->initial_value) == bl->regno - || REGNO (bl->initial_value) < FIRST_PSEUDO_REGISTER - || ! invariant_p (bl->initial_value))) - { - rtx tem = gen_reg_rtx (bl->biv->mode); - - record_base_value (REGNO (tem), bl->biv->add_val, 0); - emit_insn_before (gen_move_insn (tem, bl->biv->src_reg), - loop_start); - - if (loop_dump_stream) - fprintf (loop_dump_stream, "Biv %d initial value remapped to %d.\n", - bl->regno, REGNO (tem)); - - splittable_regs[bl->regno] = tem; - } - else - splittable_regs[bl->regno] = bl->initial_value; - } - else - splittable_regs[bl->regno] = const0_rtx; - - /* Save the number of instructions that modify the biv, so that - we can treat the last one specially. */ - - splittable_regs_updates[bl->regno] = bl->biv_count; - result += bl->biv_count; - - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Biv %d safe to split.\n", bl->regno); - } - - /* Check every giv that depends on this biv to see whether it is - splittable also. Even if the biv isn't splittable, givs which - depend on it may be splittable if the biv is live outside the - loop, and the givs aren't. */ - - result += find_splittable_givs (bl, unroll_type, loop_start, loop_end, - increment, unroll_number); - - /* If final value is non-zero, then must emit an instruction which sets - the value of the biv to the proper value. This is done after - handling all of the givs, since some of them may need to use the - biv's value in their initialization code. */ - if (biv_final_value) - { - /* If the loop has multiple exits, emit the insns before the - loop to ensure that it will always be executed no matter - how the loop exits. Otherwise emit the insn after the loop, - since this is slightly more efficient. */ - if (! loop_number_exit_count[uid_loop_num[INSN_UID (loop_start)]]) - emit_insn_before (gen_move_insn (bl->biv->src_reg, - biv_final_value), - end_insert_before); - else - { - /* Create a new register to hold the value of the biv, and then - set the biv to its final value before the loop start. The biv - is set to its final value before loop start to ensure that - this insn will always be executed, no matter how the loop - exits. */ - rtx tem = gen_reg_rtx (bl->biv->mode); - record_base_value (REGNO (tem), bl->biv->add_val, 0); - - emit_insn_before (gen_move_insn (tem, bl->biv->src_reg), - loop_start); - emit_insn_before (gen_move_insn (bl->biv->src_reg, - biv_final_value), - loop_start); - - if (loop_dump_stream) - fprintf (loop_dump_stream, "Biv %d mapped to %d for split.\n", - REGNO (bl->biv->src_reg), REGNO (tem)); - - /* Set up the mapping from the original biv register to the new - register. */ - bl->biv->src_reg = tem; - } - } - } - return result; -} - -/* Return 1 if the first and last unrolled copy of the address giv V is valid - for the instruction that is using it. Do not make any changes to that - instruction. */ - -static int -verify_addresses (v, giv_inc, unroll_number) - struct induction *v; - rtx giv_inc; - int unroll_number; -{ - int ret = 1; - rtx orig_addr = *v->location; - rtx last_addr = plus_constant (v->dest_reg, - INTVAL (giv_inc) * (unroll_number - 1)); - - /* First check to see if either address would fail. Handle the fact - that we have may have a match_dup. */ - if (! validate_replace_rtx (*v->location, v->dest_reg, v->insn) - || ! validate_replace_rtx (*v->location, last_addr, v->insn)) - ret = 0; - - /* Now put things back the way they were before. This should always - succeed. */ - if (! validate_replace_rtx (*v->location, orig_addr, v->insn)) - abort (); - - return ret; -} - -/* For every giv based on the biv BL, check to determine whether it is - splittable. This is a subroutine to find_splittable_regs (). - - Return the number of instructions that set splittable registers. */ - -static int -find_splittable_givs (bl, unroll_type, loop_start, loop_end, increment, - unroll_number) - struct iv_class *bl; - enum unroll_types unroll_type; - rtx loop_start, loop_end; - rtx increment; - int unroll_number; -{ - struct induction *v, *v2; - rtx final_value; - rtx tem; - int result = 0; - - /* Scan the list of givs, and set the same_insn field when there are - multiple identical givs in the same insn. */ - for (v = bl->giv; v; v = v->next_iv) - for (v2 = v->next_iv; v2; v2 = v2->next_iv) - if (v->insn == v2->insn && rtx_equal_p (v->new_reg, v2->new_reg) - && ! v2->same_insn) - v2->same_insn = v; - - for (v = bl->giv; v; v = v->next_iv) - { - rtx giv_inc, value; - - /* Only split the giv if it has already been reduced, or if the loop is - being completely unrolled. */ - if (unroll_type != UNROLL_COMPLETELY && v->ignore) - continue; - - /* The giv can be split if the insn that sets the giv is executed once - and only once on every iteration of the loop. */ - /* An address giv can always be split. v->insn is just a use not a set, - and hence it does not matter whether it is always executed. All that - matters is that all the biv increments are always executed, and we - won't reach here if they aren't. */ - if (v->giv_type != DEST_ADDR - && (! v->always_computable - || back_branch_in_range_p (v->insn, loop_start, loop_end))) - continue; - - /* The giv increment value must be a constant. */ - giv_inc = fold_rtx_mult_add (v->mult_val, increment, const0_rtx, - v->mode); - if (! giv_inc || GET_CODE (giv_inc) != CONST_INT) - continue; - - /* The loop must be unrolled completely, or else have a known number of - iterations and only one exit, or else the giv must be dead outside - the loop, or else the final value of the giv must be known. - Otherwise, it is not safe to split the giv since it may not have the - proper value on loop exit. */ - - /* The used outside loop test will fail for DEST_ADDR givs. They are - never used outside the loop anyways, so it is always safe to split a - DEST_ADDR giv. */ - - final_value = 0; - if (unroll_type != UNROLL_COMPLETELY - && (loop_number_exit_count[uid_loop_num[INSN_UID (loop_start)]] - || unroll_type == UNROLL_NAIVE) - && v->giv_type != DEST_ADDR - /* The next part is true if the pseudo is used outside the loop. - We assume that this is true for any pseudo created after loop - starts, because we don't have a reg_n_info entry for them. */ - && (REGNO (v->dest_reg) >= max_reg_before_loop - || (REGNO_FIRST_UID (REGNO (v->dest_reg)) != INSN_UID (v->insn) - /* Check for the case where the pseudo is set by a shift/add - sequence, in which case the first insn setting the pseudo - is the first insn of the shift/add sequence. */ - && (! (tem = find_reg_note (v->insn, REG_RETVAL, NULL_RTX)) - || (REGNO_FIRST_UID (REGNO (v->dest_reg)) - != INSN_UID (XEXP (tem, 0))))) - /* Line above always fails if INSN was moved by loop opt. */ - || (uid_luid[REGNO_LAST_UID (REGNO (v->dest_reg))] - >= INSN_LUID (loop_end))) - /* Givs made from biv increments are missed by the above test, so - test explicitly for them. */ - && (REGNO (v->dest_reg) < first_increment_giv - || REGNO (v->dest_reg) > last_increment_giv) - && ! (final_value = v->final_value)) - continue; - -#if 0 - /* Currently, non-reduced/final-value givs are never split. */ - /* Should emit insns after the loop if possible, as the biv final value - code below does. */ - - /* If the final value is non-zero, and the giv has not been reduced, - then must emit an instruction to set the final value. */ - if (final_value && !v->new_reg) - { - /* Create a new register to hold the value of the giv, and then set - the giv to its final value before the loop start. The giv is set - to its final value before loop start to ensure that this insn - will always be executed, no matter how we exit. */ - tem = gen_reg_rtx (v->mode); - emit_insn_before (gen_move_insn (tem, v->dest_reg), loop_start); - emit_insn_before (gen_move_insn (v->dest_reg, final_value), - loop_start); - - if (loop_dump_stream) - fprintf (loop_dump_stream, "Giv %d mapped to %d for split.\n", - REGNO (v->dest_reg), REGNO (tem)); - - v->src_reg = tem; - } -#endif - - /* This giv is splittable. If completely unrolling the loop, save the - giv's initial value. Otherwise, save the constant zero for it. */ - - if (unroll_type == UNROLL_COMPLETELY) - { - /* It is not safe to use bl->initial_value here, because it may not - be invariant. It is safe to use the initial value stored in - the splittable_regs array if it is set. In rare cases, it won't - be set, so then we do exactly the same thing as - find_splittable_regs does to get a safe value. */ - rtx biv_initial_value; - - if (splittable_regs[bl->regno]) - biv_initial_value = splittable_regs[bl->regno]; - else if (GET_CODE (bl->initial_value) != REG - || (REGNO (bl->initial_value) != bl->regno - && REGNO (bl->initial_value) >= FIRST_PSEUDO_REGISTER)) - biv_initial_value = bl->initial_value; - else - { - rtx tem = gen_reg_rtx (bl->biv->mode); - - record_base_value (REGNO (tem), bl->biv->add_val, 0); - emit_insn_before (gen_move_insn (tem, bl->biv->src_reg), - loop_start); - biv_initial_value = tem; - } - value = fold_rtx_mult_add (v->mult_val, biv_initial_value, - v->add_val, v->mode); - } - else - value = const0_rtx; - - if (v->new_reg) - { - /* If a giv was combined with another giv, then we can only split - this giv if the giv it was combined with was reduced. This - is because the value of v->new_reg is meaningless in this - case. */ - if (v->same && ! v->same->new_reg) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "giv combined with unreduced giv not split.\n"); - continue; - } - /* If the giv is an address destination, it could be something other - than a simple register, these have to be treated differently. */ - else if (v->giv_type == DEST_REG) - { - /* If value is not a constant, register, or register plus - constant, then compute its value into a register before - loop start. This prevents invalid rtx sharing, and should - generate better code. We can use bl->initial_value here - instead of splittable_regs[bl->regno] because this code - is going before the loop start. */ - if (unroll_type == UNROLL_COMPLETELY - && GET_CODE (value) != CONST_INT - && GET_CODE (value) != REG - && (GET_CODE (value) != PLUS - || GET_CODE (XEXP (value, 0)) != REG - || GET_CODE (XEXP (value, 1)) != CONST_INT)) - { - rtx tem = gen_reg_rtx (v->mode); - record_base_value (REGNO (tem), v->add_val, 0); - emit_iv_add_mult (bl->initial_value, v->mult_val, - v->add_val, tem, loop_start); - value = tem; - } - - splittable_regs[REGNO (v->new_reg)] = value; - derived_regs[REGNO (v->new_reg)] = v->derived_from != 0; - } - else - { - /* Splitting address givs is useful since it will often allow us - to eliminate some increment insns for the base giv as - unnecessary. */ - - /* If the addr giv is combined with a dest_reg giv, then all - references to that dest reg will be remapped, which is NOT - what we want for split addr regs. We always create a new - register for the split addr giv, just to be safe. */ - - /* If we have multiple identical address givs within a - single instruction, then use a single pseudo reg for - both. This is necessary in case one is a match_dup - of the other. */ - - v->const_adjust = 0; - - if (v->same_insn) - { - v->dest_reg = v->same_insn->dest_reg; - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Sharing address givs in insn %d\n", - INSN_UID (v->insn)); - } - /* If multiple address GIVs have been combined with the - same dest_reg GIV, do not create a new register for - each. */ - else if (unroll_type != UNROLL_COMPLETELY - && v->giv_type == DEST_ADDR - && v->same && v->same->giv_type == DEST_ADDR - && v->same->unrolled - /* combine_givs_p may return true for some cases - where the add and mult values are not equal. - To share a register here, the values must be - equal. */ - && rtx_equal_p (v->same->mult_val, v->mult_val) - && rtx_equal_p (v->same->add_val, v->add_val)) - - { - v->dest_reg = v->same->dest_reg; - v->shared = 1; - } - else if (unroll_type != UNROLL_COMPLETELY) - { - /* If not completely unrolling the loop, then create a new - register to hold the split value of the DEST_ADDR giv. - Emit insn to initialize its value before loop start. */ - - rtx tem = gen_reg_rtx (v->mode); - struct induction *same = v->same; - rtx new_reg = v->new_reg; - record_base_value (REGNO (tem), v->add_val, 0); - - if (same && same->derived_from) - { - /* calculate_giv_inc doesn't work for derived givs. - copy_loop_body works around the problem for the - DEST_REG givs themselves, but it can't handle - DEST_ADDR givs that have been combined with - a derived DEST_REG giv. - So Handle V as if the giv from which V->SAME has - been derived has been combined with V. - recombine_givs only derives givs from givs that - are reduced the ordinary, so we need not worry - about same->derived_from being in turn derived. */ - - same = same->derived_from; - new_reg = express_from (same, v); - new_reg = replace_rtx (new_reg, same->dest_reg, - same->new_reg); - } - - /* If the address giv has a constant in its new_reg value, - then this constant can be pulled out and put in value, - instead of being part of the initialization code. */ - - if (GET_CODE (new_reg) == PLUS - && GET_CODE (XEXP (new_reg, 1)) == CONST_INT) - { - v->dest_reg - = plus_constant (tem, INTVAL (XEXP (new_reg, 1))); - - /* Only succeed if this will give valid addresses. - Try to validate both the first and the last - address resulting from loop unrolling, if - one fails, then can't do const elim here. */ - if (verify_addresses (v, giv_inc, unroll_number)) - { - /* Save the negative of the eliminated const, so - that we can calculate the dest_reg's increment - value later. */ - v->const_adjust = - INTVAL (XEXP (new_reg, 1)); - - new_reg = XEXP (new_reg, 0); - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Eliminating constant from giv %d\n", - REGNO (tem)); - } - else - v->dest_reg = tem; - } - else - v->dest_reg = tem; - - /* If the address hasn't been checked for validity yet, do so - now, and fail completely if either the first or the last - unrolled copy of the address is not a valid address - for the instruction that uses it. */ - if (v->dest_reg == tem - && ! verify_addresses (v, giv_inc, unroll_number)) - { - for (v2 = v->next_iv; v2; v2 = v2->next_iv) - if (v2->same_insn == v) - v2->same_insn = 0; - - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Invalid address for giv at insn %d\n", - INSN_UID (v->insn)); - continue; - } - - v->new_reg = new_reg; - v->same = same; - - /* We set this after the address check, to guarantee that - the register will be initialized. */ - v->unrolled = 1; - - /* To initialize the new register, just move the value of - new_reg into it. This is not guaranteed to give a valid - instruction on machines with complex addressing modes. - If we can't recognize it, then delete it and emit insns - to calculate the value from scratch. */ - emit_insn_before (gen_rtx_SET (VOIDmode, tem, - copy_rtx (v->new_reg)), - loop_start); - if (recog_memoized (PREV_INSN (loop_start)) < 0) - { - rtx sequence, ret; - - /* We can't use bl->initial_value to compute the initial - value, because the loop may have been preconditioned. - We must calculate it from NEW_REG. Try using - force_operand instead of emit_iv_add_mult. */ - delete_insn (PREV_INSN (loop_start)); - - start_sequence (); - ret = force_operand (v->new_reg, tem); - if (ret != tem) - emit_move_insn (tem, ret); - sequence = gen_sequence (); - end_sequence (); - emit_insn_before (sequence, loop_start); - - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Invalid init insn, rewritten.\n"); - } - } - else - { - v->dest_reg = value; - - /* Check the resulting address for validity, and fail - if the resulting address would be invalid. */ - if (! verify_addresses (v, giv_inc, unroll_number)) - { - for (v2 = v->next_iv; v2; v2 = v2->next_iv) - if (v2->same_insn == v) - v2->same_insn = 0; - - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Invalid address for giv at insn %d\n", - INSN_UID (v->insn)); - continue; - } - if (v->same && v->same->derived_from) - { - /* Handle V as if the giv from which V->SAME has - been derived has been combined with V. */ - - v->same = v->same->derived_from; - v->new_reg = express_from (v->same, v); - v->new_reg = replace_rtx (v->new_reg, v->same->dest_reg, - v->same->new_reg); - } - - } - - /* Store the value of dest_reg into the insn. This sharing - will not be a problem as this insn will always be copied - later. */ - - *v->location = v->dest_reg; - - /* If this address giv is combined with a dest reg giv, then - save the base giv's induction pointer so that we will be - able to handle this address giv properly. The base giv - itself does not have to be splittable. */ - - if (v->same && v->same->giv_type == DEST_REG) - addr_combined_regs[REGNO (v->same->new_reg)] = v->same; - - if (GET_CODE (v->new_reg) == REG) - { - /* This giv maybe hasn't been combined with any others. - Make sure that it's giv is marked as splittable here. */ - - splittable_regs[REGNO (v->new_reg)] = value; - derived_regs[REGNO (v->new_reg)] = v->derived_from != 0; - - /* Make it appear to depend upon itself, so that the - giv will be properly split in the main loop above. */ - if (! v->same) - { - v->same = v; - addr_combined_regs[REGNO (v->new_reg)] = v; - } - } - - if (loop_dump_stream) - fprintf (loop_dump_stream, "DEST_ADDR giv being split.\n"); - } - } - else - { -#if 0 - /* Currently, unreduced giv's can't be split. This is not too much - of a problem since unreduced giv's are not live across loop - iterations anyways. When unrolling a loop completely though, - it makes sense to reduce&split givs when possible, as this will - result in simpler instructions, and will not require that a reg - be live across loop iterations. */ - - splittable_regs[REGNO (v->dest_reg)] = value; - fprintf (stderr, "Giv %d at insn %d not reduced\n", - REGNO (v->dest_reg), INSN_UID (v->insn)); -#else - continue; -#endif - } - - /* Unreduced givs are only updated once by definition. Reduced givs - are updated as many times as their biv is. Mark it so if this is - a splittable register. Don't need to do anything for address givs - where this may not be a register. */ - - if (GET_CODE (v->new_reg) == REG) - { - int count = 1; - if (! v->ignore) - count = reg_biv_class[REGNO (v->src_reg)]->biv_count; - - if (count > 1 && v->derived_from) - /* In this case, there is one set where the giv insn was and one - set each after each biv increment. (Most are likely dead.) */ - count++; - - splittable_regs_updates[REGNO (v->new_reg)] = count; - } - - result++; - - if (loop_dump_stream) - { - int regnum; - - if (GET_CODE (v->dest_reg) == CONST_INT) - regnum = -1; - else if (GET_CODE (v->dest_reg) != REG) - regnum = REGNO (XEXP (v->dest_reg, 0)); - else - regnum = REGNO (v->dest_reg); - fprintf (loop_dump_stream, "Giv %d at insn %d safe to split.\n", - regnum, INSN_UID (v->insn)); - } - } - - return result; -} - -/* Try to prove that the register is dead after the loop exits. Trace every - loop exit looking for an insn that will always be executed, which sets - the register to some value, and appears before the first use of the register - is found. If successful, then return 1, otherwise return 0. */ - -/* ?? Could be made more intelligent in the handling of jumps, so that - it can search past if statements and other similar structures. */ - -static int -reg_dead_after_loop (reg, loop_start, loop_end) - rtx reg, loop_start, loop_end; -{ - rtx insn, label; - enum rtx_code code; - int jump_count = 0; - int label_count = 0; - int this_loop_num = uid_loop_num[INSN_UID (loop_start)]; - - /* In addition to checking all exits of this loop, we must also check - all exits of inner nested loops that would exit this loop. We don't - have any way to identify those, so we just give up if there are any - such inner loop exits. */ - - for (label = loop_number_exit_labels[this_loop_num]; label; - label = LABEL_NEXTREF (label)) - label_count++; - - if (label_count != loop_number_exit_count[this_loop_num]) - return 0; - - /* HACK: Must also search the loop fall through exit, create a label_ref - here which points to the loop_end, and append the loop_number_exit_labels - list to it. */ - label = gen_rtx_LABEL_REF (VOIDmode, loop_end); - LABEL_NEXTREF (label) = loop_number_exit_labels[this_loop_num]; - - for ( ; label; label = LABEL_NEXTREF (label)) - { - /* Succeed if find an insn which sets the biv or if reach end of - function. Fail if find an insn that uses the biv, or if come to - a conditional jump. */ - - insn = NEXT_INSN (XEXP (label, 0)); - while (insn) - { - code = GET_CODE (insn); - if (GET_RTX_CLASS (code) == 'i') - { - rtx set; - - if (reg_referenced_p (reg, PATTERN (insn))) - return 0; - - set = single_set (insn); - if (set && rtx_equal_p (SET_DEST (set), reg)) - break; - } - - if (code == JUMP_INSN) - { - if (GET_CODE (PATTERN (insn)) == RETURN) - break; - else if (! simplejump_p (insn) - /* Prevent infinite loop following infinite loops. */ - || jump_count++ > 20) - return 0; - else - insn = JUMP_LABEL (insn); - } - - insn = NEXT_INSN (insn); - } - } - - /* Success, the register is dead on all loop exits. */ - return 1; -} - -/* Try to calculate the final value of the biv, the value it will have at - the end of the loop. If we can do it, return that value. */ - -rtx -final_biv_value (bl, loop_start, loop_end, n_iterations) - struct iv_class *bl; - rtx loop_start, loop_end; - unsigned HOST_WIDE_INT n_iterations; -{ - rtx increment, tem; - - /* ??? This only works for MODE_INT biv's. Reject all others for now. */ - - if (GET_MODE_CLASS (bl->biv->mode) != MODE_INT) - return 0; - - /* The final value for reversed bivs must be calculated differently than - for ordinary bivs. In this case, there is already an insn after the - loop which sets this biv's final value (if necessary), and there are - no other loop exits, so we can return any value. */ - if (bl->reversed) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Final biv value for %d, reversed biv.\n", bl->regno); - - return const0_rtx; - } - - /* Try to calculate the final value as initial value + (number of iterations - * increment). For this to work, increment must be invariant, the only - exit from the loop must be the fall through at the bottom (otherwise - it may not have its final value when the loop exits), and the initial - value of the biv must be invariant. */ - - if (n_iterations != 0 - && ! loop_number_exit_count[uid_loop_num[INSN_UID (loop_start)]] - && invariant_p (bl->initial_value)) - { - increment = biv_total_increment (bl, loop_start, loop_end); - - if (increment && invariant_p (increment)) - { - /* Can calculate the loop exit value, emit insns after loop - end to calculate this value into a temporary register in - case it is needed later. */ - - tem = gen_reg_rtx (bl->biv->mode); - record_base_value (REGNO (tem), bl->biv->add_val, 0); - /* Make sure loop_end is not the last insn. */ - if (NEXT_INSN (loop_end) == 0) - emit_note_after (NOTE_INSN_DELETED, loop_end); - emit_iv_add_mult (increment, GEN_INT (n_iterations), - bl->initial_value, tem, NEXT_INSN (loop_end)); - - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Final biv value for %d, calculated.\n", bl->regno); - - return tem; - } - } - - /* Check to see if the biv is dead at all loop exits. */ - if (reg_dead_after_loop (bl->biv->src_reg, loop_start, loop_end)) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Final biv value for %d, biv dead after loop exit.\n", - bl->regno); - - return const0_rtx; - } - - return 0; -} - -/* Try to calculate the final value of the giv, the value it will have at - the end of the loop. If we can do it, return that value. */ - -rtx -final_giv_value (v, loop_start, loop_end, n_iterations) - struct induction *v; - rtx loop_start, loop_end; - unsigned HOST_WIDE_INT n_iterations; -{ - struct iv_class *bl; - rtx insn; - rtx increment, tem; - rtx insert_before, seq; - - bl = reg_biv_class[REGNO (v->src_reg)]; - - /* The final value for givs which depend on reversed bivs must be calculated - differently than for ordinary givs. In this case, there is already an - insn after the loop which sets this giv's final value (if necessary), - and there are no other loop exits, so we can return any value. */ - if (bl->reversed) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Final giv value for %d, depends on reversed biv\n", - REGNO (v->dest_reg)); - return const0_rtx; - } - - /* Try to calculate the final value as a function of the biv it depends - upon. The only exit from the loop must be the fall through at the bottom - (otherwise it may not have its final value when the loop exits). */ - - /* ??? Can calculate the final giv value by subtracting off the - extra biv increments times the giv's mult_val. The loop must have - only one exit for this to work, but the loop iterations does not need - to be known. */ - - if (n_iterations != 0 - && ! loop_number_exit_count[uid_loop_num[INSN_UID (loop_start)]]) - { - /* ?? It is tempting to use the biv's value here since these insns will - be put after the loop, and hence the biv will have its final value - then. However, this fails if the biv is subsequently eliminated. - Perhaps determine whether biv's are eliminable before trying to - determine whether giv's are replaceable so that we can use the - biv value here if it is not eliminable. */ - - /* We are emitting code after the end of the loop, so we must make - sure that bl->initial_value is still valid then. It will still - be valid if it is invariant. */ - - increment = biv_total_increment (bl, loop_start, loop_end); - - if (increment && invariant_p (increment) - && invariant_p (bl->initial_value)) - { - /* Can calculate the loop exit value of its biv as - (n_iterations * increment) + initial_value */ - - /* The loop exit value of the giv is then - (final_biv_value - extra increments) * mult_val + add_val. - The extra increments are any increments to the biv which - occur in the loop after the giv's value is calculated. - We must search from the insn that sets the giv to the end - of the loop to calculate this value. */ - - insert_before = NEXT_INSN (loop_end); - - /* Put the final biv value in tem. */ - tem = gen_reg_rtx (bl->biv->mode); - record_base_value (REGNO (tem), bl->biv->add_val, 0); - emit_iv_add_mult (increment, GEN_INT (n_iterations), - bl->initial_value, tem, insert_before); - - /* Subtract off extra increments as we find them. */ - for (insn = NEXT_INSN (v->insn); insn != loop_end; - insn = NEXT_INSN (insn)) - { - struct induction *biv; - - for (biv = bl->biv; biv; biv = biv->next_iv) - if (biv->insn == insn) - { - start_sequence (); - tem = expand_binop (GET_MODE (tem), sub_optab, tem, - biv->add_val, NULL_RTX, 0, - OPTAB_LIB_WIDEN); - seq = gen_sequence (); - end_sequence (); - emit_insn_before (seq, insert_before); - } - } - - /* Now calculate the giv's final value. */ - emit_iv_add_mult (tem, v->mult_val, v->add_val, tem, - insert_before); - - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Final giv value for %d, calc from biv's value.\n", - REGNO (v->dest_reg)); - - return tem; - } - } - - /* Replaceable giv's should never reach here. */ - if (v->replaceable) - abort (); - - /* Check to see if the biv is dead at all loop exits. */ - if (reg_dead_after_loop (v->dest_reg, loop_start, loop_end)) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Final giv value for %d, giv dead after loop exit.\n", - REGNO (v->dest_reg)); - - return const0_rtx; - } - - return 0; -} - - -/* Look back before LOOP_START for then insn that sets REG and return - the equivalent constant if there is a REG_EQUAL note otherwise just - the SET_SRC of REG. */ - -static rtx -loop_find_equiv_value (loop_start, reg) - rtx loop_start; - rtx reg; -{ - rtx insn, set; - rtx ret; - - ret = reg; - for (insn = PREV_INSN (loop_start); insn ; insn = PREV_INSN (insn)) - { - if (GET_CODE (insn) == CODE_LABEL) - break; - - else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' - && reg_set_p (reg, insn)) - { - /* We found the last insn before the loop that sets the register. - If it sets the entire register, and has a REG_EQUAL note, - then use the value of the REG_EQUAL note. */ - if ((set = single_set (insn)) - && (SET_DEST (set) == reg)) - { - rtx note = find_reg_note (insn, REG_EQUAL, NULL_RTX); - - /* Only use the REG_EQUAL note if it is a constant. - Other things, divide in particular, will cause - problems later if we use them. */ - if (note && GET_CODE (XEXP (note, 0)) != EXPR_LIST - && CONSTANT_P (XEXP (note, 0))) - ret = XEXP (note, 0); - else - ret = SET_SRC (set); - } - break; - } - } - return ret; -} - - -/* Return a simplified rtx for the expression OP - REG. - - REG must appear in OP, and OP must be a register or the sum of a register - and a second term. - - Thus, the return value must be const0_rtx or the second term. - - The caller is responsible for verifying that REG appears in OP and OP has - the proper form. */ - -static rtx -subtract_reg_term (op, reg) - rtx op, reg; -{ - if (op == reg) - return const0_rtx; - if (GET_CODE (op) == PLUS) - { - if (XEXP (op, 0) == reg) - return XEXP (op, 1); - else if (XEXP (op, 1) == reg) - return XEXP (op, 0); - } - /* OP does not contain REG as a term. */ - abort (); -} - - -/* Find and return register term common to both expressions OP0 and - OP1 or NULL_RTX if no such term exists. Each expression must be a - REG or a PLUS of a REG. */ - -static rtx -find_common_reg_term (op0, op1) - rtx op0, op1; -{ - if ((GET_CODE (op0) == REG || GET_CODE (op0) == PLUS) - && (GET_CODE (op1) == REG || GET_CODE (op1) == PLUS)) - { - rtx op00; - rtx op01; - rtx op10; - rtx op11; - - if (GET_CODE (op0) == PLUS) - op01 = XEXP (op0, 1), op00 = XEXP (op0, 0); - else - op01 = const0_rtx, op00 = op0; - - if (GET_CODE (op1) == PLUS) - op11 = XEXP (op1, 1), op10 = XEXP (op1, 0); - else - op11 = const0_rtx, op10 = op1; - - /* Find and return common register term if present. */ - if (REG_P (op00) && (op00 == op10 || op00 == op11)) - return op00; - else if (REG_P (op01) && (op01 == op10 || op01 == op11)) - return op01; - } - - /* No common register term found. */ - return NULL_RTX; -} - - -/* Calculate the number of loop iterations. Returns the exact number of loop - iterations if it can be calculated, otherwise returns zero. */ - -unsigned HOST_WIDE_INT -loop_iterations (loop_start, loop_end, loop_info) - rtx loop_start, loop_end; - struct loop_info *loop_info; -{ - rtx comparison, comparison_value; - rtx iteration_var, initial_value, increment, final_value; - enum rtx_code comparison_code; - HOST_WIDE_INT abs_inc; - unsigned HOST_WIDE_INT abs_diff; - int off_by_one; - int increment_dir; - int unsigned_p, compare_dir, final_larger; - rtx last_loop_insn; - rtx vtop; - rtx reg_term; - - loop_info->n_iterations = 0; - loop_info->initial_value = 0; - loop_info->initial_equiv_value = 0; - loop_info->comparison_value = 0; - loop_info->final_value = 0; - loop_info->final_equiv_value = 0; - loop_info->increment = 0; - loop_info->iteration_var = 0; - loop_info->unroll_number = 1; - loop_info->vtop = 0; - - /* First find the iteration variable. If the last insn is a conditional - branch, and the insn before tests a register value, make that the - iteration variable. */ - - /* We used to use prev_nonnote_insn here, but that fails because it might - accidentally get the branch for a contained loop if the branch for this - loop was deleted. We can only trust branches immediately before the - loop_end. */ - last_loop_insn = PREV_INSN (loop_end); - - comparison = get_condition_for_loop (last_loop_insn); - if (comparison == 0) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Loop iterations: No final conditional branch found.\n"); - return 0; - } - - /* ??? Get_condition may switch position of induction variable and - invariant register when it canonicalizes the comparison. */ - - comparison_code = GET_CODE (comparison); - iteration_var = XEXP (comparison, 0); - comparison_value = XEXP (comparison, 1); - - /* Check if there is a NOTE_INSN_LOOP_VTOP note. If there is, - that means that this is a for or while style loop, with - a loop exit test at the start. Thus, we can assume that - the loop condition was true when the loop was entered. - - We start at the end and search backwards for the previous - NOTE. If there is no NOTE_INSN_LOOP_VTOP for this loop, - the search will stop at the NOTE_INSN_LOOP_CONT. */ - vtop = loop_end; - do - vtop = PREV_INSN (vtop); - while (GET_CODE (vtop) != NOTE - || NOTE_LINE_NUMBER (vtop) > 0 - || NOTE_LINE_NUMBER (vtop) == NOTE_REPEATED_LINE_NUMBER - || NOTE_LINE_NUMBER (vtop) == NOTE_INSN_DELETED); - if (NOTE_LINE_NUMBER (vtop) != NOTE_INSN_LOOP_VTOP) - vtop = NULL_RTX; - loop_info->vtop = vtop; - - if (GET_CODE (iteration_var) != REG) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Loop iterations: Comparison not against register.\n"); - return 0; - } - - /* Loop iterations is always called before any new registers are created - now, so this should never occur. */ - - if (REGNO (iteration_var) >= max_reg_before_loop) - abort (); - - iteration_info (iteration_var, &initial_value, &increment, - loop_start, loop_end); - if (initial_value == 0) - /* iteration_info already printed a message. */ - return 0; - - unsigned_p = 0; - off_by_one = 0; - switch (comparison_code) - { - case LEU: - unsigned_p = 1; - case LE: - compare_dir = 1; - off_by_one = 1; - break; - case GEU: - unsigned_p = 1; - case GE: - compare_dir = -1; - off_by_one = -1; - break; - case EQ: - /* Cannot determine loop iterations with this case. */ - compare_dir = 0; - break; - case LTU: - unsigned_p = 1; - case LT: - compare_dir = 1; - break; - case GTU: - unsigned_p = 1; - case GT: - compare_dir = -1; - case NE: - compare_dir = 0; - break; - default: - abort (); - } - - /* If the comparison value is an invariant register, then try to find - its value from the insns before the start of the loop. */ - - final_value = comparison_value; - if (GET_CODE (comparison_value) == REG && invariant_p (comparison_value)) - { - final_value = loop_find_equiv_value (loop_start, comparison_value); - /* If we don't get an invariant final value, we are better - off with the original register. */ - if (!invariant_p (final_value)) - final_value = comparison_value; - } - - /* Calculate the approximate final value of the induction variable - (on the last successful iteration). The exact final value - depends on the branch operator, and increment sign. It will be - wrong if the iteration variable is not incremented by one each - time through the loop and (comparison_value + off_by_one - - initial_value) % increment != 0. - ??? Note that the final_value may overflow and thus final_larger - will be bogus. A potentially infinite loop will be classified - as immediate, e.g. for (i = 0x7ffffff0; i <= 0x7fffffff; i++) */ - if (off_by_one) - final_value = plus_constant (final_value, off_by_one); - - /* Save the calculated values describing this loop's bounds, in case - precondition_loop_p will need them later. These values can not be - recalculated inside precondition_loop_p because strength reduction - optimizations may obscure the loop's structure. - - These values are only required by precondition_loop_p and insert_bct - whenever the number of iterations cannot be computed at compile time. - Only the difference between final_value and initial_value is - important. Note that final_value is only approximate. */ - loop_info->initial_value = initial_value; - loop_info->comparison_value = comparison_value; - loop_info->final_value = plus_constant (comparison_value, off_by_one); - loop_info->increment = increment; - loop_info->iteration_var = iteration_var; - loop_info->comparison_code = comparison_code; - - /* Try to determine the iteration count for loops such - as (for i = init; i < init + const; i++). When running the - loop optimization twice, the first pass often converts simple - loops into this form. */ - - if (REG_P (initial_value)) - { - rtx reg1; - rtx reg2; - rtx const2; - - reg1 = initial_value; - if (GET_CODE (final_value) == PLUS) - reg2 = XEXP (final_value, 0), const2 = XEXP (final_value, 1); - else - reg2 = final_value, const2 = const0_rtx; - - /* Check for initial_value = reg1, final_value = reg2 + const2, - where reg1 != reg2. */ - if (REG_P (reg2) && reg2 != reg1) - { - rtx temp; - - /* Find what reg1 is equivalent to. Hopefully it will - either be reg2 or reg2 plus a constant. */ - temp = loop_find_equiv_value (loop_start, reg1); - if (find_common_reg_term (temp, reg2)) - initial_value = temp; - else - { - /* Find what reg2 is equivalent to. Hopefully it will - either be reg1 or reg1 plus a constant. Let's ignore - the latter case for now since it is not so common. */ - temp = loop_find_equiv_value (loop_start, reg2); - if (temp == loop_info->iteration_var) - temp = initial_value; - if (temp == reg1) - final_value = (const2 == const0_rtx) - ? reg1 : gen_rtx_PLUS (GET_MODE (reg1), reg1, const2); - } - } - else if (loop_info->vtop && GET_CODE (reg2) == CONST_INT) - { - rtx temp; - - /* When running the loop optimizer twice, check_dbra_loop - further obfuscates reversible loops of the form: - for (i = init; i < init + const; i++). We often end up with - final_value = 0, initial_value = temp, temp = temp2 - init, - where temp2 = init + const. If the loop has a vtop we - can replace initial_value with const. */ - - temp = loop_find_equiv_value (loop_start, reg1); - if (GET_CODE (temp) == MINUS && REG_P (XEXP (temp, 0))) - { - rtx temp2 = loop_find_equiv_value (loop_start, XEXP (temp, 0)); - if (GET_CODE (temp2) == PLUS - && XEXP (temp2, 0) == XEXP (temp, 1)) - initial_value = XEXP (temp2, 1); - } - } - } - - /* If have initial_value = reg + const1 and final_value = reg + - const2, then replace initial_value with const1 and final_value - with const2. This should be safe since we are protected by the - initial comparison before entering the loop if we have a vtop. - For example, a + b < a + c is not equivalent to b < c for all a - when using modulo arithmetic. - - ??? Without a vtop we could still perform the optimization if we check - the initial and final values carefully. */ - if (loop_info->vtop - && (reg_term = find_common_reg_term (initial_value, final_value))) - { - initial_value = subtract_reg_term (initial_value, reg_term); - final_value = subtract_reg_term (final_value, reg_term); - } - - loop_info->initial_equiv_value = initial_value; - loop_info->final_equiv_value = final_value; - - if (increment == 0) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Loop iterations: Increment value can't be calculated.\n"); - return 0; - } - - if (GET_CODE (increment) != CONST_INT) - { - increment = loop_find_equiv_value (loop_start, increment); - - if (GET_CODE (increment) != CONST_INT) - { - if (loop_dump_stream) - { - fprintf (loop_dump_stream, - "Loop iterations: Increment value not constant "); - print_rtl (loop_dump_stream, increment); - fprintf (loop_dump_stream, ".\n"); - } - return 0; - } - loop_info->increment = increment; - } - - if (GET_CODE (initial_value) != CONST_INT) - { - if (loop_dump_stream) - { - fprintf (loop_dump_stream, - "Loop iterations: Initial value not constant "); - print_rtl (loop_dump_stream, initial_value); - fprintf (loop_dump_stream, ".\n"); - } - return 0; - } - else if (comparison_code == EQ) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Loop iterations: EQ comparison loop.\n"); - return 0; - } - else if (GET_CODE (final_value) != CONST_INT) - { - if (loop_dump_stream) - { - fprintf (loop_dump_stream, - "Loop iterations: Final value not constant "); - print_rtl (loop_dump_stream, final_value); - fprintf (loop_dump_stream, ".\n"); - } - return 0; - } - - /* Final_larger is 1 if final larger, 0 if they are equal, otherwise -1. */ - if (unsigned_p) - final_larger - = ((unsigned HOST_WIDE_INT) INTVAL (final_value) - > (unsigned HOST_WIDE_INT) INTVAL (initial_value)) - - ((unsigned HOST_WIDE_INT) INTVAL (final_value) - < (unsigned HOST_WIDE_INT) INTVAL (initial_value)); - else - final_larger = (INTVAL (final_value) > INTVAL (initial_value)) - - (INTVAL (final_value) < INTVAL (initial_value)); - - if (INTVAL (increment) > 0) - increment_dir = 1; - else if (INTVAL (increment) == 0) - increment_dir = 0; - else - increment_dir = -1; - - /* There are 27 different cases: compare_dir = -1, 0, 1; - final_larger = -1, 0, 1; increment_dir = -1, 0, 1. - There are 4 normal cases, 4 reverse cases (where the iteration variable - will overflow before the loop exits), 4 infinite loop cases, and 15 - immediate exit (0 or 1 iteration depending on loop type) cases. - Only try to optimize the normal cases. */ - - /* (compare_dir/final_larger/increment_dir) - Normal cases: (0/-1/-1), (0/1/1), (-1/-1/-1), (1/1/1) - Reverse cases: (0/-1/1), (0/1/-1), (-1/-1/1), (1/1/-1) - Infinite loops: (0/-1/0), (0/1/0), (-1/-1/0), (1/1/0) - Immediate exit: (0/0/X), (-1/0/X), (-1/1/X), (1/0/X), (1/-1/X) */ - - /* ?? If the meaning of reverse loops (where the iteration variable - will overflow before the loop exits) is undefined, then could - eliminate all of these special checks, and just always assume - the loops are normal/immediate/infinite. Note that this means - the sign of increment_dir does not have to be known. Also, - since it does not really hurt if immediate exit loops or infinite loops - are optimized, then that case could be ignored also, and hence all - loops can be optimized. - - According to ANSI Spec, the reverse loop case result is undefined, - because the action on overflow is undefined. - - See also the special test for NE loops below. */ - - if (final_larger == increment_dir && final_larger != 0 - && (final_larger == compare_dir || compare_dir == 0)) - /* Normal case. */ - ; - else - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Loop iterations: Not normal loop.\n"); - return 0; - } - - /* Calculate the number of iterations, final_value is only an approximation, - so correct for that. Note that abs_diff and n_iterations are - unsigned, because they can be as large as 2^n - 1. */ - - abs_inc = INTVAL (increment); - if (abs_inc > 0) - abs_diff = INTVAL (final_value) - INTVAL (initial_value); - else if (abs_inc < 0) - { - abs_diff = INTVAL (initial_value) - INTVAL (final_value); - abs_inc = -abs_inc; - } - else - abort (); - - /* For NE tests, make sure that the iteration variable won't miss - the final value. If abs_diff mod abs_incr is not zero, then the - iteration variable will overflow before the loop exits, and we - can not calculate the number of iterations. */ - if (compare_dir == 0 && (abs_diff % abs_inc) != 0) - return 0; - - /* Note that the number of iterations could be calculated using - (abs_diff + abs_inc - 1) / abs_inc, provided care was taken to - handle potential overflow of the summation. */ - loop_info->n_iterations = abs_diff / abs_inc + ((abs_diff % abs_inc) != 0); - return loop_info->n_iterations; -} - - -/* Replace uses of split bivs with their split pseudo register. This is - for original instructions which remain after loop unrolling without - copying. */ - -static rtx -remap_split_bivs (x) - rtx x; -{ - register enum rtx_code code; - register int i; - register char *fmt; - - if (x == 0) - return x; - - code = GET_CODE (x); - switch (code) - { - case SCRATCH: - case PC: - case CC0: - case CONST_INT: - case CONST_DOUBLE: - case CONST: - case SYMBOL_REF: - case LABEL_REF: - return x; - - case REG: -#if 0 - /* If non-reduced/final-value givs were split, then this would also - have to remap those givs also. */ -#endif - if (REGNO (x) < max_reg_before_loop - && REG_IV_TYPE (REGNO (x)) == BASIC_INDUCT) - return reg_biv_class[REGNO (x)]->biv->src_reg; - break; - - default: - break; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - XEXP (x, i) = remap_split_bivs (XEXP (x, i)); - if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - XVECEXP (x, i, j) = remap_split_bivs (XVECEXP (x, i, j)); - } - } - return x; -} - -/* If FIRST_UID is a set of REGNO, and FIRST_UID dominates LAST_UID (e.g. - FIST_UID is always executed if LAST_UID is), then return 1. Otherwise - return 0. COPY_START is where we can start looking for the insns - FIRST_UID and LAST_UID. COPY_END is where we stop looking for these - insns. - - If there is no JUMP_INSN between LOOP_START and FIRST_UID, then FIRST_UID - must dominate LAST_UID. - - If there is a CODE_LABEL between FIRST_UID and LAST_UID, then FIRST_UID - may not dominate LAST_UID. - - If there is no CODE_LABEL between FIRST_UID and LAST_UID, then FIRST_UID - must dominate LAST_UID. */ - -int -set_dominates_use (regno, first_uid, last_uid, copy_start, copy_end) - int regno; - int first_uid; - int last_uid; - rtx copy_start; - rtx copy_end; -{ - int passed_jump = 0; - rtx p = NEXT_INSN (copy_start); - - while (INSN_UID (p) != first_uid) - { - if (GET_CODE (p) == JUMP_INSN) - passed_jump= 1; - /* Could not find FIRST_UID. */ - if (p == copy_end) - return 0; - p = NEXT_INSN (p); - } - - /* Verify that FIRST_UID is an insn that entirely sets REGNO. */ - if (GET_RTX_CLASS (GET_CODE (p)) != 'i' - || ! dead_or_set_regno_p (p, regno)) - return 0; - - /* FIRST_UID is always executed. */ - if (passed_jump == 0) - return 1; - - while (INSN_UID (p) != last_uid) - { - /* If we see a CODE_LABEL between FIRST_UID and LAST_UID, then we - can not be sure that FIRST_UID dominates LAST_UID. */ - if (GET_CODE (p) == CODE_LABEL) - return 0; - /* Could not find LAST_UID, but we reached the end of the loop, so - it must be safe. */ - else if (p == copy_end) - return 1; - p = NEXT_INSN (p); - } - - /* FIRST_UID is always executed if LAST_UID is executed. */ - return 1; -} |