/* RTL reader for GNU C Compiler. Copyright (C) 1987, 1988, 1991, 1994, 1997, 1998, 1999, 2000, 2001 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 "hconfig.h" #include "system.h" #include "rtl.h" #include "obstack.h" #include "hashtab.h" #define obstack_chunk_alloc xmalloc #define obstack_chunk_free free static htab_t md_constants; static void fatal_with_file_and_line PARAMS ((FILE *, const char *, ...)) ATTRIBUTE_PRINTF_2 ATTRIBUTE_NORETURN; static void fatal_expected_char PARAMS ((FILE *, int, int)) ATTRIBUTE_NORETURN; static void read_name PARAMS ((char *, FILE *)); static char *read_string PARAMS ((struct obstack *, FILE *, int)); static char *read_quoted_string PARAMS ((struct obstack *, FILE *)); static char *read_braced_string PARAMS ((struct obstack *, FILE *)); static void read_escape PARAMS ((struct obstack *, FILE *)); static unsigned def_hash PARAMS ((const void *)); static int def_name_eq_p PARAMS ((const void *, const void *)); static void read_constants PARAMS ((FILE *infile, char *tmp_char)); /* Subroutines of read_rtx. */ /* The current line number for the file. */ int read_rtx_lineno = 1; /* The filename for aborting with file and line. */ const char *read_rtx_filename = ""; static void fatal_with_file_and_line VPARAMS ((FILE *infile, const char *msg, ...)) { #ifndef ANSI_PROTOTYPES FILE *infile; const char *msg; #endif va_list ap; char context[64]; size_t i; int c; VA_START (ap, msg); #ifndef ANSI_PROTOTYPES infile = va_arg (ap, FILE *); msg = va_arg (ap, const char *); #endif fprintf (stderr, "%s:%d: ", read_rtx_filename, read_rtx_lineno); vfprintf (stderr, msg, ap); putc ('\n', stderr); /* Gather some following context. */ for (i = 0; i < sizeof(context)-1; ++i) { c = getc (infile); if (c == EOF) break; if (c == '\r' || c == '\n') break; context[i] = c; } context[i] = '\0'; fprintf (stderr, "%s:%d: following context is `%s'\n", read_rtx_filename, read_rtx_lineno, context); va_end (ap); exit (1); } /* Dump code after printing a message. Used when read_rtx finds invalid data. */ static void fatal_expected_char (infile, expected_c, actual_c) FILE *infile; int expected_c, actual_c; { fatal_with_file_and_line (infile, "expected character `%c', found `%c'", expected_c, actual_c); } /* 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 (1) { c = getc (infile); switch (c) { case '\n': read_rtx_lineno++; break; case ' ': case '\t': case '\f': case '\r': break; case ';': do c = getc (infile); while (c != '\n' && c != EOF); read_rtx_lineno++; break; case '/': { register int prevc; c = getc (infile); if (c != '*') fatal_expected_char (infile, '*', c); prevc = 0; while ((c = getc (infile)) && c != EOF) { if (c == '\n') read_rtx_lineno++; else if (prevc == '*' && c == '/') break; prevc = c; } } break; default: 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) fatal_with_file_and_line (infile, "missing name or number"); if (c == '\n') read_rtx_lineno++; *p = 0; if (md_constants) { /* Do constant expansion. */ struct md_constant *def; p = str; do { struct md_constant tmp_def; tmp_def.name = p; def = htab_find (md_constants, &tmp_def); if (def) p = def->value; } while (def); if (p != str) strcpy (str, p); } } /* Subroutine of the string readers. Handles backslash escapes. Caller has read the backslash, but not placed it into the obstack. */ static void read_escape (ob, infile) struct obstack *ob; FILE *infile; { int c = getc (infile); switch (c) { /* Backslash-newline is replaced by nothing, as in C. */ case '\n': read_rtx_lineno++; return; /* \" \' \\ are replaced by the second character. */ case '\\': case '"': case '\'': break; /* Standard C string escapes: \a \b \f \n \r \t \v \[0-7] \x all are passed through to the output string unmolested. In normal use these wind up in a string constant processed by the C compiler, which will translate them appropriately. We do not bother checking that \[0-7] are followed by up to two octal digits, or that \x is followed by N hex digits. \? \u \U are left out because they are not in traditional C. */ case 'a': case 'b': case 'f': case 'n': case 'r': case 't': case 'v': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case 'x': obstack_1grow (ob, '\\'); break; /* \; makes stuff for a C string constant containing newline and tab. */ case ';': obstack_grow (ob, "\\n\\t", 4); return; /* pass anything else through, but issue a warning. */ default: fprintf (stderr, "%s:%d: warning: unrecognized escape \\%c\n", read_rtx_filename, read_rtx_lineno, c); obstack_1grow (ob, '\\'); break; } obstack_1grow (ob, c); } /* Read a double-quoted string onto the obstack. Caller has scanned the leading quote. */ static char * read_quoted_string (ob, infile) struct obstack *ob; FILE *infile; { int c; while (1) { c = getc (infile); /* Read the string */ if (c == '\n') read_rtx_lineno++; else if (c == '\\') { read_escape (ob, infile); continue; } else if (c == '"') break; obstack_1grow (ob, c); } obstack_1grow (ob, 0); return obstack_finish (ob); } /* Read a braced string (a la Tcl) onto the obstack. Caller has scanned the leading brace. Note that unlike quoted strings, the outermost braces _are_ included in the string constant. */ static char * read_braced_string (ob, infile) struct obstack *ob; FILE *infile; { int c; int brace_depth = 1; /* caller-processed */ obstack_1grow (ob, '{'); while (brace_depth) { c = getc (infile); /* Read the string */ if (c == '\n') read_rtx_lineno++; else if (c == '{') brace_depth++; else if (c == '}') brace_depth--; else if (c == '\\') { read_escape (ob, infile); continue; } obstack_1grow (ob, c); } obstack_1grow (ob, 0); return obstack_finish (ob); } /* Read some kind of string constant. This is the high-level routine used by read_rtx. It handles surrounding parentheses, leading star, and dispatch to the appropriate string constant reader. */ static char * read_string (ob, infile, star_if_braced) struct obstack *ob; FILE *infile; int star_if_braced; { char *stringbuf; int saw_paren = 0; int c; c = read_skip_spaces (infile); if (c == '(') { saw_paren = 1; c = read_skip_spaces (infile); } if (c == '"') stringbuf = read_quoted_string (ob, infile); else if (c == '{') { if (star_if_braced) obstack_1grow (ob, '*'); stringbuf = read_braced_string (ob, infile); } else fatal_with_file_and_line (infile, "expected `\"' or `{', found `%c'", c); if (saw_paren) { c = read_skip_spaces (infile); if (c != ')') fatal_expected_char (infile, ')', c); } return stringbuf; } /* 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 /* Given a constant definition, return a hash code for its name. */ static unsigned def_hash (def) const void *def; { unsigned result, i; const char *string = ((const struct md_constant *)def)->name; for (result = i = 0;*string++ != '\0'; i++) result += ((unsigned char) *string << (i % CHAR_BIT)); return result; } /* Given two constant definitions, return true if they have the same name. */ static int def_name_eq_p (def1, def2) const void *def1, *def2; { return ! strcmp (((const struct md_constant *)def1)->name, ((const struct md_constant *)def2)->name); } /* INFILE is a FILE pointer to read text from. TMP_CHAR is a buffer suitable to read a name or number into. Process a define_constants directive, starting with the optional space after the "define_constants". */ static void read_constants (infile, tmp_char) FILE *infile; char *tmp_char; { int c; htab_t defs; c = read_skip_spaces (infile); if (c != '[') fatal_expected_char (infile, '[', c); defs = md_constants; if (! defs) defs = htab_create (32, def_hash, def_name_eq_p, (htab_del) 0); /* Disable constant expansion during definition processing. */ md_constants = 0; while ( (c = read_skip_spaces (infile)) != ']') { struct md_constant *def; void **entry_ptr; if (c != '(') fatal_expected_char (infile, '(', c); def = xmalloc (sizeof (struct md_constant)); def->name = tmp_char; read_name (tmp_char, infile); entry_ptr = htab_find_slot (defs, def, TRUE); if (! *entry_ptr) def->name = xstrdup (tmp_char); c = read_skip_spaces (infile); ungetc (c, infile); read_name (tmp_char, infile); if (! *entry_ptr) { def->value = xstrdup (tmp_char); *entry_ptr = def; } else { def = *entry_ptr; if (strcmp (def->value, tmp_char)) fatal_with_file_and_line (infile, "redefinition of %s, was %s, now %s", def->name, def->value, tmp_char); } c = read_skip_spaces (infile); if (c != ')') fatal_expected_char (infile, ')', c); } md_constants = defs; c = read_skip_spaces (infile); if (c != ')') fatal_expected_char (infile, ')', c); } /* For every constant definition, call CALLBACK with two arguments: a pointer a pointer to the constant definition and INFO. Stops when CALLBACK returns zero. */ void traverse_md_constants (callback, info) htab_trav callback; void *info; { if (md_constants) htab_traverse (md_constants, callback, info); } /* 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; RTX_CODE tmp_code; register const 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; /* Obstack used for allocating RTL objects. */ static struct obstack rtl_obstack; static int initialized; /* Linked list structure for making RTXs: */ struct rtx_list { struct rtx_list *next; rtx value; /* Value of this node. */ }; if (!initialized) { obstack_init (&rtl_obstack); initialized = 1; } again: c = read_skip_spaces (infile); /* Should be open paren. */ if (c != '(') fatal_expected_char (infile, '(', c); read_name (tmp_char, infile); tmp_code = UNKNOWN; if (! strcmp (tmp_char, "define_constants")) { read_constants (infile, tmp_char); goto again; } for (i = 0; i < NUM_RTX_CODE; i++) if (! strcmp (tmp_char, GET_RTX_NAME (i))) { tmp_code = (RTX_CODE) i; /* get value for name */ break; } if (tmp_code == UNKNOWN) fatal_with_file_and_line (infile, "unknown rtx code `%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; } /* If we end up with an insn expression then we free this space below. */ return_rtx = rtx_alloc (tmp_code); 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 == ':') { read_name (tmp_char, infile); for (j = 0; j < NUM_MACHINE_MODES; j++) if (! strcmp (GET_MODE_NAME (j), tmp_char)) break; if (j == MAX_MACHINE_MODE) fatal_with_file_and_line (infile, "unknown mode `%s'", tmp_char); PUT_MODE (return_rtx, (enum machine_mode) j); } 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': { /* Obstack to store scratch vector in. */ struct obstack vector_stack; int list_counter = 0; rtvec return_vec = NULL_RTVEC; c = read_skip_spaces (infile); if (c != '[') fatal_expected_char (infile, '[', c); /* add expressions to a list, while keeping a count */ obstack_init (&vector_stack); while ((c = read_skip_spaces (infile)) && c != ']') { ungetc (c, infile); list_counter++; obstack_ptr_grow (&vector_stack, (PTR) read_rtx (infile)); } if (list_counter > 0) { return_vec = rtvec_alloc (list_counter); memcpy (&return_vec->elem[0], obstack_finish (&vector_stack), list_counter * sizeof (rtx)); } XVEC (return_rtx, i) = return_vec; obstack_free (&vector_stack, NULL); /* 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 'T': case 's': { char *stringbuf; /* The output template slot of a DEFINE_INSN, DEFINE_INSN_AND_SPLIT, or DEFINE_PEEPHOLE automatically gets a star inserted as its first character, if it is written with a brace block instead of a string constant. */ int star_if_braced = (format_ptr[-1] == 'T'); stringbuf = read_string (&rtl_obstack, infile, star_if_braced); /* For insn patterns, we want to provide a default name based on the file and line, like "*foo.md:12", if the given name is blank. These are only for define_insn and define_insn_and_split, to aid debugging. */ if (*stringbuf == '\0' && i == 0 && (GET_CODE (return_rtx) == DEFINE_INSN || GET_CODE (return_rtx) == DEFINE_INSN_AND_SPLIT)) { char line_name[20]; const char *fn = (read_rtx_filename ? read_rtx_filename : "rtx"); const char *slash; for (slash = fn; *slash; slash ++) if (*slash == '/' || *slash == '\\' || *slash == ':') fn = slash + 1; obstack_1grow (&rtl_obstack, '*'); obstack_grow (&rtl_obstack, fn, strlen (fn)); sprintf (line_name, ":%d", read_rtx_lineno); obstack_grow (&rtl_obstack, line_name, strlen (line_name)+1); stringbuf = (char *) obstack_finish (&rtl_obstack); } if (star_if_braced) XTMPL (return_rtx, i) = stringbuf; else 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 C99 standard. 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 != ')') fatal_expected_char (infile, ')', c); return return_rtx; }