914 lines
22 KiB
C
914 lines
22 KiB
C
/* Allocate and read RTL for GNU C Compiler.
|
||
Copyright (C) 1987, 1988, 1991, 1994, 1997 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] = {
|
||
#include "machmode.def"
|
||
|
||
#ifdef EXTRA_CC_MODES
|
||
EXTRA_CC_NAMES
|
||
#endif
|
||
|
||
};
|
||
|
||
#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) \
|
||
(enum machine_mode) WIDER,
|
||
|
||
enum machine_mode mode_wider_mode[(int) MAX_MACHINE_MODE] = {
|
||
#include "machmode.def" /* machine modes are documented here */
|
||
};
|
||
|
||
#undef DEF_MACHMODE
|
||
|
||
/* 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_BR_PROB",
|
||
"REG_EXEC_COUNT", "REG_NOALIAS", "REG_SAVE_AREA",
|
||
"REG_BR_PRED", "REG_EH_CONTEXT" };
|
||
|
||
static void dump_and_abort PROTO((int, int, FILE *));
|
||
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. */
|
||
|
||
length = (sizeof (struct rtx_def) - sizeof (rtunion) - 1) / sizeof (int);
|
||
for (; length >= 0; length--)
|
||
((int *) rt)[length] = 0;
|
||
|
||
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;
|
||
{
|
||
register rtx copy;
|
||
register int i, j;
|
||
register RTX_CODE code;
|
||
register char *format_ptr;
|
||
|
||
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:
|
||
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;
|
||
}
|
||
|
||
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_size[i] = mode_size[(int) CCmode];
|
||
mode_unit_size[i] = mode_unit_size[(int) CCmode];
|
||
mode_wider_mode[i - 1] = (enum machine_mode) i;
|
||
mode_wider_mode[i] = 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);
|
||
}
|
||
}
|
||
}
|