summaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rwxr-xr-xgcc/dwarf2out_020422.c9925
-rwxr-xr-xgcc/function.BAK6650
-rwxr-xr-xgcc/function_990206.c6578
-rwxr-xr-xgcc/rtl_020422.c935
-rwxr-xr-xgcc/rtl_020422.h1569
-rwxr-xr-xgcc/unroll_991002.c4045
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 (&REG_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 (&REG_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 (&REG_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 (&REG_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;
-}