|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T r
Length: 31410 (0x7ab2) Types: TextFile Names: »rtl.c«
└─⟦a05ed705a⟧ Bits:30007078 DKUUG GNU 2/12/89 └─⟦d53cfc7b2⟧ »./gcc-1.35.tar.Z« └─⟦90f628c1d⟧ └─⟦this⟧ »gcc-1.35/rtl.c«
/* Manage RTL for C-Compiler Copyright (C) 1987, 1988 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* This file contains the low level primitives for allocating, printing and reading rtl expressions and vectors. It also contains some functions for semantic analysis on rtl expressions. */ #include "config.h" #include <ctype.h> #include <stdio.h> #include "rtl.h" #include "obstack.h" #define obstack_chunk_alloc xmalloc #define obstack_chunk_free free extern int xmalloc (); extern void 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 temporary_obstack. */ extern struct obstack *rtl_obstack; #define MIN(x,y) ((x < y) ? x : y) extern long ftell(); \f /* 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) 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) NAME, char *mode_name[] = { #include "machmode.def" }; #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) CLASS, enum mode_class mode_class[] = { #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) SIZE, int mode_size[] = { #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) UNIT, int mode_unit_size[] = { #include "machmode.def" /* machine modes are documented here */ }; #undef DEF_MACHMODE /* 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 charcter 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 "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 "u" a pointer to another insn prints the uid of the insn. */ #define DEF_RTL_EXPR(ENUM, NAME, FORMAT) FORMAT , #include "rtl.def" /* rtl expressions are defined here */ #undef DEF_RTL_EXPR }; /* Names for kinds of NOTEs and REG_NOTEs. */ char *note_insn_name[] = { "NOTE_INSN_FUNCTION_BEG", "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" }; char *reg_note_name[] = { "", "REG_DEAD", "REG_INC", "REG_EQUIV", "REG_WAS_0", "REG_EQUAL", "REG_RETVAL", "REG_LIBCALL", "REG_NONNEG", "REG_ASM_LINE", "REG_ASM_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].rtvec = NULL; /* @@ not portable due to rtunion */ 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 int nelts = GET_RTX_LENGTH (code); register int length = sizeof (struct rtx_def) + (nelts - 1) * sizeof (rtunion); rt = (rtx) obstack_alloc (rtl_obstack, length); * (int *) rt = 0; PUT_CODE (rt, code); return rt; } \f /* 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: return 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) = copy_rtx (XEXP (orig, i)); break; case 'E': 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; default: XINT (copy, i) = XINT (orig, i); break; } } return copy; } \f /* Return 1 if the value of X is unstable (would be different at a different point in the program). The frame pointer, arg pointer, etc. are considered stable (within one function) and so is anything marked `unchanging'. */ int rtx_unstable_p (x) rtx x; { register RTX_CODE code = GET_CODE (x); register int i; register char *fmt; if (code == MEM) return ! RTX_UNCHANGING_P (x); if (code == QUEUED) return 1; if (code == CONST || code == CONST_INT) return 0; if (code == REG) return ! (REGNO (x) == FRAME_POINTER_REGNUM || REGNO (x) == ARG_POINTER_REGNUM || RTX_UNCHANGING_P (x)); fmt = GET_RTX_FORMAT (code); for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) if (fmt[i] == 'e') if (rtx_unstable_p (XEXP (x, i))) return 1; return 0; } /* Return 1 if X has a value that can vary even between two executions of the program. 0 means X can be compared reliably against certain constants or near-constants. The frame pointer and the arg pointer are considered constant. */ int rtx_varies_p (x) rtx x; { register RTX_CODE code = GET_CODE (x); register int i; register char *fmt; if (code == MEM) return 1; if (code == QUEUED) return 1; if (code == CONST || code == CONST_INT) return 0; if (code == REG) return ! (REGNO (x) == FRAME_POINTER_REGNUM || REGNO (x) == ARG_POINTER_REGNUM); fmt = GET_RTX_FORMAT (code); for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) if (fmt[i] == 'e') if (rtx_varies_p (XEXP (x, i))) return 1; return 0; } /* Return 1 if X refers to a memory location whose address cannot be compared reliably with constant addresses, or if X refers to a BLKmode memory object. */ int rtx_addr_varies_p (x) rtx x; { register enum rtx_code code; register int i; register char *fmt; if (x == 0) return 0; code = GET_CODE (x); if (code == MEM) return GET_MODE (x) == BLKmode || rtx_varies_p (XEXP (x, 0)); fmt = GET_RTX_FORMAT (code); for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) if (fmt[i] == 'e') if (rtx_addr_varies_p (XEXP (x, i))) return 1; return 0; } \f /* Nonzero if register REG appears somewhere within IN. Also works if REG is not a register; in this case it checks for a subexpression of IN that is Lisp "equal" to REG. */ int reg_mentioned_p (reg, in) register rtx reg, in; { register char *fmt; register int i; register enum rtx_code code; if (in == 0) return 0; if (reg == in) return 1; code = GET_CODE (in); switch (code) { /* Compare registers by number. */ case REG: return GET_CODE (reg) == REG && REGNO (in) == REGNO (reg); /* These codes have no constituent expressions and are unique. */ case CC0: case PC: return 0; case CONST_INT: return GET_CODE (reg) == CONST_INT && INTVAL (in) == INTVAL (reg); } if (GET_CODE (reg) == code && rtx_equal_p (reg, in)) return 1; fmt = GET_RTX_FORMAT (code); for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) { if (fmt[i] == 'E') { register int j; for (j = XVECLEN (in, i) - 1; j >= 0; j--) if (reg_mentioned_p (reg, XVECEXP (in, i, j))) return 1; } else if (fmt[i] == 'e' && reg_mentioned_p (reg, XEXP (in, i))) return 1; } return 0; } /* Nonzero if register REG is used in an insn between FROM_INSN and TO_INSN (exclusive of those two). */ int reg_used_between_p (reg, from_insn, to_insn) rtx reg, from_insn, to_insn; { register rtx insn; register RTX_CODE code; for (insn = NEXT_INSN (from_insn); insn != to_insn; insn = NEXT_INSN (insn)) if (((code = GET_CODE (insn)) == INSN || code == JUMP_INSN || code == CALL_INSN) && reg_mentioned_p (reg, PATTERN (insn))) return 1; return 0; } \f /* Return nonzero if hard register in range [REGNO, ENDREGNO) appears either explicitly or implicitly in X other than being stored into. References contained within the substructure at LOC do not count. LOC may be zero, meaning don't ignore anything. */ int refers_to_regno_p (regno, endregno, x, loc) int regno, endregno; rtx x; rtx *loc; { register int i; register RTX_CODE code; register char *fmt; repeat: code = GET_CODE (x); if (code == REG) { i = REGNO (x); return (endregno > i && regno < i + HARD_REGNO_NREGS (i, GET_MODE (x))); } if (code == SET) { /* Note setting a SUBREG counts as referring to the REG it is in! */ if (GET_CODE (SET_DEST (x)) != REG && refers_to_regno_p (regno, endregno, SET_DEST (x), loc)) return 1; if (loc == &SET_SRC (x)) return 0; x = SET_SRC (x); goto repeat; } if (code == CLOBBER) { if (GET_CODE (SET_DEST (x)) != REG && refers_to_regno_p (regno, endregno, SET_DEST (x), loc)) return 1; return 0; } /* X does not match, so try its subexpressions. */ fmt = GET_RTX_FORMAT (code); for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) { if (fmt[i] == 'e' && loc != &XEXP (x, i)) { if (i == 0) { x = XEXP (x, 0); goto repeat; } else if (refers_to_regno_p (regno, endregno, XEXP (x, i), loc)) return 1; } else if (fmt[i] == 'E') { register int j; for (j = XVECLEN (x, i) - 1; j >=0; j--) if (loc != &XVECEXP (x, i, j) && refers_to_regno_p (regno, endregno, XVECEXP (x, i, j), loc)) return 1; } } return 0; } /* Nonzero if X contains any reg that overlaps hard register REG. */ int reg_overlap_mentioned_p (reg, x) rtx reg, x; { int regno = REGNO (reg); int endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (reg)); return refers_to_regno_p (regno, endregno, x, 0); } \f /* This is 1 until after reload pass. */ int rtx_equal_function_value_matters; /* Return 1 if X and Y are identical-looking rtx's. This is the Lisp function EQUAL for rtx arguments. */ int rtx_equal_p (x, y) rtx x, y; { register int i; register int j; register enum rtx_code code; register char *fmt; if (x == y) return 1; if (x == 0 || y == 0) return 0; code = GET_CODE (x); /* Rtx's of different codes cannot be equal. */ if (code != GET_CODE (y)) return 0; /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. (REG:SI x) and (REG:HI x) are NOT equivalent. */ if (GET_MODE (x) != GET_MODE (y)) return 0; /* These three types of rtx's can be compared nonrecursively. */ /* Until the end of reload, don't consider the a reference to the return register of the current function the same as the return from a called function. This eases the job of function integration. Once the distinction no longer matters, the insn will be deleted. */ if (code == REG) return (REGNO (x) == REGNO (y) && (! rtx_equal_function_value_matters || REG_FUNCTION_VALUE_P (x) == REG_FUNCTION_VALUE_P (y))); if (code == LABEL_REF) return XEXP (x, 0) == XEXP (y, 0); if (code == SYMBOL_REF) return XSTR (x, 0) == XSTR (y, 0); /* Compare the elements. If any pair of corresponding elements fail to match, return 0 for the whole things. */ fmt = GET_RTX_FORMAT (code); for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) { switch (fmt[i]) { case 'i': if (XINT (x, i) != XINT (y, i)) return 0; break; case 'E': for (j = 0; j < XVECLEN (x, i); j++) if (rtx_equal_p (XVECEXP (x, i, j), XVECEXP (y, i, j)) == 0) return 0; break; case 'e': if (rtx_equal_p (XEXP (x, i), XEXP (y, i)) == 0) return 0; break; case 's': if (strcmp (XSTR (x, i), XSTR (y, i))) return 0; break; case 'u': /* These are just backpointers, so they don't matter. */ break; case '0': break; /* It is believed that rtx's at this level will never contain anything but integers and other rtx's, except for within LABEL_REFs and SYMBOL_REFs. */ default: abort (); } } return 1; } \f /* Call FUN on each register or MEM that is stored into or clobbered by X. (X would be the pattern of an insn). FUN receives two arguments: the REG, MEM, CC0 or PC being stored in or clobbered, the SET or CLOBBER rtx that does the store. */ void note_stores (x, fun) register rtx x; void (*fun) (); { if ((GET_CODE (x) == SET || GET_CODE (x) == CLOBBER)) { register rtx dest = SET_DEST (x); while (GET_CODE (dest) == SUBREG || GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT || GET_CODE (dest) == STRICT_LOW_PART) dest = XEXP (dest, 0); (*fun) (dest, x); } else if (GET_CODE (x) == PARALLEL) { register int i; for (i = XVECLEN (x, 0) - 1; i >= 0; i--) { register rtx y = XVECEXP (x, 0, i); if (GET_CODE (y) == SET || GET_CODE (y) == CLOBBER) { register rtx dest = SET_DEST (y); while (GET_CODE (dest) == SUBREG || GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT || GET_CODE (dest) == STRICT_LOW_PART) dest = XEXP (dest, 0); (*fun) (dest, XVECEXP (x, 0, i)); } } } } \f /* Return nonzero if register REG's old contents don't survive after INSN. This can be because REG dies in INSN or because INSN entirely sets REG. "Entirely set" means set directly and not through a SUBREG, ZERO_EXTRACT or SIGN_EXTRACT, so no trace of the old contents remains. REG may be a hard or pseudo reg. Renumbering is not taken into account, but for this use that makes no difference, since regs don't overlap during their lifetimes. Therefore, this function may be used at any time after deaths have been computed (in flow.c). */ int dead_or_set_p (insn, reg) rtx insn; rtx reg; { register rtx link; register int regno = REGNO (reg); for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) if ((REG_NOTE_KIND (link) == REG_DEAD || REG_NOTE_KIND (link) == REG_INC) && REGNO (XEXP (link, 0)) == regno) return 1; if (GET_CODE (PATTERN (insn)) == SET) return SET_DEST (PATTERN (insn)) == reg; else if (GET_CODE (PATTERN (insn)) == PARALLEL) { register int i; for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--) { if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET && SET_DEST (XVECEXP (PATTERN (insn), 0, i)) == reg) return 1; } } return 0; } /* Return the reg-note of kind KIND in insn INSN, if there is one. If DATUM is nonzero, look for one whose datum is DATUM. */ rtx find_reg_note (insn, kind, datum) rtx insn; enum reg_note kind; rtx datum; { register rtx link; for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) if (REG_NOTE_KIND (link) == kind && (datum == 0 || datum == XEXP (link, 0))) return link; return 0; } /* Return the reg-note of kind KIND in insn INSN which applies to register number REGNO, if any. Return 0 if there is no such reg-note. */ rtx find_regno_note (insn, kind, regno) rtx insn; enum reg_note kind; int regno; { register rtx link; for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) if (REG_NOTE_KIND (link) == kind && REGNO (XEXP (link, 0)) == regno) return link; return 0; } \f /* Nonzero if FROM precedes TO with no intervening labels. */ int no_labels_between (from, to) register rtx from, to; { register rtx p = to; while (1) { p = PREV_INSN (p); if (p == 0) return 0; if (p == from) return 1; if (GET_CODE (p) == CODE_LABEL) return 0; } } \f /* Nonzero if X contains any volatile memory references or volatile ASM_OPERANDS expressions. */ int volatile_refs_p (x) rtx x; { register RTX_CODE code; code = GET_CODE (x); switch (code) { case LABEL_REF: case SYMBOL_REF: case CONST_INT: case CONST: case CONST_DOUBLE: case CC0: case PC: case REG: case CLOBBER: case ASM_INPUT: case ADDR_VEC: case ADDR_DIFF_VEC: return 0; case CALL: return 1; case MEM: case ASM_OPERANDS: if (MEM_VOLATILE_P (x)) return 1; } /* Recursively scan the operands of this expression. */ { register char *fmt = GET_RTX_FORMAT (code); register int i; for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) { if (fmt[i] == 'e') { if (volatile_refs_p (XEXP (x, i))) return 1; } if (fmt[i] == 'E') { register int j; for (j = 0; j < XVECLEN (x, i); j++) if (volatile_refs_p (XVECEXP (x, i, j))) return 1; } } } return 0; } \f /* Printing rtl for debugging dumps. */ static FILE *outfile; char spaces[] = " "; static int sawclose = 0; /* Print IN_RTX onto OUTFILE. This is the recursive part of printing. */ static void print_rtx (in_rtx) register rtx in_rtx; { static int indent; register int i, j; register char *format_ptr; if (sawclose) { fprintf (outfile, "\n%s", (spaces + (sizeof spaces - indent * 2))); sawclose = 0; } if (in_rtx == 0) { fprintf (outfile, "(nil)"); sawclose = 1; return; } /* print name of expression code */ fprintf (outfile, "(%s", GET_RTX_NAME (GET_CODE (in_rtx))); if (in_rtx->in_struct) fprintf (outfile, "/s"); if (in_rtx->volatil) fprintf (outfile, "/v"); if (in_rtx->unchanging) fprintf (outfile, "/u"); if (in_rtx->integrated) fprintf (outfile, "/i"); if (GET_MODE (in_rtx) != VOIDmode) { /* Print REG_NOTE names for EXPR_LIST and INSN_LIST. */ if (GET_CODE (in_rtx) == EXPR_LIST || GET_CODE (in_rtx) == INSN_LIST) fprintf (outfile, ":%s", GET_REG_NOTE_NAME (GET_MODE (in_rtx))); else fprintf (outfile, ":%s", GET_MODE_NAME (GET_MODE (in_rtx))); } format_ptr = GET_RTX_FORMAT (GET_CODE (in_rtx)); for (i = 0; i < GET_RTX_LENGTH (GET_CODE (in_rtx)); i++) switch (*format_ptr++) { case 'S': case 's': if (XSTR (in_rtx, i) == 0) fprintf (outfile, " \"\""); else fprintf (outfile, " (\"%s\")", XSTR (in_rtx, i)); sawclose = 1; break; /* 0 indicates a field for internal use that should not be printed. */ case '0': break; case 'e': indent += 2; if (!sawclose) fprintf (outfile, " "); print_rtx (XEXP (in_rtx, i)); indent -= 2; break; case 'E': indent += 2; if (sawclose) { fprintf (outfile, "\n%s", (spaces + (sizeof spaces - indent * 2))); sawclose = 0; } fprintf (outfile, "[ "); if (NULL != XVEC (in_rtx, i)) { indent += 2; if (XVECLEN (in_rtx, i)) sawclose = 1; for (j = 0; j < XVECLEN (in_rtx, i); j++) print_rtx (XVECEXP (in_rtx, i, j)); indent -= 2; } if (sawclose) fprintf (outfile, "\n%s", (spaces + (sizeof spaces - indent * 2))); fprintf (outfile, "] "); sawclose = 1; indent -= 2; break; case 'i': fprintf (outfile, " %d", XINT (in_rtx, i)); sawclose = 0; break; /* Print NOTE_INSN names rather than integer codes. */ case 'n': if (XINT (in_rtx, i) <= 0) fprintf (outfile, " %s", GET_NOTE_INSN_NAME (XINT (in_rtx, i))); else fprintf (outfile, " %d", XINT (in_rtx, i)); sawclose = 0; break; case 'u': if (XEXP (in_rtx, i) != NULL) fprintf(outfile, " %d", INSN_UID (XEXP (in_rtx, i))); else fprintf(outfile, " 0"); sawclose = 0; break; default: fprintf (stderr, "switch format wrong in rtl.print_rtx(). format was: %c.\n", format_ptr[-1]); abort (); } fprintf (outfile, ")"); sawclose = 1; } /* Call this function from the debugger to see what X looks like. */ void debug_rtx (x) rtx x; { outfile = stderr; print_rtx (x); fprintf (stderr, "\n"); } /* External entry point for printing a chain of insns starting with RTX_FIRST onto file OUTF. A blank line separates insns. If RTX_FIRST is not an insn, then it alone is printed, with no newline. */ void print_rtl (outf, rtx_first) FILE *outf; rtx rtx_first; { register rtx tmp_rtx; outfile = outf; sawclose = 0; switch (GET_CODE (rtx_first)) { case INSN: case JUMP_INSN: case CALL_INSN: case NOTE: case CODE_LABEL: case BARRIER: for (tmp_rtx = rtx_first; NULL != tmp_rtx; tmp_rtx = NEXT_INSN (tmp_rtx)) { print_rtx (tmp_rtx); fprintf (outfile, "\n"); } break; default: print_rtx (rtx_first); } } \f /* 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; fprintf (stderr, "Expected character %c. Read character %c. At file position: %ld\n", expected_c, actual_c, 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') ; } else if (c == '/') { register int prevc; c = getc (infile); if (c != '*') dump_and_abort ('*', c, infile); prevc = 0; while (c = getc (infile)) { 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); } *p = NULL; } \f /* 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; /* 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 'E': { register struct rtx_list *next_rtx, *rtx_list_link; struct rtx_list *list_rtx; 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); 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; int stringbufsize; c = read_skip_spaces (infile); if (c == '(') { saw_paren = 1; c = read_skip_spaces (infile); } if (c != '"') dump_and_abort ('"', c, infile); j = 0; stringbufsize = 10; stringbuf = (char *) xmalloc (stringbufsize + 1); while (1) { if (j >= stringbufsize - 4) { stringbufsize *= 2; stringbuf = (char *) xrealloc (stringbuf, stringbufsize + 1); } stringbuf[j] = getc (infile); /* Read the string */ if (stringbuf[j] == '\\') { stringbuf[j] = getc (infile); /* Read the string */ /* \; makes stuff for a C string constant containing newline and tab. */ if (stringbuf[j] == ';') { strcpy (&stringbuf[j], "\\n\\t"); j += 3; } } else if (stringbuf[j] == '"') break; j++; } stringbuf[j] = 0; /* NUL terminate the string */ stringbuf = (char *) xrealloc (stringbuf, j + 1); if (saw_paren) { c = read_skip_spaces (infile); if (c != ')') dump_and_abort (')', c, infile); } XSTR (return_rtx, i) = stringbuf; } 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; } \f /* This is called once per compilation, before any rtx's are constructed. It initializes the vector `rtx_length'. */ void init_rtl () { 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 *) permalloc (i + 1); rtx_length[(int) CONST_DOUBLE] = i; *s++ = 'e'; *s++ = '0'; /* Set the GET_RTX_FORMAT of CONST_DOUBLE to a string of as many `i's as we now have elements. */ for (i = 0; i < rtx_length[(int) CONST_DOUBLE]; i++) *s++ = 'i'; *s++ = 0; } #endif }