From df62c1c110e8532b995b23540b7e3695729c0779 Mon Sep 17 00:00:00 2001 From: Jing Yu Date: Thu, 5 Nov 2009 15:11:04 -0800 Subject: Check in gcc sources for prebuilt toolchains in Eclair. --- gcc-4.4.0/gcc/genattrtab.c | 4614 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 4614 insertions(+) create mode 100644 gcc-4.4.0/gcc/genattrtab.c (limited to 'gcc-4.4.0/gcc/genattrtab.c') diff --git a/gcc-4.4.0/gcc/genattrtab.c b/gcc-4.4.0/gcc/genattrtab.c new file mode 100644 index 000000000..794a8db1b --- /dev/null +++ b/gcc-4.4.0/gcc/genattrtab.c @@ -0,0 +1,4614 @@ +/* Generate code from machine description to compute values of attributes. + Copyright (C) 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, + 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. + Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu) + +This file is part of GCC. + +GCC 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 3, or (at your option) any later +version. + +GCC 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 GCC; see the file COPYING3. If not see +. */ + +/* This program handles insn attributes and the DEFINE_DELAY and + DEFINE_INSN_RESERVATION definitions. + + It produces a series of functions named `get_attr_...', one for each insn + attribute. Each of these is given the rtx for an insn and returns a member + of the enum for the attribute. + + These subroutines have the form of a `switch' on the INSN_CODE (via + `recog_memoized'). Each case either returns a constant attribute value + or a value that depends on tests on other attributes, the form of + operands, or some random C expression (encoded with a SYMBOL_REF + expression). + + If the attribute `alternative', or a random C expression is present, + `constrain_operands' is called. If either of these cases of a reference to + an operand is found, `extract_insn' is called. + + The special attribute `length' is also recognized. For this operand, + expressions involving the address of an operand or the current insn, + (address (pc)), are valid. In this case, an initial pass is made to + set all lengths that do not depend on address. Those that do are set to + the maximum length. Then each insn that depends on an address is checked + and possibly has its length changed. The process repeats until no further + changed are made. The resulting lengths are saved for use by + `get_attr_length'. + + A special form of DEFINE_ATTR, where the expression for default value is a + CONST expression, indicates an attribute that is constant for a given run + of the compiler. The subroutine generated for these attributes has no + parameters as it does not depend on any particular insn. Constant + attributes are typically used to specify which variety of processor is + used. + + Internal attributes are defined to handle DEFINE_DELAY and + DEFINE_INSN_RESERVATION. Special routines are output for these cases. + + This program works by keeping a list of possible values for each attribute. + These include the basic attribute choices, default values for attribute, and + all derived quantities. + + As the description file is read, the definition for each insn is saved in a + `struct insn_def'. When the file reading is complete, a `struct insn_ent' + is created for each insn and chained to the corresponding attribute value, + either that specified, or the default. + + An optimization phase is then run. This simplifies expressions for each + insn. EQ_ATTR tests are resolved, whenever possible, to a test that + indicates when the attribute has the specified value for the insn. This + avoids recursive calls during compilation. + + The strategy used when processing DEFINE_DELAY definitions is to create + arbitrarily complex expressions and have the optimization simplify them. + + Once optimization is complete, any required routines and definitions + will be written. + + An optimization that is not yet implemented is to hoist the constant + expressions entirely out of the routines and definitions that are written. + A way to do this is to iterate over all possible combinations of values + for constant attributes and generate a set of functions for that given + combination. An initialization function would be written that evaluates + the attributes and installs the corresponding set of routines and + definitions (each would be accessed through a pointer). + + We use the flags in an RTX as follows: + `unchanging' (ATTR_IND_SIMPLIFIED_P): This rtx is fully simplified + independent of the insn code. + `in_struct' (ATTR_CURR_SIMPLIFIED_P): This rtx is fully simplified + for the insn code currently being processed (see optimize_attrs). + `return_val' (ATTR_PERMANENT_P): This rtx is permanent and unique + (see attr_rtx). */ + +#define ATTR_IND_SIMPLIFIED_P(RTX) (RTX_FLAG((RTX), unchanging)) +#define ATTR_CURR_SIMPLIFIED_P(RTX) (RTX_FLAG((RTX), in_struct)) +#define ATTR_PERMANENT_P(RTX) (RTX_FLAG((RTX), return_val)) + +#if 0 +#define strcmp_check(S1, S2) ((S1) == (S2) \ + ? 0 \ + : (gcc_assert (strcmp ((S1), (S2))), 1)) +#else +#define strcmp_check(S1, S2) ((S1) != (S2)) +#endif + +#include "bconfig.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "rtl.h" +#include "gensupport.h" +#include "obstack.h" +#include "errors.h" + +/* Flags for make_internal_attr's `special' parameter. */ +#define ATTR_NONE 0 +#define ATTR_SPECIAL (1 << 0) + +static struct obstack obstack1, obstack2; +static struct obstack *hash_obstack = &obstack1; +static struct obstack *temp_obstack = &obstack2; + +/* enough space to reserve for printing out ints */ +#define MAX_DIGITS (HOST_BITS_PER_INT * 3 / 10 + 3) + +/* Define structures used to record attributes and values. */ + +/* As each DEFINE_INSN, DEFINE_PEEPHOLE, or DEFINE_ASM_ATTRIBUTES is + encountered, we store all the relevant information into a + `struct insn_def'. This is done to allow attribute definitions to occur + anywhere in the file. */ + +struct insn_def +{ + struct insn_def *next; /* Next insn in chain. */ + rtx def; /* The DEFINE_... */ + int insn_code; /* Instruction number. */ + int insn_index; /* Expression number in file, for errors. */ + int lineno; /* Line number. */ + int num_alternatives; /* Number of alternatives. */ + int vec_idx; /* Index of attribute vector in `def'. */ +}; + +/* Once everything has been read in, we store in each attribute value a list + of insn codes that have that value. Here is the structure used for the + list. */ + +struct insn_ent +{ + struct insn_ent *next; /* Next in chain. */ + struct insn_def *def; /* Instruction definition. */ +}; + +/* Each value of an attribute (either constant or computed) is assigned a + structure which is used as the listhead of the insns that have that + value. */ + +struct attr_value +{ + rtx value; /* Value of attribute. */ + struct attr_value *next; /* Next attribute value in chain. */ + struct insn_ent *first_insn; /* First insn with this value. */ + int num_insns; /* Number of insns with this value. */ + int has_asm_insn; /* True if this value used for `asm' insns */ +}; + +/* Structure for each attribute. */ + +struct attr_desc +{ + char *name; /* Name of attribute. */ + struct attr_desc *next; /* Next attribute. */ + struct attr_value *first_value; /* First value of this attribute. */ + struct attr_value *default_val; /* Default value for this attribute. */ + int lineno : 24; /* Line number. */ + unsigned is_numeric : 1; /* Values of this attribute are numeric. */ + unsigned is_const : 1; /* Attribute value constant for each run. */ + unsigned is_special : 1; /* Don't call `write_attr_set'. */ +}; + +/* Structure for each DEFINE_DELAY. */ + +struct delay_desc +{ + rtx def; /* DEFINE_DELAY expression. */ + struct delay_desc *next; /* Next DEFINE_DELAY. */ + int num; /* Number of DEFINE_DELAY, starting at 1. */ + int lineno; /* Line number. */ +}; + +struct attr_value_list +{ + struct attr_value *av; + struct insn_ent *ie; + struct attr_desc *attr; + struct attr_value_list *next; +}; + +/* Listheads of above structures. */ + +/* This one is indexed by the first character of the attribute name. */ +#define MAX_ATTRS_INDEX 256 +static struct attr_desc *attrs[MAX_ATTRS_INDEX]; +static struct insn_def *defs; +static struct delay_desc *delays; +struct attr_value_list **insn_code_values; + +/* Other variables. */ + +static int insn_code_number; +static int insn_index_number; +static int got_define_asm_attributes; +static int must_extract; +static int must_constrain; +static int address_used; +static int length_used; +static int num_delays; +static int have_annul_true, have_annul_false; +static int num_insn_ents; + +/* Stores, for each insn code, the number of constraint alternatives. */ + +static int *insn_n_alternatives; + +/* Stores, for each insn code, a bitmap that has bits on for each possible + alternative. */ + +static int *insn_alternatives; + +/* Used to simplify expressions. */ + +static rtx true_rtx, false_rtx; + +/* Used to reduce calls to `strcmp' */ + +static const char *alternative_name; +static const char *length_str; +static const char *delay_type_str; +static const char *delay_1_0_str; +static const char *num_delay_slots_str; + +/* Simplify an expression. Only call the routine if there is something to + simplify. */ +#define SIMPLIFY_TEST_EXP(EXP,INSN_CODE,INSN_INDEX) \ + (ATTR_IND_SIMPLIFIED_P (EXP) || ATTR_CURR_SIMPLIFIED_P (EXP) ? (EXP) \ + : simplify_test_exp (EXP, INSN_CODE, INSN_INDEX)) + +#define DEF_ATTR_STRING(S) (attr_string ((S), strlen (S))) + +/* Forward declarations of functions used before their definitions, only. */ +static char *attr_string (const char *, int); +static char *attr_printf (unsigned int, const char *, ...) + ATTRIBUTE_PRINTF_2; +static rtx make_numeric_value (int); +static struct attr_desc *find_attr (const char **, int); +static rtx mk_attr_alt (int); +static char *next_comma_elt (const char **); +static rtx insert_right_side (enum rtx_code, rtx, rtx, int, int); +static rtx copy_boolean (rtx); +static int compares_alternatives_p (rtx); +static void make_internal_attr (const char *, rtx, int); +static void insert_insn_ent (struct attr_value *, struct insn_ent *); +static void walk_attr_value (rtx); +static int max_attr_value (rtx, int*); +static int min_attr_value (rtx, int*); +static int or_attr_value (rtx, int*); +static rtx simplify_test_exp (rtx, int, int); +static rtx simplify_test_exp_in_temp (rtx, int, int); +static rtx copy_rtx_unchanging (rtx); +static bool attr_alt_subset_p (rtx, rtx); +static bool attr_alt_subset_of_compl_p (rtx, rtx); +static void clear_struct_flag (rtx); +static void write_attr_valueq (struct attr_desc *, const char *); +static struct attr_value *find_most_used (struct attr_desc *); +static void write_attr_set (struct attr_desc *, int, rtx, + const char *, const char *, rtx, + int, int); +static void write_attr_case (struct attr_desc *, struct attr_value *, + int, const char *, const char *, int, rtx); +static void write_attr_value (struct attr_desc *, rtx); +static void write_upcase (const char *); +static void write_indent (int); +static rtx identity_fn (rtx); +static rtx zero_fn (rtx); +static rtx one_fn (rtx); +static rtx max_fn (rtx); +static rtx min_fn (rtx); + +#define oballoc(T) XOBNEW (hash_obstack, T) +#define oballocvec(T, N) XOBNEWVEC (hash_obstack, T, (N)) + +/* Hash table for sharing RTL and strings. */ + +/* Each hash table slot is a bucket containing a chain of these structures. + Strings are given negative hash codes; RTL expressions are given positive + hash codes. */ + +struct attr_hash +{ + struct attr_hash *next; /* Next structure in the bucket. */ + int hashcode; /* Hash code of this rtx or string. */ + union + { + char *str; /* The string (negative hash codes) */ + rtx rtl; /* or the RTL recorded here. */ + } u; +}; + +/* Now here is the hash table. When recording an RTL, it is added to + the slot whose index is the hash code mod the table size. Note + that the hash table is used for several kinds of RTL (see attr_rtx) + and for strings. While all these live in the same table, they are + completely independent, and the hash code is computed differently + for each. */ + +#define RTL_HASH_SIZE 4093 +static struct attr_hash *attr_hash_table[RTL_HASH_SIZE]; + +/* Here is how primitive or already-shared RTL's hash + codes are made. */ +#define RTL_HASH(RTL) ((long) (RTL) & 0777777) + +/* Add an entry to the hash table for RTL with hash code HASHCODE. */ + +static void +attr_hash_add_rtx (int hashcode, rtx rtl) +{ + struct attr_hash *h; + + h = XOBNEW (hash_obstack, struct attr_hash); + h->hashcode = hashcode; + h->u.rtl = rtl; + h->next = attr_hash_table[hashcode % RTL_HASH_SIZE]; + attr_hash_table[hashcode % RTL_HASH_SIZE] = h; +} + +/* Add an entry to the hash table for STRING with hash code HASHCODE. */ + +static void +attr_hash_add_string (int hashcode, char *str) +{ + struct attr_hash *h; + + h = XOBNEW (hash_obstack, struct attr_hash); + h->hashcode = -hashcode; + h->u.str = str; + h->next = attr_hash_table[hashcode % RTL_HASH_SIZE]; + attr_hash_table[hashcode % RTL_HASH_SIZE] = h; +} + +/* Generate an RTL expression, but avoid duplicates. + Set the ATTR_PERMANENT_P flag for these permanent objects. + + In some cases we cannot uniquify; then we return an ordinary + impermanent rtx with ATTR_PERMANENT_P clear. + + Args are as follows: + + rtx attr_rtx (code, [element1, ..., elementn]) */ + +static rtx +attr_rtx_1 (enum rtx_code code, va_list p) +{ + rtx rt_val = NULL_RTX;/* RTX to return to caller... */ + int hashcode; + struct attr_hash *h; + struct obstack *old_obstack = rtl_obstack; + + /* For each of several cases, search the hash table for an existing entry. + Use that entry if one is found; otherwise create a new RTL and add it + to the table. */ + + if (GET_RTX_CLASS (code) == RTX_UNARY) + { + rtx arg0 = va_arg (p, rtx); + + /* A permanent object cannot point to impermanent ones. */ + if (! ATTR_PERMANENT_P (arg0)) + { + rt_val = rtx_alloc (code); + XEXP (rt_val, 0) = arg0; + return rt_val; + } + + hashcode = ((HOST_WIDE_INT) code + RTL_HASH (arg0)); + for (h = attr_hash_table[hashcode % RTL_HASH_SIZE]; h; h = h->next) + if (h->hashcode == hashcode + && GET_CODE (h->u.rtl) == code + && XEXP (h->u.rtl, 0) == arg0) + return h->u.rtl; + + if (h == 0) + { + rtl_obstack = hash_obstack; + rt_val = rtx_alloc (code); + XEXP (rt_val, 0) = arg0; + } + } + else if (GET_RTX_CLASS (code) == RTX_BIN_ARITH + || GET_RTX_CLASS (code) == RTX_COMM_ARITH + || GET_RTX_CLASS (code) == RTX_COMPARE + || GET_RTX_CLASS (code) == RTX_COMM_COMPARE) + { + rtx arg0 = va_arg (p, rtx); + rtx arg1 = va_arg (p, rtx); + + /* A permanent object cannot point to impermanent ones. */ + if (! ATTR_PERMANENT_P (arg0) || ! ATTR_PERMANENT_P (arg1)) + { + rt_val = rtx_alloc (code); + XEXP (rt_val, 0) = arg0; + XEXP (rt_val, 1) = arg1; + return rt_val; + } + + hashcode = ((HOST_WIDE_INT) code + RTL_HASH (arg0) + RTL_HASH (arg1)); + for (h = attr_hash_table[hashcode % RTL_HASH_SIZE]; h; h = h->next) + if (h->hashcode == hashcode + && GET_CODE (h->u.rtl) == code + && XEXP (h->u.rtl, 0) == arg0 + && XEXP (h->u.rtl, 1) == arg1) + return h->u.rtl; + + if (h == 0) + { + rtl_obstack = hash_obstack; + rt_val = rtx_alloc (code); + XEXP (rt_val, 0) = arg0; + XEXP (rt_val, 1) = arg1; + } + } + else if (GET_RTX_LENGTH (code) == 1 + && GET_RTX_FORMAT (code)[0] == 's') + { + char *arg0 = va_arg (p, char *); + + arg0 = DEF_ATTR_STRING (arg0); + + hashcode = ((HOST_WIDE_INT) code + RTL_HASH (arg0)); + for (h = attr_hash_table[hashcode % RTL_HASH_SIZE]; h; h = h->next) + if (h->hashcode == hashcode + && GET_CODE (h->u.rtl) == code + && XSTR (h->u.rtl, 0) == arg0) + return h->u.rtl; + + if (h == 0) + { + rtl_obstack = hash_obstack; + rt_val = rtx_alloc (code); + XSTR (rt_val, 0) = arg0; + } + } + else if (GET_RTX_LENGTH (code) == 2 + && GET_RTX_FORMAT (code)[0] == 's' + && GET_RTX_FORMAT (code)[1] == 's') + { + char *arg0 = va_arg (p, char *); + char *arg1 = va_arg (p, char *); + + hashcode = ((HOST_WIDE_INT) code + RTL_HASH (arg0) + RTL_HASH (arg1)); + for (h = attr_hash_table[hashcode % RTL_HASH_SIZE]; h; h = h->next) + if (h->hashcode == hashcode + && GET_CODE (h->u.rtl) == code + && XSTR (h->u.rtl, 0) == arg0 + && XSTR (h->u.rtl, 1) == arg1) + return h->u.rtl; + + if (h == 0) + { + rtl_obstack = hash_obstack; + rt_val = rtx_alloc (code); + XSTR (rt_val, 0) = arg0; + XSTR (rt_val, 1) = arg1; + } + } + else if (code == CONST_INT) + { + HOST_WIDE_INT arg0 = va_arg (p, HOST_WIDE_INT); + if (arg0 == 0) + return false_rtx; + else if (arg0 == 1) + return true_rtx; + else + goto nohash; + } + else + { + int i; /* Array indices... */ + const char *fmt; /* Current rtx's format... */ + nohash: + rt_val = rtx_alloc (code); /* Allocate the storage space. */ + + fmt = GET_RTX_FORMAT (code); /* Find the right format... */ + for (i = 0; i < GET_RTX_LENGTH (code); i++) + { + switch (*fmt++) + { + case '0': /* Unused field. */ + break; + + case 'i': /* An integer? */ + XINT (rt_val, i) = va_arg (p, int); + break; + + case 'w': /* A wide integer? */ + XWINT (rt_val, i) = va_arg (p, HOST_WIDE_INT); + break; + + case 's': /* A string? */ + XSTR (rt_val, i) = va_arg (p, char *); + break; + + case 'e': /* An expression? */ + case 'u': /* An insn? Same except when printing. */ + XEXP (rt_val, i) = va_arg (p, rtx); + break; + + case 'E': /* An RTX vector? */ + XVEC (rt_val, i) = va_arg (p, rtvec); + break; + + default: + gcc_unreachable (); + } + } + return rt_val; + } + + rtl_obstack = old_obstack; + attr_hash_add_rtx (hashcode, rt_val); + ATTR_PERMANENT_P (rt_val) = 1; + return rt_val; +} + +static rtx +attr_rtx (enum rtx_code code, ...) +{ + rtx result; + va_list p; + + va_start (p, code); + result = attr_rtx_1 (code, p); + va_end (p); + return result; +} + +/* Create a new string printed with the printf line arguments into a space + of at most LEN bytes: + + rtx attr_printf (len, format, [arg1, ..., argn]) */ + +static char * +attr_printf (unsigned int len, const char *fmt, ...) +{ + char str[256]; + va_list p; + + va_start (p, fmt); + + gcc_assert (len < sizeof str); /* Leave room for \0. */ + + vsprintf (str, fmt, p); + va_end (p); + + return DEF_ATTR_STRING (str); +} + +static rtx +attr_eq (const char *name, const char *value) +{ + return attr_rtx (EQ_ATTR, DEF_ATTR_STRING (name), DEF_ATTR_STRING (value)); +} + +static const char * +attr_numeral (int n) +{ + return XSTR (make_numeric_value (n), 0); +} + +/* Return a permanent (possibly shared) copy of a string STR (not assumed + to be null terminated) with LEN bytes. */ + +static char * +attr_string (const char *str, int len) +{ + struct attr_hash *h; + int hashcode; + int i; + char *new_str; + + /* Compute the hash code. */ + hashcode = (len + 1) * 613 + (unsigned) str[0]; + for (i = 1; i < len; i += 2) + hashcode = ((hashcode * 613) + (unsigned) str[i]); + if (hashcode < 0) + hashcode = -hashcode; + + /* Search the table for the string. */ + for (h = attr_hash_table[hashcode % RTL_HASH_SIZE]; h; h = h->next) + if (h->hashcode == -hashcode && h->u.str[0] == str[0] + && !strncmp (h->u.str, str, len)) + return h->u.str; /* <-- return if found. */ + + /* Not found; create a permanent copy and add it to the hash table. */ + new_str = XOBNEWVAR (hash_obstack, char, len + 1); + memcpy (new_str, str, len); + new_str[len] = '\0'; + attr_hash_add_string (hashcode, new_str); + + return new_str; /* Return the new string. */ +} + +/* Check two rtx's for equality of contents, + taking advantage of the fact that if both are hashed + then they can't be equal unless they are the same object. */ + +static int +attr_equal_p (rtx x, rtx y) +{ + return (x == y || (! (ATTR_PERMANENT_P (x) && ATTR_PERMANENT_P (y)) + && rtx_equal_p (x, y))); +} + +/* Copy an attribute value expression, + descending to all depths, but not copying any + permanent hashed subexpressions. */ + +static rtx +attr_copy_rtx (rtx orig) +{ + rtx copy; + int i, j; + RTX_CODE code; + const char *format_ptr; + + /* No need to copy a permanent object. */ + if (ATTR_PERMANENT_P (orig)) + return orig; + + code = GET_CODE (orig); + + switch (code) + { + case REG: + case CONST_INT: + case CONST_DOUBLE: + case CONST_VECTOR: + case SYMBOL_REF: + case CODE_LABEL: + case PC: + case CC0: + return orig; + + default: + break; + } + + copy = rtx_alloc (code); + PUT_MODE (copy, GET_MODE (orig)); + ATTR_IND_SIMPLIFIED_P (copy) = ATTR_IND_SIMPLIFIED_P (orig); + ATTR_CURR_SIMPLIFIED_P (copy) = ATTR_CURR_SIMPLIFIED_P (orig); + ATTR_PERMANENT_P (copy) = ATTR_PERMANENT_P (orig); + + 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) = attr_copy_rtx (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) = attr_copy_rtx (XVECEXP (orig, i, j)); + } + break; + + case 'n': + case 'i': + XINT (copy, i) = XINT (orig, i); + break; + + case 'w': + XWINT (copy, i) = XWINT (orig, i); + break; + + case 's': + case 'S': + XSTR (copy, i) = XSTR (orig, i); + break; + + default: + gcc_unreachable (); + } + } + return copy; +} + +/* Given a test expression for an attribute, ensure it is validly formed. + IS_CONST indicates whether the expression is constant for each compiler + run (a constant expression may not test any particular insn). + + Convert (eq_attr "att" "a1,a2") to (ior (eq_attr ... ) (eq_attrq ..)) + and (eq_attr "att" "!a1") to (not (eq_attr "att" "a1")). Do the latter + test first so that (eq_attr "att" "!a1,a2,a3") works as expected. + + Update the string address in EQ_ATTR expression to be the same used + in the attribute (or `alternative_name') to speed up subsequent + `find_attr' calls and eliminate most `strcmp' calls. + + Return the new expression, if any. */ + +static rtx +check_attr_test (rtx exp, int is_const, int lineno) +{ + struct attr_desc *attr; + struct attr_value *av; + const char *name_ptr, *p; + rtx orexp, newexp; + + switch (GET_CODE (exp)) + { + case EQ_ATTR: + /* Handle negation test. */ + if (XSTR (exp, 1)[0] == '!') + return check_attr_test (attr_rtx (NOT, + attr_eq (XSTR (exp, 0), + &XSTR (exp, 1)[1])), + is_const, lineno); + + else if (n_comma_elts (XSTR (exp, 1)) == 1) + { + attr = find_attr (&XSTR (exp, 0), 0); + if (attr == NULL) + { + if (! strcmp (XSTR (exp, 0), "alternative")) + return mk_attr_alt (1 << atoi (XSTR (exp, 1))); + else + fatal ("unknown attribute `%s' in EQ_ATTR", XSTR (exp, 0)); + } + + if (is_const && ! attr->is_const) + fatal ("constant expression uses insn attribute `%s' in EQ_ATTR", + XSTR (exp, 0)); + + /* Copy this just to make it permanent, + so expressions using it can be permanent too. */ + exp = attr_eq (XSTR (exp, 0), XSTR (exp, 1)); + + /* It shouldn't be possible to simplify the value given to a + constant attribute, so don't expand this until it's time to + write the test expression. */ + if (attr->is_const) + ATTR_IND_SIMPLIFIED_P (exp) = 1; + + if (attr->is_numeric) + { + for (p = XSTR (exp, 1); *p; p++) + if (! ISDIGIT (*p)) + fatal ("attribute `%s' takes only numeric values", + XSTR (exp, 0)); + } + else + { + for (av = attr->first_value; av; av = av->next) + if (GET_CODE (av->value) == CONST_STRING + && ! strcmp (XSTR (exp, 1), XSTR (av->value, 0))) + break; + + if (av == NULL) + fatal ("unknown value `%s' for `%s' attribute", + XSTR (exp, 1), XSTR (exp, 0)); + } + } + else + { + if (! strcmp (XSTR (exp, 0), "alternative")) + { + int set = 0; + + name_ptr = XSTR (exp, 1); + while ((p = next_comma_elt (&name_ptr)) != NULL) + set |= 1 << atoi (p); + + return mk_attr_alt (set); + } + else + { + /* Make an IOR tree of the possible values. */ + orexp = false_rtx; + name_ptr = XSTR (exp, 1); + while ((p = next_comma_elt (&name_ptr)) != NULL) + { + newexp = attr_eq (XSTR (exp, 0), p); + orexp = insert_right_side (IOR, orexp, newexp, -2, -2); + } + + return check_attr_test (orexp, is_const, lineno); + } + } + break; + + case ATTR_FLAG: + break; + + case CONST_INT: + /* Either TRUE or FALSE. */ + if (XWINT (exp, 0)) + return true_rtx; + else + return false_rtx; + + case IOR: + case AND: + XEXP (exp, 0) = check_attr_test (XEXP (exp, 0), is_const, lineno); + XEXP (exp, 1) = check_attr_test (XEXP (exp, 1), is_const, lineno); + break; + + case NOT: + XEXP (exp, 0) = check_attr_test (XEXP (exp, 0), is_const, lineno); + break; + + case MATCH_OPERAND: + if (is_const) + fatal ("RTL operator \"%s\" not valid in constant attribute test", + GET_RTX_NAME (GET_CODE (exp))); + /* These cases can't be simplified. */ + ATTR_IND_SIMPLIFIED_P (exp) = 1; + break; + + case LE: case LT: case GT: case GE: + case LEU: case LTU: case GTU: case GEU: + case NE: case EQ: + if (GET_CODE (XEXP (exp, 0)) == SYMBOL_REF + && GET_CODE (XEXP (exp, 1)) == SYMBOL_REF) + exp = attr_rtx (GET_CODE (exp), + attr_rtx (SYMBOL_REF, XSTR (XEXP (exp, 0), 0)), + attr_rtx (SYMBOL_REF, XSTR (XEXP (exp, 1), 0))); + /* These cases can't be simplified. */ + ATTR_IND_SIMPLIFIED_P (exp) = 1; + break; + + case SYMBOL_REF: + if (is_const) + { + /* These cases are valid for constant attributes, but can't be + simplified. */ + exp = attr_rtx (SYMBOL_REF, XSTR (exp, 0)); + ATTR_IND_SIMPLIFIED_P (exp) = 1; + break; + } + default: + fatal ("RTL operator \"%s\" not valid in attribute test", + GET_RTX_NAME (GET_CODE (exp))); + } + + return exp; +} + +/* Given an expression, ensure that it is validly formed and that all named + attribute values are valid for the given attribute. Issue a fatal error + if not. If no attribute is specified, assume a numeric attribute. + + Return a perhaps modified replacement expression for the value. */ + +static rtx +check_attr_value (rtx exp, struct attr_desc *attr) +{ + struct attr_value *av; + const char *p; + int i; + + switch (GET_CODE (exp)) + { + case CONST_INT: + if (attr && ! attr->is_numeric) + { + message_with_line (attr->lineno, + "CONST_INT not valid for non-numeric attribute %s", + attr->name); + have_error = 1; + break; + } + + if (INTVAL (exp) < 0) + { + message_with_line (attr->lineno, + "negative numeric value specified for attribute %s", + attr->name); + have_error = 1; + break; + } + break; + + case CONST_STRING: + if (! strcmp (XSTR (exp, 0), "*")) + break; + + if (attr == 0 || attr->is_numeric) + { + p = XSTR (exp, 0); + for (; *p; p++) + if (! ISDIGIT (*p)) + { + message_with_line (attr ? attr->lineno : 0, + "non-numeric value for numeric attribute %s", + attr ? attr->name : "internal"); + have_error = 1; + break; + } + break; + } + + for (av = attr->first_value; av; av = av->next) + if (GET_CODE (av->value) == CONST_STRING + && ! strcmp (XSTR (av->value, 0), XSTR (exp, 0))) + break; + + if (av == NULL) + { + message_with_line (attr->lineno, + "unknown value `%s' for `%s' attribute", + XSTR (exp, 0), attr ? attr->name : "internal"); + have_error = 1; + } + break; + + case IF_THEN_ELSE: + XEXP (exp, 0) = check_attr_test (XEXP (exp, 0), + attr ? attr->is_const : 0, + attr ? attr->lineno : 0); + XEXP (exp, 1) = check_attr_value (XEXP (exp, 1), attr); + XEXP (exp, 2) = check_attr_value (XEXP (exp, 2), attr); + break; + + case PLUS: + case MINUS: + case MULT: + case DIV: + case MOD: + if (attr && !attr->is_numeric) + { + message_with_line (attr->lineno, + "invalid operation `%s' for non-numeric attribute value", + GET_RTX_NAME (GET_CODE (exp))); + have_error = 1; + break; + } + /* Fall through. */ + + case IOR: + case AND: + XEXP (exp, 0) = check_attr_value (XEXP (exp, 0), attr); + XEXP (exp, 1) = check_attr_value (XEXP (exp, 1), attr); + break; + + case FFS: + case CLZ: + case CTZ: + case POPCOUNT: + case PARITY: + case BSWAP: + XEXP (exp, 0) = check_attr_value (XEXP (exp, 0), attr); + break; + + case COND: + if (XVECLEN (exp, 0) % 2 != 0) + { + message_with_line (attr->lineno, + "first operand of COND must have even length"); + have_error = 1; + break; + } + + for (i = 0; i < XVECLEN (exp, 0); i += 2) + { + XVECEXP (exp, 0, i) = check_attr_test (XVECEXP (exp, 0, i), + attr ? attr->is_const : 0, + attr ? attr->lineno : 0); + XVECEXP (exp, 0, i + 1) + = check_attr_value (XVECEXP (exp, 0, i + 1), attr); + } + + XEXP (exp, 1) = check_attr_value (XEXP (exp, 1), attr); + break; + + case ATTR: + { + struct attr_desc *attr2 = find_attr (&XSTR (exp, 0), 0); + if (attr2 == NULL) + { + message_with_line (attr ? attr->lineno : 0, + "unknown attribute `%s' in ATTR", + XSTR (exp, 0)); + have_error = 1; + } + else if (attr && attr->is_const && ! attr2->is_const) + { + message_with_line (attr->lineno, + "non-constant attribute `%s' referenced from `%s'", + XSTR (exp, 0), attr->name); + have_error = 1; + } + else if (attr + && attr->is_numeric != attr2->is_numeric) + { + message_with_line (attr->lineno, + "numeric attribute mismatch calling `%s' from `%s'", + XSTR (exp, 0), attr->name); + have_error = 1; + } + } + break; + + case SYMBOL_REF: + /* A constant SYMBOL_REF is valid as a constant attribute test and + is expanded later by make_canonical into a COND. In a non-constant + attribute test, it is left be. */ + return attr_rtx (SYMBOL_REF, XSTR (exp, 0)); + + default: + message_with_line (attr ? attr->lineno : 0, + "invalid operation `%s' for attribute value", + GET_RTX_NAME (GET_CODE (exp))); + have_error = 1; + break; + } + + return exp; +} + +/* Given an SET_ATTR_ALTERNATIVE expression, convert to the canonical SET. + It becomes a COND with each test being (eq_attr "alternative" "n") */ + +static rtx +convert_set_attr_alternative (rtx exp, struct insn_def *id) +{ + int num_alt = id->num_alternatives; + rtx condexp; + int i; + + if (XVECLEN (exp, 1) != num_alt) + { + message_with_line (id->lineno, + "bad number of entries in SET_ATTR_ALTERNATIVE"); + have_error = 1; + return NULL_RTX; + } + + /* Make a COND with all tests but the last. Select the last value via the + default. */ + condexp = rtx_alloc (COND); + XVEC (condexp, 0) = rtvec_alloc ((num_alt - 1) * 2); + + for (i = 0; i < num_alt - 1; i++) + { + const char *p; + p = attr_numeral (i); + + XVECEXP (condexp, 0, 2 * i) = attr_eq (alternative_name, p); + XVECEXP (condexp, 0, 2 * i + 1) = XVECEXP (exp, 1, i); + } + + XEXP (condexp, 1) = XVECEXP (exp, 1, i); + + return attr_rtx (SET, attr_rtx (ATTR, XSTR (exp, 0)), condexp); +} + +/* Given a SET_ATTR, convert to the appropriate SET. If a comma-separated + list of values is given, convert to SET_ATTR_ALTERNATIVE first. */ + +static rtx +convert_set_attr (rtx exp, struct insn_def *id) +{ + rtx newexp; + const char *name_ptr; + char *p; + int n; + + /* See how many alternative specified. */ + n = n_comma_elts (XSTR (exp, 1)); + if (n == 1) + return attr_rtx (SET, + attr_rtx (ATTR, XSTR (exp, 0)), + attr_rtx (CONST_STRING, XSTR (exp, 1))); + + newexp = rtx_alloc (SET_ATTR_ALTERNATIVE); + XSTR (newexp, 0) = XSTR (exp, 0); + XVEC (newexp, 1) = rtvec_alloc (n); + + /* Process each comma-separated name. */ + name_ptr = XSTR (exp, 1); + n = 0; + while ((p = next_comma_elt (&name_ptr)) != NULL) + XVECEXP (newexp, 1, n++) = attr_rtx (CONST_STRING, p); + + return convert_set_attr_alternative (newexp, id); +} + +/* Scan all definitions, checking for validity. Also, convert any SET_ATTR + and SET_ATTR_ALTERNATIVE expressions to the corresponding SET + expressions. */ + +static void +check_defs (void) +{ + struct insn_def *id; + struct attr_desc *attr; + int i; + rtx value; + + for (id = defs; id; id = id->next) + { + if (XVEC (id->def, id->vec_idx) == NULL) + continue; + + for (i = 0; i < XVECLEN (id->def, id->vec_idx); i++) + { + value = XVECEXP (id->def, id->vec_idx, i); + switch (GET_CODE (value)) + { + case SET: + if (GET_CODE (XEXP (value, 0)) != ATTR) + { + message_with_line (id->lineno, "bad attribute set"); + have_error = 1; + value = NULL_RTX; + } + break; + + case SET_ATTR_ALTERNATIVE: + value = convert_set_attr_alternative (value, id); + break; + + case SET_ATTR: + value = convert_set_attr (value, id); + break; + + default: + message_with_line (id->lineno, "invalid attribute code %s", + GET_RTX_NAME (GET_CODE (value))); + have_error = 1; + value = NULL_RTX; + } + if (value == NULL_RTX) + continue; + + if ((attr = find_attr (&XSTR (XEXP (value, 0), 0), 0)) == NULL) + { + message_with_line (id->lineno, "unknown attribute %s", + XSTR (XEXP (value, 0), 0)); + have_error = 1; + continue; + } + + XVECEXP (id->def, id->vec_idx, i) = value; + XEXP (value, 1) = check_attr_value (XEXP (value, 1), attr); + } + } +} + +/* Given a valid expression for an attribute value, remove any IF_THEN_ELSE + expressions by converting them into a COND. This removes cases from this + program. Also, replace an attribute value of "*" with the default attribute + value. */ + +static rtx +make_canonical (struct attr_desc *attr, rtx exp) +{ + int i; + rtx newexp; + + switch (GET_CODE (exp)) + { + case CONST_INT: + exp = make_numeric_value (INTVAL (exp)); + break; + + case CONST_STRING: + if (! strcmp (XSTR (exp, 0), "*")) + { + if (attr == 0 || attr->default_val == 0) + fatal ("(attr_value \"*\") used in invalid context"); + exp = attr->default_val->value; + } + else + XSTR (exp, 0) = DEF_ATTR_STRING (XSTR (exp, 0)); + + break; + + case SYMBOL_REF: + if (!attr->is_const || ATTR_IND_SIMPLIFIED_P (exp)) + break; + /* The SYMBOL_REF is constant for a given run, so mark it as unchanging. + This makes the COND something that won't be considered an arbitrary + expression by walk_attr_value. */ + ATTR_IND_SIMPLIFIED_P (exp) = 1; + exp = check_attr_value (exp, attr); + break; + + case IF_THEN_ELSE: + newexp = rtx_alloc (COND); + XVEC (newexp, 0) = rtvec_alloc (2); + XVECEXP (newexp, 0, 0) = XEXP (exp, 0); + XVECEXP (newexp, 0, 1) = XEXP (exp, 1); + + XEXP (newexp, 1) = XEXP (exp, 2); + + exp = newexp; + /* Fall through to COND case since this is now a COND. */ + + case COND: + { + int allsame = 1; + rtx defval; + + /* First, check for degenerate COND. */ + if (XVECLEN (exp, 0) == 0) + return make_canonical (attr, XEXP (exp, 1)); + defval = XEXP (exp, 1) = make_canonical (attr, XEXP (exp, 1)); + + for (i = 0; i < XVECLEN (exp, 0); i += 2) + { + XVECEXP (exp, 0, i) = copy_boolean (XVECEXP (exp, 0, i)); + XVECEXP (exp, 0, i + 1) + = make_canonical (attr, XVECEXP (exp, 0, i + 1)); + if (! rtx_equal_p (XVECEXP (exp, 0, i + 1), defval)) + allsame = 0; + } + if (allsame) + return defval; + } + break; + + default: + break; + } + + return exp; +} + +static rtx +copy_boolean (rtx exp) +{ + if (GET_CODE (exp) == AND || GET_CODE (exp) == IOR) + return attr_rtx (GET_CODE (exp), copy_boolean (XEXP (exp, 0)), + copy_boolean (XEXP (exp, 1))); + if (GET_CODE (exp) == MATCH_OPERAND) + { + XSTR (exp, 1) = DEF_ATTR_STRING (XSTR (exp, 1)); + XSTR (exp, 2) = DEF_ATTR_STRING (XSTR (exp, 2)); + } + else if (GET_CODE (exp) == EQ_ATTR) + { + XSTR (exp, 0) = DEF_ATTR_STRING (XSTR (exp, 0)); + XSTR (exp, 1) = DEF_ATTR_STRING (XSTR (exp, 1)); + } + + return exp; +} + +/* Given a value and an attribute description, return a `struct attr_value *' + that represents that value. This is either an existing structure, if the + value has been previously encountered, or a newly-created structure. + + `insn_code' is the code of an insn whose attribute has the specified + value (-2 if not processing an insn). We ensure that all insns for + a given value have the same number of alternatives if the value checks + alternatives. */ + +static struct attr_value * +get_attr_value (rtx value, struct attr_desc *attr, int insn_code) +{ + struct attr_value *av; + int num_alt = 0; + + value = make_canonical (attr, value); + if (compares_alternatives_p (value)) + { + if (insn_code < 0 || insn_alternatives == NULL) + fatal ("(eq_attr \"alternatives\" ...) used in non-insn context"); + else + num_alt = insn_alternatives[insn_code]; + } + + for (av = attr->first_value; av; av = av->next) + if (rtx_equal_p (value, av->value) + && (num_alt == 0 || av->first_insn == NULL + || insn_alternatives[av->first_insn->def->insn_code])) + return av; + + av = oballoc (struct attr_value); + av->value = value; + av->next = attr->first_value; + attr->first_value = av; + av->first_insn = NULL; + av->num_insns = 0; + av->has_asm_insn = 0; + + return av; +} + +/* After all DEFINE_DELAYs have been read in, create internal attributes + to generate the required routines. + + First, we compute the number of delay slots for each insn (as a COND of + each of the test expressions in DEFINE_DELAYs). Then, if more than one + delay type is specified, we compute a similar function giving the + DEFINE_DELAY ordinal for each insn. + + Finally, for each [DEFINE_DELAY, slot #] pair, we compute an attribute that + tells whether a given insn can be in that delay slot. + + Normal attribute filling and optimization expands these to contain the + information needed to handle delay slots. */ + +static void +expand_delays (void) +{ + struct delay_desc *delay; + rtx condexp; + rtx newexp; + int i; + char *p; + + /* First, generate data for `num_delay_slots' function. */ + + condexp = rtx_alloc (COND); + XVEC (condexp, 0) = rtvec_alloc (num_delays * 2); + XEXP (condexp, 1) = make_numeric_value (0); + + for (i = 0, delay = delays; delay; i += 2, delay = delay->next) + { + XVECEXP (condexp, 0, i) = XEXP (delay->def, 0); + XVECEXP (condexp, 0, i + 1) + = make_numeric_value (XVECLEN (delay->def, 1) / 3); + } + + make_internal_attr (num_delay_slots_str, condexp, ATTR_NONE); + + /* If more than one delay type, do the same for computing the delay type. */ + if (num_delays > 1) + { + condexp = rtx_alloc (COND); + XVEC (condexp, 0) = rtvec_alloc (num_delays * 2); + XEXP (condexp, 1) = make_numeric_value (0); + + for (i = 0, delay = delays; delay; i += 2, delay = delay->next) + { + XVECEXP (condexp, 0, i) = XEXP (delay->def, 0); + XVECEXP (condexp, 0, i + 1) = make_numeric_value (delay->num); + } + + make_internal_attr (delay_type_str, condexp, ATTR_SPECIAL); + } + + /* For each delay possibility and delay slot, compute an eligibility + attribute for non-annulled insns and for each type of annulled (annul + if true and annul if false). */ + for (delay = delays; delay; delay = delay->next) + { + for (i = 0; i < XVECLEN (delay->def, 1); i += 3) + { + condexp = XVECEXP (delay->def, 1, i); + if (condexp == 0) + condexp = false_rtx; + newexp = attr_rtx (IF_THEN_ELSE, condexp, + make_numeric_value (1), make_numeric_value (0)); + + p = attr_printf (sizeof "*delay__" + MAX_DIGITS * 2, + "*delay_%d_%d", delay->num, i / 3); + make_internal_attr (p, newexp, ATTR_SPECIAL); + + if (have_annul_true) + { + condexp = XVECEXP (delay->def, 1, i + 1); + if (condexp == 0) condexp = false_rtx; + newexp = attr_rtx (IF_THEN_ELSE, condexp, + make_numeric_value (1), + make_numeric_value (0)); + p = attr_printf (sizeof "*annul_true__" + MAX_DIGITS * 2, + "*annul_true_%d_%d", delay->num, i / 3); + make_internal_attr (p, newexp, ATTR_SPECIAL); + } + + if (have_annul_false) + { + condexp = XVECEXP (delay->def, 1, i + 2); + if (condexp == 0) condexp = false_rtx; + newexp = attr_rtx (IF_THEN_ELSE, condexp, + make_numeric_value (1), + make_numeric_value (0)); + p = attr_printf (sizeof "*annul_false__" + MAX_DIGITS * 2, + "*annul_false_%d_%d", delay->num, i / 3); + make_internal_attr (p, newexp, ATTR_SPECIAL); + } + } + } +} + +/* Once all attributes and insns have been read and checked, we construct for + each attribute value a list of all the insns that have that value for + the attribute. */ + +static void +fill_attr (struct attr_desc *attr) +{ + struct attr_value *av; + struct insn_ent *ie; + struct insn_def *id; + int i; + rtx value; + + /* Don't fill constant attributes. The value is independent of + any particular insn. */ + if (attr->is_const) + return; + + for (id = defs; id; id = id->next) + { + /* If no value is specified for this insn for this attribute, use the + default. */ + value = NULL; + if (XVEC (id->def, id->vec_idx)) + for (i = 0; i < XVECLEN (id->def, id->vec_idx); i++) + if (! strcmp_check (XSTR (XEXP (XVECEXP (id->def, id->vec_idx, i), 0), 0), + attr->name)) + value = XEXP (XVECEXP (id->def, id->vec_idx, i), 1); + + if (value == NULL) + av = attr->default_val; + else + av = get_attr_value (value, attr, id->insn_code); + + ie = oballoc (struct insn_ent); + ie->def = id; + insert_insn_ent (av, ie); + } +} + +/* Given an expression EXP, see if it is a COND or IF_THEN_ELSE that has a + test that checks relative positions of insns (uses MATCH_DUP or PC). + If so, replace it with what is obtained by passing the expression to + ADDRESS_FN. If not but it is a COND or IF_THEN_ELSE, call this routine + recursively on each value (including the default value). Otherwise, + return the value returned by NO_ADDRESS_FN applied to EXP. */ + +static rtx +substitute_address (rtx exp, rtx (*no_address_fn) (rtx), + rtx (*address_fn) (rtx)) +{ + int i; + rtx newexp; + + if (GET_CODE (exp) == COND) + { + /* See if any tests use addresses. */ + address_used = 0; + for (i = 0; i < XVECLEN (exp, 0); i += 2) + walk_attr_value (XVECEXP (exp, 0, i)); + + if (address_used) + return (*address_fn) (exp); + + /* Make a new copy of this COND, replacing each element. */ + newexp = rtx_alloc (COND); + XVEC (newexp, 0) = rtvec_alloc (XVECLEN (exp, 0)); + for (i = 0; i < XVECLEN (exp, 0); i += 2) + { + XVECEXP (newexp, 0, i) = XVECEXP (exp, 0, i); + XVECEXP (newexp, 0, i + 1) + = substitute_address (XVECEXP (exp, 0, i + 1), + no_address_fn, address_fn); + } + + XEXP (newexp, 1) = substitute_address (XEXP (exp, 1), + no_address_fn, address_fn); + + return newexp; + } + + else if (GET_CODE (exp) == IF_THEN_ELSE) + { + address_used = 0; + walk_attr_value (XEXP (exp, 0)); + if (address_used) + return (*address_fn) (exp); + + return attr_rtx (IF_THEN_ELSE, + substitute_address (XEXP (exp, 0), + no_address_fn, address_fn), + substitute_address (XEXP (exp, 1), + no_address_fn, address_fn), + substitute_address (XEXP (exp, 2), + no_address_fn, address_fn)); + } + + return (*no_address_fn) (exp); +} + +/* Make new attributes from the `length' attribute. The following are made, + each corresponding to a function called from `shorten_branches' or + `get_attr_length': + + *insn_default_length This is the length of the insn to be returned + by `get_attr_length' before `shorten_branches' + has been called. In each case where the length + depends on relative addresses, the largest + possible is used. This routine is also used + to compute the initial size of the insn. + + *insn_variable_length_p This returns 1 if the insn's length depends + on relative addresses, zero otherwise. + + *insn_current_length This is only called when it is known that the + insn has a variable length and returns the + current length, based on relative addresses. + */ + +static void +make_length_attrs (void) +{ + static const char *new_names[] = + { + "*insn_default_length", + "*insn_min_length", + "*insn_variable_length_p", + "*insn_current_length" + }; + static rtx (*const no_address_fn[]) (rtx) + = {identity_fn,identity_fn, zero_fn, zero_fn}; + static rtx (*const address_fn[]) (rtx) + = {max_fn, min_fn, one_fn, identity_fn}; + size_t i; + struct attr_desc *length_attr, *new_attr; + struct attr_value *av, *new_av; + struct insn_ent *ie, *new_ie; + + /* See if length attribute is defined. If so, it must be numeric. Make + it special so we don't output anything for it. */ + length_attr = find_attr (&length_str, 0); + if (length_attr == 0) + return; + + if (! length_attr->is_numeric) + fatal ("length attribute must be numeric"); + + length_attr->is_const = 0; + length_attr->is_special = 1; + + /* Make each new attribute, in turn. */ + for (i = 0; i < ARRAY_SIZE (new_names); i++) + { + make_internal_attr (new_names[i], + substitute_address (length_attr->default_val->value, + no_address_fn[i], address_fn[i]), + ATTR_NONE); + new_attr = find_attr (&new_names[i], 0); + for (av = length_attr->first_value; av; av = av->next) + for (ie = av->first_insn; ie; ie = ie->next) + { + new_av = get_attr_value (substitute_address (av->value, + no_address_fn[i], + address_fn[i]), + new_attr, ie->def->insn_code); + new_ie = oballoc (struct insn_ent); + new_ie->def = ie->def; + insert_insn_ent (new_av, new_ie); + } + } +} + +/* Utility functions called from above routine. */ + +static rtx +identity_fn (rtx exp) +{ + return exp; +} + +static rtx +zero_fn (rtx exp ATTRIBUTE_UNUSED) +{ + return make_numeric_value (0); +} + +static rtx +one_fn (rtx exp ATTRIBUTE_UNUSED) +{ + return make_numeric_value (1); +} + +static rtx +max_fn (rtx exp) +{ + int unknown; + return make_numeric_value (max_attr_value (exp, &unknown)); +} + +static rtx +min_fn (rtx exp) +{ + int unknown; + return make_numeric_value (min_attr_value (exp, &unknown)); +} + +static void +write_length_unit_log (void) +{ + struct attr_desc *length_attr = find_attr (&length_str, 0); + struct attr_value *av; + struct insn_ent *ie; + unsigned int length_unit_log, length_or; + int unknown = 0; + + if (length_attr == 0) + return; + length_or = or_attr_value (length_attr->default_val->value, &unknown); + for (av = length_attr->first_value; av; av = av->next) + for (ie = av->first_insn; ie; ie = ie->next) + length_or |= or_attr_value (av->value, &unknown); + + if (unknown) + length_unit_log = 0; + else + { + length_or = ~length_or; + for (length_unit_log = 0; length_or & 1; length_or >>= 1) + length_unit_log++; + } + printf ("const int length_unit_log = %u;\n", length_unit_log); +} + +/* Take a COND expression and see if any of the conditions in it can be + simplified. If any are known true or known false for the particular insn + code, the COND can be further simplified. + + Also call ourselves on any COND operations that are values of this COND. + + We do not modify EXP; rather, we make and return a new rtx. */ + +static rtx +simplify_cond (rtx exp, int insn_code, int insn_index) +{ + int i, j; + /* We store the desired contents here, + then build a new expression if they don't match EXP. */ + rtx defval = XEXP (exp, 1); + rtx new_defval = XEXP (exp, 1); + int len = XVECLEN (exp, 0); + rtx *tests = XNEWVEC (rtx, len); + int allsame = 1; + rtx ret; + + /* This lets us free all storage allocated below, if appropriate. */ + obstack_finish (rtl_obstack); + + memcpy (tests, XVEC (exp, 0)->elem, len * sizeof (rtx)); + + /* See if default value needs simplification. */ + if (GET_CODE (defval) == COND) + new_defval = simplify_cond (defval, insn_code, insn_index); + + /* Simplify the subexpressions, and see what tests we can get rid of. */ + + for (i = 0; i < len; i += 2) + { + rtx newtest, newval; + + /* Simplify this test. */ + newtest = simplify_test_exp_in_temp (tests[i], insn_code, insn_index); + tests[i] = newtest; + + newval = tests[i + 1]; + /* See if this value may need simplification. */ + if (GET_CODE (newval) == COND) + newval = simplify_cond (newval, insn_code, insn_index); + + /* Look for ways to delete or combine this test. */ + if (newtest == true_rtx) + { + /* If test is true, make this value the default + and discard this + any following tests. */ + len = i; + defval = tests[i + 1]; + new_defval = newval; + } + + else if (newtest == false_rtx) + { + /* If test is false, discard it and its value. */ + for (j = i; j < len - 2; j++) + tests[j] = tests[j + 2]; + i -= 2; + len -= 2; + } + + else if (i > 0 && attr_equal_p (newval, tests[i - 1])) + { + /* If this value and the value for the prev test are the same, + merge the tests. */ + + tests[i - 2] + = insert_right_side (IOR, tests[i - 2], newtest, + insn_code, insn_index); + + /* Delete this test/value. */ + for (j = i; j < len - 2; j++) + tests[j] = tests[j + 2]; + len -= 2; + i -= 2; + } + + else + tests[i + 1] = newval; + } + + /* If the last test in a COND has the same value + as the default value, that test isn't needed. */ + + while (len > 0 && attr_equal_p (tests[len - 1], new_defval)) + len -= 2; + + /* See if we changed anything. */ + if (len != XVECLEN (exp, 0) || new_defval != XEXP (exp, 1)) + allsame = 0; + else + for (i = 0; i < len; i++) + if (! attr_equal_p (tests[i], XVECEXP (exp, 0, i))) + { + allsame = 0; + break; + } + + if (len == 0) + { + if (GET_CODE (defval) == COND) + ret = simplify_cond (defval, insn_code, insn_index); + else + ret = defval; + } + else if (allsame) + ret = exp; + else + { + rtx newexp = rtx_alloc (COND); + + XVEC (newexp, 0) = rtvec_alloc (len); + memcpy (XVEC (newexp, 0)->elem, tests, len * sizeof (rtx)); + XEXP (newexp, 1) = new_defval; + ret = newexp; + } + free (tests); + return ret; +} + +/* Remove an insn entry from an attribute value. */ + +static void +remove_insn_ent (struct attr_value *av, struct insn_ent *ie) +{ + struct insn_ent *previe; + + if (av->first_insn == ie) + av->first_insn = ie->next; + else + { + for (previe = av->first_insn; previe->next != ie; previe = previe->next) + ; + previe->next = ie->next; + } + + av->num_insns--; + if (ie->def->insn_code == -1) + av->has_asm_insn = 0; + + num_insn_ents--; +} + +/* Insert an insn entry in an attribute value list. */ + +static void +insert_insn_ent (struct attr_value *av, struct insn_ent *ie) +{ + ie->next = av->first_insn; + av->first_insn = ie; + av->num_insns++; + if (ie->def->insn_code == -1) + av->has_asm_insn = 1; + + num_insn_ents++; +} + +/* This is a utility routine to take an expression that is a tree of either + AND or IOR expressions and insert a new term. The new term will be + inserted at the right side of the first node whose code does not match + the root. A new node will be created with the root's code. Its left + side will be the old right side and its right side will be the new + term. + + If the `term' is itself a tree, all its leaves will be inserted. */ + +static rtx +insert_right_side (enum rtx_code code, rtx exp, rtx term, int insn_code, int insn_index) +{ + rtx newexp; + + /* Avoid consing in some special cases. */ + if (code == AND && term == true_rtx) + return exp; + if (code == AND && term == false_rtx) + return false_rtx; + if (code == AND && exp == true_rtx) + return term; + if (code == AND && exp == false_rtx) + return false_rtx; + if (code == IOR && term == true_rtx) + return true_rtx; + if (code == IOR && term == false_rtx) + return exp; + if (code == IOR && exp == true_rtx) + return true_rtx; + if (code == IOR && exp == false_rtx) + return term; + if (attr_equal_p (exp, term)) + return exp; + + if (GET_CODE (term) == code) + { + exp = insert_right_side (code, exp, XEXP (term, 0), + insn_code, insn_index); + exp = insert_right_side (code, exp, XEXP (term, 1), + insn_code, insn_index); + + return exp; + } + + if (GET_CODE (exp) == code) + { + rtx new_rtx = insert_right_side (code, XEXP (exp, 1), + term, insn_code, insn_index); + if (new_rtx != XEXP (exp, 1)) + /* Make a copy of this expression and call recursively. */ + newexp = attr_rtx (code, XEXP (exp, 0), new_rtx); + else + newexp = exp; + } + else + { + /* Insert the new term. */ + newexp = attr_rtx (code, exp, term); + } + + return simplify_test_exp_in_temp (newexp, insn_code, insn_index); +} + +/* If we have an expression which AND's a bunch of + (not (eq_attrq "alternative" "n")) + terms, we may have covered all or all but one of the possible alternatives. + If so, we can optimize. Similarly for IOR's of EQ_ATTR. + + This routine is passed an expression and either AND or IOR. It returns a + bitmask indicating which alternatives are mentioned within EXP. */ + +static int +compute_alternative_mask (rtx exp, enum rtx_code code) +{ + const char *string; + if (GET_CODE (exp) == code) + return compute_alternative_mask (XEXP (exp, 0), code) + | compute_alternative_mask (XEXP (exp, 1), code); + + else if (code == AND && GET_CODE (exp) == NOT + && GET_CODE (XEXP (exp, 0)) == EQ_ATTR + && XSTR (XEXP (exp, 0), 0) == alternative_name) + string = XSTR (XEXP (exp, 0), 1); + + else if (code == IOR && GET_CODE (exp) == EQ_ATTR + && XSTR (exp, 0) == alternative_name) + string = XSTR (exp, 1); + + else if (GET_CODE (exp) == EQ_ATTR_ALT) + { + if (code == AND && XINT (exp, 1)) + return XINT (exp, 0); + + if (code == IOR && !XINT (exp, 1)) + return XINT (exp, 0); + + return 0; + } + else + return 0; + + if (string[1] == 0) + return 1 << (string[0] - '0'); + return 1 << atoi (string); +} + +/* Given I, a single-bit mask, return RTX to compare the `alternative' + attribute with the value represented by that bit. */ + +static rtx +make_alternative_compare (int mask) +{ + return mk_attr_alt (mask); +} + +/* If we are processing an (eq_attr "attr" "value") test, we find the value + of "attr" for this insn code. From that value, we can compute a test + showing when the EQ_ATTR will be true. This routine performs that + computation. If a test condition involves an address, we leave the EQ_ATTR + intact because addresses are only valid for the `length' attribute. + + EXP is the EQ_ATTR expression and VALUE is the value of that attribute + for the insn corresponding to INSN_CODE and INSN_INDEX. */ + +static rtx +evaluate_eq_attr (rtx exp, rtx value, int insn_code, int insn_index) +{ + rtx orexp, andexp; + rtx right; + rtx newexp; + int i; + + switch (GET_CODE (value)) + { + case CONST_STRING: + if (! strcmp_check (XSTR (value, 0), XSTR (exp, 1))) + newexp = true_rtx; + else + newexp = false_rtx; + break; + + case SYMBOL_REF: + { + char *p; + char string[256]; + + gcc_assert (GET_CODE (exp) == EQ_ATTR); + gcc_assert (strlen (XSTR (exp, 0)) + strlen (XSTR (exp, 1)) + 2 + <= 256); + + strcpy (string, XSTR (exp, 0)); + strcat (string, "_"); + strcat (string, XSTR (exp, 1)); + for (p = string; *p; p++) + *p = TOUPPER (*p); + + newexp = attr_rtx (EQ, value, + attr_rtx (SYMBOL_REF, + DEF_ATTR_STRING (string))); + break; + } + + case COND: + /* We construct an IOR of all the cases for which the + requested attribute value is present. Since we start with + FALSE, if it is not present, FALSE will be returned. + + Each case is the AND of the NOT's of the previous conditions with the + current condition; in the default case the current condition is TRUE. + + For each possible COND value, call ourselves recursively. + + The extra TRUE and FALSE expressions will be eliminated by another + call to the simplification routine. */ + + orexp = false_rtx; + andexp = true_rtx; + + for (i = 0; i < XVECLEN (value, 0); i += 2) + { + rtx this_cond = simplify_test_exp_in_temp (XVECEXP (value, 0, i), + insn_code, insn_index); + + right = insert_right_side (AND, andexp, this_cond, + insn_code, insn_index); + right = insert_right_side (AND, right, + evaluate_eq_attr (exp, + XVECEXP (value, 0, + i + 1), + insn_code, insn_index), + insn_code, insn_index); + orexp = insert_right_side (IOR, orexp, right, + insn_code, insn_index); + + /* Add this condition into the AND expression. */ + newexp = attr_rtx (NOT, this_cond); + andexp = insert_right_side (AND, andexp, newexp, + insn_code, insn_index); + } + + /* Handle the default case. */ + right = insert_right_side (AND, andexp, + evaluate_eq_attr (exp, XEXP (value, 1), + insn_code, insn_index), + insn_code, insn_index); + newexp = insert_right_side (IOR, orexp, right, insn_code, insn_index); + break; + + default: + gcc_unreachable (); + } + + /* If uses an address, must return original expression. But set the + ATTR_IND_SIMPLIFIED_P bit so we don't try to simplify it again. */ + + address_used = 0; + walk_attr_value (newexp); + + if (address_used) + { + if (! ATTR_IND_SIMPLIFIED_P (exp)) + return copy_rtx_unchanging (exp); + return exp; + } + else + return newexp; +} + +/* This routine is called when an AND of a term with a tree of AND's is + encountered. If the term or its complement is present in the tree, it + can be replaced with TRUE or FALSE, respectively. + + Note that (eq_attr "att" "v1") and (eq_attr "att" "v2") cannot both + be true and hence are complementary. + + There is one special case: If we see + (and (not (eq_attr "att" "v1")) + (eq_attr "att" "v2")) + this can be replaced by (eq_attr "att" "v2"). To do this we need to + replace the term, not anything in the AND tree. So we pass a pointer to + the term. */ + +static rtx +simplify_and_tree (rtx exp, rtx *pterm, int insn_code, int insn_index) +{ + rtx left, right; + rtx newexp; + rtx temp; + int left_eliminates_term, right_eliminates_term; + + if (GET_CODE (exp) == AND) + { + left = simplify_and_tree (XEXP (exp, 0), pterm, insn_code, insn_index); + right = simplify_and_tree (XEXP (exp, 1), pterm, insn_code, insn_index); + if (left != XEXP (exp, 0) || right != XEXP (exp, 1)) + { + newexp = attr_rtx (AND, left, right); + + exp = simplify_test_exp_in_temp (newexp, insn_code, insn_index); + } + } + + else if (GET_CODE (exp) == IOR) + { + /* For the IOR case, we do the same as above, except that we can + only eliminate `term' if both sides of the IOR would do so. */ + temp = *pterm; + left = simplify_and_tree (XEXP (exp, 0), &temp, insn_code, insn_index); + left_eliminates_term = (temp == true_rtx); + + temp = *pterm; + right = simplify_and_tree (XEXP (exp, 1), &temp, insn_code, insn_index); + right_eliminates_term = (temp == true_rtx); + + if (left_eliminates_term && right_eliminates_term) + *pterm = true_rtx; + + if (left != XEXP (exp, 0) || right != XEXP (exp, 1)) + { + newexp = attr_rtx (IOR, left, right); + + exp = simplify_test_exp_in_temp (newexp, insn_code, insn_index); + } + } + + /* Check for simplifications. Do some extra checking here since this + routine is called so many times. */ + + if (exp == *pterm) + return true_rtx; + + else if (GET_CODE (exp) == NOT && XEXP (exp, 0) == *pterm) + return false_rtx; + + else if (GET_CODE (*pterm) == NOT && exp == XEXP (*pterm, 0)) + return false_rtx; + + else if (GET_CODE (exp) == EQ_ATTR_ALT && GET_CODE (*pterm) == EQ_ATTR_ALT) + { + if (attr_alt_subset_p (*pterm, exp)) + return true_rtx; + + if (attr_alt_subset_of_compl_p (*pterm, exp)) + return false_rtx; + + if (attr_alt_subset_p (exp, *pterm)) + *pterm = true_rtx; + + return exp; + } + + else if (GET_CODE (exp) == EQ_ATTR && GET_CODE (*pterm) == EQ_ATTR) + { + if (XSTR (exp, 0) != XSTR (*pterm, 0)) + return exp; + + if (! strcmp_check (XSTR (exp, 1), XSTR (*pterm, 1))) + return true_rtx; + else + return false_rtx; + } + + else if (GET_CODE (*pterm) == EQ_ATTR && GET_CODE (exp) == NOT + && GET_CODE (XEXP (exp, 0)) == EQ_ATTR) + { + if (XSTR (*pterm, 0) != XSTR (XEXP (exp, 0), 0)) + return exp; + + if (! strcmp_check (XSTR (*pterm, 1), XSTR (XEXP (exp, 0), 1))) + return false_rtx; + else + return true_rtx; + } + + else if (GET_CODE (exp) == EQ_ATTR && GET_CODE (*pterm) == NOT + && GET_CODE (XEXP (*pterm, 0)) == EQ_ATTR) + { + if (XSTR (exp, 0) != XSTR (XEXP (*pterm, 0), 0)) + return exp; + + if (! strcmp_check (XSTR (exp, 1), XSTR (XEXP (*pterm, 0), 1))) + return false_rtx; + else + *pterm = true_rtx; + } + + else if (GET_CODE (exp) == NOT && GET_CODE (*pterm) == NOT) + { + if (attr_equal_p (XEXP (exp, 0), XEXP (*pterm, 0))) + return true_rtx; + } + + else if (GET_CODE (exp) == NOT) + { + if (attr_equal_p (XEXP (exp, 0), *pterm)) + return false_rtx; + } + + else if (GET_CODE (*pterm) == NOT) + { + if (attr_equal_p (XEXP (*pterm, 0), exp)) + return false_rtx; + } + + else if (attr_equal_p (exp, *pterm)) + return true_rtx; + + return exp; +} + +/* Similar to `simplify_and_tree', but for IOR trees. */ + +static rtx +simplify_or_tree (rtx exp, rtx *pterm, int insn_code, int insn_index) +{ + rtx left, right; + rtx newexp; + rtx temp; + int left_eliminates_term, right_eliminates_term; + + if (GET_CODE (exp) == IOR) + { + left = simplify_or_tree (XEXP (exp, 0), pterm, insn_code, insn_index); + right = simplify_or_tree (XEXP (exp, 1), pterm, insn_code, insn_index); + if (left != XEXP (exp, 0) || right != XEXP (exp, 1)) + { + newexp = attr_rtx (GET_CODE (exp), left, right); + + exp = simplify_test_exp_in_temp (newexp, insn_code, insn_index); + } + } + + else if (GET_CODE (exp) == AND) + { + /* For the AND case, we do the same as above, except that we can + only eliminate `term' if both sides of the AND would do so. */ + temp = *pterm; + left = simplify_or_tree (XEXP (exp, 0), &temp, insn_code, insn_index); + left_eliminates_term = (temp == false_rtx); + + temp = *pterm; + right = simplify_or_tree (XEXP (exp, 1), &temp, insn_code, insn_index); + right_eliminates_term = (temp == false_rtx); + + if (left_eliminates_term && right_eliminates_term) + *pterm = false_rtx; + + if (left != XEXP (exp, 0) || right != XEXP (exp, 1)) + { + newexp = attr_rtx (GET_CODE (exp), left, right); + + exp = simplify_test_exp_in_temp (newexp, insn_code, insn_index); + } + } + + if (attr_equal_p (exp, *pterm)) + return false_rtx; + + else if (GET_CODE (exp) == NOT && attr_equal_p (XEXP (exp, 0), *pterm)) + return true_rtx; + + else if (GET_CODE (*pterm) == NOT && attr_equal_p (XEXP (*pterm, 0), exp)) + return true_rtx; + + else if (GET_CODE (*pterm) == EQ_ATTR && GET_CODE (exp) == NOT + && GET_CODE (XEXP (exp, 0)) == EQ_ATTR + && XSTR (*pterm, 0) == XSTR (XEXP (exp, 0), 0)) + *pterm = false_rtx; + + else if (GET_CODE (exp) == EQ_ATTR && GET_CODE (*pterm) == NOT + && GET_CODE (XEXP (*pterm, 0)) == EQ_ATTR + && XSTR (exp, 0) == XSTR (XEXP (*pterm, 0), 0)) + return false_rtx; + + return exp; +} + +/* Compute approximate cost of the expression. Used to decide whether + expression is cheap enough for inline. */ +static int +attr_rtx_cost (rtx x) +{ + int cost = 0; + enum rtx_code code; + if (!x) + return 0; + code = GET_CODE (x); + switch (code) + { + case MATCH_OPERAND: + if (XSTR (x, 1)[0]) + return 10; + else + return 0; + + case EQ_ATTR_ALT: + return 0; + + case EQ_ATTR: + /* Alternatives don't result into function call. */ + if (!strcmp_check (XSTR (x, 0), alternative_name)) + return 0; + else + return 5; + default: + { + int i, j; + const char *fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + switch (fmt[i]) + { + case 'V': + case 'E': + for (j = 0; j < XVECLEN (x, i); j++) + cost += attr_rtx_cost (XVECEXP (x, i, j)); + break; + case 'e': + cost += attr_rtx_cost (XEXP (x, i)); + break; + } + } + } + break; + } + return cost; +} + +/* Simplify test expression and use temporary obstack in order to avoid + memory bloat. Use ATTR_IND_SIMPLIFIED to avoid unnecessary simplifications + and avoid unnecessary copying if possible. */ + +static rtx +simplify_test_exp_in_temp (rtx exp, int insn_code, int insn_index) +{ + rtx x; + struct obstack *old; + if (ATTR_IND_SIMPLIFIED_P (exp)) + return exp; + old = rtl_obstack; + rtl_obstack = temp_obstack; + x = simplify_test_exp (exp, insn_code, insn_index); + rtl_obstack = old; + if (x == exp || rtl_obstack == temp_obstack) + return x; + return attr_copy_rtx (x); +} + +/* Returns true if S1 is a subset of S2. */ + +static bool +attr_alt_subset_p (rtx s1, rtx s2) +{ + switch ((XINT (s1, 1) << 1) | XINT (s2, 1)) + { + case (0 << 1) | 0: + return !(XINT (s1, 0) &~ XINT (s2, 0)); + + case (0 << 1) | 1: + return !(XINT (s1, 0) & XINT (s2, 0)); + + case (1 << 1) | 0: + return false; + + case (1 << 1) | 1: + return !(XINT (s2, 0) &~ XINT (s1, 0)); + + default: + gcc_unreachable (); + } +} + +/* Returns true if S1 is a subset of complement of S2. */ + +static bool +attr_alt_subset_of_compl_p (rtx s1, rtx s2) +{ + switch ((XINT (s1, 1) << 1) | XINT (s2, 1)) + { + case (0 << 1) | 0: + return !(XINT (s1, 0) & XINT (s2, 0)); + + case (0 << 1) | 1: + return !(XINT (s1, 0) & ~XINT (s2, 0)); + + case (1 << 1) | 0: + return !(XINT (s2, 0) &~ XINT (s1, 0)); + + case (1 << 1) | 1: + return false; + + default: + gcc_unreachable (); + } +} + +/* Return EQ_ATTR_ALT expression representing intersection of S1 and S2. */ + +static rtx +attr_alt_intersection (rtx s1, rtx s2) +{ + rtx result = rtx_alloc (EQ_ATTR_ALT); + + switch ((XINT (s1, 1) << 1) | XINT (s2, 1)) + { + case (0 << 1) | 0: + XINT (result, 0) = XINT (s1, 0) & XINT (s2, 0); + break; + case (0 << 1) | 1: + XINT (result, 0) = XINT (s1, 0) & ~XINT (s2, 0); + break; + case (1 << 1) | 0: + XINT (result, 0) = XINT (s2, 0) & ~XINT (s1, 0); + break; + case (1 << 1) | 1: + XINT (result, 0) = XINT (s1, 0) | XINT (s2, 0); + break; + default: + gcc_unreachable (); + } + XINT (result, 1) = XINT (s1, 1) & XINT (s2, 1); + + return result; +} + +/* Return EQ_ATTR_ALT expression representing union of S1 and S2. */ + +static rtx +attr_alt_union (rtx s1, rtx s2) +{ + rtx result = rtx_alloc (EQ_ATTR_ALT); + + switch ((XINT (s1, 1) << 1) | XINT (s2, 1)) + { + case (0 << 1) | 0: + XINT (result, 0) = XINT (s1, 0) | XINT (s2, 0); + break; + case (0 << 1) | 1: + XINT (result, 0) = XINT (s2, 0) & ~XINT (s1, 0); + break; + case (1 << 1) | 0: + XINT (result, 0) = XINT (s1, 0) & ~XINT (s2, 0); + break; + case (1 << 1) | 1: + XINT (result, 0) = XINT (s1, 0) & XINT (s2, 0); + break; + default: + gcc_unreachable (); + } + + XINT (result, 1) = XINT (s1, 1) | XINT (s2, 1); + return result; +} + +/* Return EQ_ATTR_ALT expression representing complement of S. */ + +static rtx +attr_alt_complement (rtx s) +{ + rtx result = rtx_alloc (EQ_ATTR_ALT); + + XINT (result, 0) = XINT (s, 0); + XINT (result, 1) = 1 - XINT (s, 1); + + return result; +} + +/* Return EQ_ATTR_ALT expression representing set containing elements set + in E. */ + +static rtx +mk_attr_alt (int e) +{ + rtx result = rtx_alloc (EQ_ATTR_ALT); + + XINT (result, 0) = e; + XINT (result, 1) = 0; + + return result; +} + +/* Given an expression, see if it can be simplified for a particular insn + code based on the values of other attributes being tested. This can + eliminate nested get_attr_... calls. + + Note that if an endless recursion is specified in the patterns, the + optimization will loop. However, it will do so in precisely the cases where + an infinite recursion loop could occur during compilation. It's better that + it occurs here! */ + +static rtx +simplify_test_exp (rtx exp, int insn_code, int insn_index) +{ + rtx left, right; + struct attr_desc *attr; + struct attr_value *av; + struct insn_ent *ie; + struct attr_value_list *iv; + int i; + rtx newexp = exp; + bool left_alt, right_alt; + + /* Don't re-simplify something we already simplified. */ + if (ATTR_IND_SIMPLIFIED_P (exp) || ATTR_CURR_SIMPLIFIED_P (exp)) + return exp; + + switch (GET_CODE (exp)) + { + case AND: + left = SIMPLIFY_TEST_EXP (XEXP (exp, 0), insn_code, insn_index); + if (left == false_rtx) + return false_rtx; + right = SIMPLIFY_TEST_EXP (XEXP (exp, 1), insn_code, insn_index); + if (right == false_rtx) + return false_rtx; + + if (GET_CODE (left) == EQ_ATTR_ALT + && GET_CODE (right) == EQ_ATTR_ALT) + { + exp = attr_alt_intersection (left, right); + return simplify_test_exp (exp, insn_code, insn_index); + } + + /* If either side is an IOR and we have (eq_attr "alternative" ..") + present on both sides, apply the distributive law since this will + yield simplifications. */ + if ((GET_CODE (left) == IOR || GET_CODE (right) == IOR) + && compute_alternative_mask (left, IOR) + && compute_alternative_mask (right, IOR)) + { + if (GET_CODE (left) == IOR) + { + rtx tem = left; + left = right; + right = tem; + } + + newexp = attr_rtx (IOR, + attr_rtx (AND, left, XEXP (right, 0)), + attr_rtx (AND, left, XEXP (right, 1))); + + return SIMPLIFY_TEST_EXP (newexp, insn_code, insn_index); + } + + /* Try with the term on both sides. */ + right = simplify_and_tree (right, &left, insn_code, insn_index); + if (left == XEXP (exp, 0) && right == XEXP (exp, 1)) + left = simplify_and_tree (left, &right, insn_code, insn_index); + + if (left == false_rtx || right == false_rtx) + return false_rtx; + else if (left == true_rtx) + { + return right; + } + else if (right == true_rtx) + { + return left; + } + /* See if all or all but one of the insn's alternatives are specified + in this tree. Optimize if so. */ + + if (GET_CODE (left) == NOT) + left_alt = (GET_CODE (XEXP (left, 0)) == EQ_ATTR + && XSTR (XEXP (left, 0), 0) == alternative_name); + else + left_alt = (GET_CODE (left) == EQ_ATTR_ALT + && XINT (left, 1)); + + if (GET_CODE (right) == NOT) + right_alt = (GET_CODE (XEXP (right, 0)) == EQ_ATTR + && XSTR (XEXP (right, 0), 0) == alternative_name); + else + right_alt = (GET_CODE (right) == EQ_ATTR_ALT + && XINT (right, 1)); + + if (insn_code >= 0 + && (GET_CODE (left) == AND + || left_alt + || GET_CODE (right) == AND + || right_alt)) + { + i = compute_alternative_mask (exp, AND); + if (i & ~insn_alternatives[insn_code]) + fatal ("invalid alternative specified for pattern number %d", + insn_index); + + /* If all alternatives are excluded, this is false. */ + i ^= insn_alternatives[insn_code]; + if (i == 0) + return false_rtx; + else if ((i & (i - 1)) == 0 && insn_alternatives[insn_code] > 1) + { + /* If just one excluded, AND a comparison with that one to the + front of the tree. The others will be eliminated by + optimization. We do not want to do this if the insn has one + alternative and we have tested none of them! */ + left = make_alternative_compare (i); + right = simplify_and_tree (exp, &left, insn_code, insn_index); + newexp = attr_rtx (AND, left, right); + + return SIMPLIFY_TEST_EXP (newexp, insn_code, insn_index); + } + } + + if (left != XEXP (exp, 0) || right != XEXP (exp, 1)) + { + newexp = attr_rtx (AND, left, right); + return SIMPLIFY_TEST_EXP (newexp, insn_code, insn_index); + } + break; + + case IOR: + left = SIMPLIFY_TEST_EXP (XEXP (exp, 0), insn_code, insn_index); + if (left == true_rtx) + return true_rtx; + right = SIMPLIFY_TEST_EXP (XEXP (exp, 1), insn_code, insn_index); + if (right == true_rtx) + return true_rtx; + + if (GET_CODE (left) == EQ_ATTR_ALT + && GET_CODE (right) == EQ_ATTR_ALT) + { + exp = attr_alt_union (left, right); + return simplify_test_exp (exp, insn_code, insn_index); + } + + right = simplify_or_tree (right, &left, insn_code, insn_index); + if (left == XEXP (exp, 0) && right == XEXP (exp, 1)) + left = simplify_or_tree (left, &right, insn_code, insn_index); + + if (right == true_rtx || left == true_rtx) + return true_rtx; + else if (left == false_rtx) + { + return right; + } + else if (right == false_rtx) + { + return left; + } + + /* Test for simple cases where the distributive law is useful. I.e., + convert (ior (and (x) (y)) + (and (x) (z))) + to (and (x) + (ior (y) (z))) + */ + + else if (GET_CODE (left) == AND && GET_CODE (right) == AND + && attr_equal_p (XEXP (left, 0), XEXP (right, 0))) + { + newexp = attr_rtx (IOR, XEXP (left, 1), XEXP (right, 1)); + + left = XEXP (left, 0); + right = newexp; + newexp = attr_rtx (AND, left, right); + return SIMPLIFY_TEST_EXP (newexp, insn_code, insn_index); + } + + /* See if all or all but one of the insn's alternatives are specified + in this tree. Optimize if so. */ + + else if (insn_code >= 0 + && (GET_CODE (left) == IOR + || (GET_CODE (left) == EQ_ATTR_ALT + && !XINT (left, 1)) + || (GET_CODE (left) == EQ_ATTR + && XSTR (left, 0) == alternative_name) + || GET_CODE (right) == IOR + || (GET_CODE (right) == EQ_ATTR_ALT + && !XINT (right, 1)) + || (GET_CODE (right) == EQ_ATTR + && XSTR (right, 0) == alternative_name))) + { + i = compute_alternative_mask (exp, IOR); + if (i & ~insn_alternatives[insn_code]) + fatal ("invalid alternative specified for pattern number %d", + insn_index); + + /* If all alternatives are included, this is true. */ + i ^= insn_alternatives[insn_code]; + if (i == 0) + return true_rtx; + else if ((i & (i - 1)) == 0 && insn_alternatives[insn_code] > 1) + { + /* If just one excluded, IOR a comparison with that one to the + front of the tree. The others will be eliminated by + optimization. We do not want to do this if the insn has one + alternative and we have tested none of them! */ + left = make_alternative_compare (i); + right = simplify_and_tree (exp, &left, insn_code, insn_index); + newexp = attr_rtx (IOR, attr_rtx (NOT, left), right); + + return SIMPLIFY_TEST_EXP (newexp, insn_code, insn_index); + } + } + + if (left != XEXP (exp, 0) || right != XEXP (exp, 1)) + { + newexp = attr_rtx (IOR, left, right); + return SIMPLIFY_TEST_EXP (newexp, insn_code, insn_index); + } + break; + + case NOT: + if (GET_CODE (XEXP (exp, 0)) == NOT) + { + left = SIMPLIFY_TEST_EXP (XEXP (XEXP (exp, 0), 0), + insn_code, insn_index); + return left; + } + + left = SIMPLIFY_TEST_EXP (XEXP (exp, 0), insn_code, insn_index); + if (GET_CODE (left) == NOT) + return XEXP (left, 0); + + if (left == false_rtx) + return true_rtx; + if (left == true_rtx) + return false_rtx; + + if (GET_CODE (left) == EQ_ATTR_ALT) + { + exp = attr_alt_complement (left); + return simplify_test_exp (exp, insn_code, insn_index); + } + + /* Try to apply De`Morgan's laws. */ + if (GET_CODE (left) == IOR) + { + newexp = attr_rtx (AND, + attr_rtx (NOT, XEXP (left, 0)), + attr_rtx (NOT, XEXP (left, 1))); + + newexp = SIMPLIFY_TEST_EXP (newexp, insn_code, insn_index); + } + else if (GET_CODE (left) == AND) + { + newexp = attr_rtx (IOR, + attr_rtx (NOT, XEXP (left, 0)), + attr_rtx (NOT, XEXP (left, 1))); + + newexp = SIMPLIFY_TEST_EXP (newexp, insn_code, insn_index); + } + else if (left != XEXP (exp, 0)) + { + newexp = attr_rtx (NOT, left); + } + break; + + case EQ_ATTR_ALT: + if (!XINT (exp, 0)) + return XINT (exp, 1) ? true_rtx : false_rtx; + break; + + case EQ_ATTR: + if (XSTR (exp, 0) == alternative_name) + { + newexp = mk_attr_alt (1 << atoi (XSTR (exp, 1))); + break; + } + + /* Look at the value for this insn code in the specified attribute. + We normally can replace this comparison with the condition that + would give this insn the values being tested for. */ + if (insn_code >= 0 + && (attr = find_attr (&XSTR (exp, 0), 0)) != NULL) + { + rtx x; + + av = NULL; + if (insn_code_values) + { + for (iv = insn_code_values[insn_code]; iv; iv = iv->next) + if (iv->attr == attr) + { + av = iv->av; + break; + } + } + else + { + for (av = attr->first_value; av; av = av->next) + for (ie = av->first_insn; ie; ie = ie->next) + if (ie->def->insn_code == insn_code) + goto got_av; + } + + if (av) + { + got_av: + x = evaluate_eq_attr (exp, av->value, insn_code, insn_index); + x = SIMPLIFY_TEST_EXP (x, insn_code, insn_index); + if (attr_rtx_cost(x) < 20) + return x; + } + } + break; + + default: + break; + } + + /* We have already simplified this expression. Simplifying it again + won't buy anything unless we weren't given a valid insn code + to process (i.e., we are canonicalizing something.). */ + if (insn_code != -2 + && ! ATTR_IND_SIMPLIFIED_P (newexp)) + return copy_rtx_unchanging (newexp); + + return newexp; +} + +/* Optimize the attribute lists by seeing if we can determine conditional + values from the known values of other attributes. This will save subroutine + calls during the compilation. */ + +static void +optimize_attrs (void) +{ + struct attr_desc *attr; + struct attr_value *av; + struct insn_ent *ie; + rtx newexp; + int i; + struct attr_value_list *ivbuf; + struct attr_value_list *iv; + + /* For each insn code, make a list of all the insn_ent's for it, + for all values for all attributes. */ + + if (num_insn_ents == 0) + return; + + /* Make 2 extra elements, for "code" values -2 and -1. */ + insn_code_values = XCNEWVEC (struct attr_value_list *, insn_code_number + 2); + + /* Offset the table address so we can index by -2 or -1. */ + insn_code_values += 2; + + iv = ivbuf = XNEWVEC (struct attr_value_list, num_insn_ents); + + for (i = 0; i < MAX_ATTRS_INDEX; i++) + for (attr = attrs[i]; attr; attr = attr->next) + for (av = attr->first_value; av; av = av->next) + for (ie = av->first_insn; ie; ie = ie->next) + { + iv->attr = attr; + iv->av = av; + iv->ie = ie; + iv->next = insn_code_values[ie->def->insn_code]; + insn_code_values[ie->def->insn_code] = iv; + iv++; + } + + /* Sanity check on num_insn_ents. */ + gcc_assert (iv == ivbuf + num_insn_ents); + + /* Process one insn code at a time. */ + for (i = -2; i < insn_code_number; i++) + { + /* Clear the ATTR_CURR_SIMPLIFIED_P flag everywhere relevant. + We use it to mean "already simplified for this insn". */ + for (iv = insn_code_values[i]; iv; iv = iv->next) + clear_struct_flag (iv->av->value); + + for (iv = insn_code_values[i]; iv; iv = iv->next) + { + struct obstack *old = rtl_obstack; + + attr = iv->attr; + av = iv->av; + ie = iv->ie; + if (GET_CODE (av->value) != COND) + continue; + + rtl_obstack = temp_obstack; + newexp = av->value; + while (GET_CODE (newexp) == COND) + { + rtx newexp2 = simplify_cond (newexp, ie->def->insn_code, + ie->def->insn_index); + if (newexp2 == newexp) + break; + newexp = newexp2; + } + + rtl_obstack = old; + if (newexp != av->value) + { + newexp = attr_copy_rtx (newexp); + remove_insn_ent (av, ie); + av = get_attr_value (newexp, attr, ie->def->insn_code); + iv->av = av; + insert_insn_ent (av, ie); + } + } + } + + free (ivbuf); + free (insn_code_values - 2); + insn_code_values = NULL; +} + +/* Clear the ATTR_CURR_SIMPLIFIED_P flag in EXP and its subexpressions. */ + +static void +clear_struct_flag (rtx x) +{ + int i; + int j; + enum rtx_code code; + const char *fmt; + + ATTR_CURR_SIMPLIFIED_P (x) = 0; + if (ATTR_IND_SIMPLIFIED_P (x)) + return; + + code = GET_CODE (x); + + switch (code) + { + case REG: + case CONST_INT: + case CONST_DOUBLE: + case CONST_VECTOR: + case SYMBOL_REF: + case CODE_LABEL: + case PC: + case CC0: + case EQ_ATTR: + case ATTR_FLAG: + return; + + default: + break; + } + + /* 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 'V': + case 'E': + for (j = 0; j < XVECLEN (x, i); j++) + clear_struct_flag (XVECEXP (x, i, j)); + break; + + case 'e': + clear_struct_flag (XEXP (x, i)); + break; + } + } +} + +/* Create table entries for DEFINE_ATTR. */ + +static void +gen_attr (rtx exp, int lineno) +{ + struct attr_desc *attr; + struct attr_value *av; + const char *name_ptr; + char *p; + + /* Make a new attribute structure. Check for duplicate by looking at + attr->default_val, since it is initialized by this routine. */ + attr = find_attr (&XSTR (exp, 0), 1); + if (attr->default_val) + { + message_with_line (lineno, "duplicate definition for attribute %s", + attr->name); + message_with_line (attr->lineno, "previous definition"); + have_error = 1; + return; + } + attr->lineno = lineno; + + if (*XSTR (exp, 1) == '\0') + attr->is_numeric = 1; + else + { + name_ptr = XSTR (exp, 1); + while ((p = next_comma_elt (&name_ptr)) != NULL) + { + av = oballoc (struct attr_value); + av->value = attr_rtx (CONST_STRING, p); + av->next = attr->first_value; + attr->first_value = av; + av->first_insn = NULL; + av->num_insns = 0; + av->has_asm_insn = 0; + } + } + + if (GET_CODE (XEXP (exp, 2)) == CONST) + { + attr->is_const = 1; + if (attr->is_numeric) + { + message_with_line (lineno, + "constant attributes may not take numeric values"); + have_error = 1; + } + + /* Get rid of the CONST node. It is allowed only at top-level. */ + XEXP (exp, 2) = XEXP (XEXP (exp, 2), 0); + } + + if (! strcmp_check (attr->name, length_str) && ! attr->is_numeric) + { + message_with_line (lineno, + "`length' attribute must take numeric values"); + have_error = 1; + } + + /* Set up the default value. */ + XEXP (exp, 2) = check_attr_value (XEXP (exp, 2), attr); + attr->default_val = get_attr_value (XEXP (exp, 2), attr, -2); +} + +/* Given a pattern for DEFINE_PEEPHOLE or DEFINE_INSN, return the number of + alternatives in the constraints. Assume all MATCH_OPERANDs have the same + number of alternatives as this should be checked elsewhere. */ + +static int +count_alternatives (rtx exp) +{ + int i, j, n; + const char *fmt; + + if (GET_CODE (exp) == MATCH_OPERAND) + return n_comma_elts (XSTR (exp, 2)); + + for (i = 0, fmt = GET_RTX_FORMAT (GET_CODE (exp)); + i < GET_RTX_LENGTH (GET_CODE (exp)); i++) + switch (*fmt++) + { + case 'e': + case 'u': + n = count_alternatives (XEXP (exp, i)); + if (n) + return n; + break; + + case 'E': + case 'V': + if (XVEC (exp, i) != NULL) + for (j = 0; j < XVECLEN (exp, i); j++) + { + n = count_alternatives (XVECEXP (exp, i, j)); + if (n) + return n; + } + } + + return 0; +} + +/* Returns nonzero if the given expression contains an EQ_ATTR with the + `alternative' attribute. */ + +static int +compares_alternatives_p (rtx exp) +{ + int i, j; + const char *fmt; + + if (GET_CODE (exp) == EQ_ATTR && XSTR (exp, 0) == alternative_name) + return 1; + + for (i = 0, fmt = GET_RTX_FORMAT (GET_CODE (exp)); + i < GET_RTX_LENGTH (GET_CODE (exp)); i++) + switch (*fmt++) + { + case 'e': + case 'u': + if (compares_alternatives_p (XEXP (exp, i))) + return 1; + break; + + case 'E': + for (j = 0; j < XVECLEN (exp, i); j++) + if (compares_alternatives_p (XVECEXP (exp, i, j))) + return 1; + break; + } + + return 0; +} + +/* Process DEFINE_PEEPHOLE, DEFINE_INSN, and DEFINE_ASM_ATTRIBUTES. */ + +static void +gen_insn (rtx exp, int lineno) +{ + struct insn_def *id; + + id = oballoc (struct insn_def); + id->next = defs; + defs = id; + id->def = exp; + id->lineno = lineno; + + switch (GET_CODE (exp)) + { + case DEFINE_INSN: + id->insn_code = insn_code_number; + id->insn_index = insn_index_number; + id->num_alternatives = count_alternatives (exp); + if (id->num_alternatives == 0) + id->num_alternatives = 1; + id->vec_idx = 4; + break; + + case DEFINE_PEEPHOLE: + id->insn_code = insn_code_number; + id->insn_index = insn_index_number; + id->num_alternatives = count_alternatives (exp); + if (id->num_alternatives == 0) + id->num_alternatives = 1; + id->vec_idx = 3; + break; + + case DEFINE_ASM_ATTRIBUTES: + id->insn_code = -1; + id->insn_index = -1; + id->num_alternatives = 1; + id->vec_idx = 0; + got_define_asm_attributes = 1; + break; + + default: + gcc_unreachable (); + } +} + +/* Process a DEFINE_DELAY. Validate the vector length, check if annul + true or annul false is specified, and make a `struct delay_desc'. */ + +static void +gen_delay (rtx def, int lineno) +{ + struct delay_desc *delay; + int i; + + if (XVECLEN (def, 1) % 3 != 0) + { + message_with_line (lineno, + "number of elements in DEFINE_DELAY must be multiple of three"); + have_error = 1; + return; + } + + for (i = 0; i < XVECLEN (def, 1); i += 3) + { + if (XVECEXP (def, 1, i + 1)) + have_annul_true = 1; + if (XVECEXP (def, 1, i + 2)) + have_annul_false = 1; + } + + delay = oballoc (struct delay_desc); + delay->def = def; + delay->num = ++num_delays; + delay->next = delays; + delay->lineno = lineno; + delays = delay; +} + +/* Given a piece of RTX, print a C expression to test its truth value. + We use AND and IOR both for logical and bit-wise operations, so + interpret them as logical unless they are inside a comparison expression. + The first bit of FLAGS will be nonzero in that case. + + Set the second bit of FLAGS to make references to attribute values use + a cached local variable instead of calling a function. */ + +static void +write_test_expr (rtx exp, int flags) +{ + int comparison_operator = 0; + RTX_CODE code; + struct attr_desc *attr; + + /* In order not to worry about operator precedence, surround our part of + the expression with parentheses. */ + + printf ("("); + code = GET_CODE (exp); + switch (code) + { + /* Binary operators. */ + case GEU: case GTU: + case LEU: case LTU: + printf ("(unsigned) "); + /* Fall through. */ + + case EQ: case NE: + case GE: case GT: + case LE: case LT: + comparison_operator = 1; + + case PLUS: case MINUS: case MULT: case DIV: case MOD: + case AND: case IOR: case XOR: + case ASHIFT: case LSHIFTRT: case ASHIFTRT: + write_test_expr (XEXP (exp, 0), flags | comparison_operator); + switch (code) + { + case EQ: + printf (" == "); + break; + case NE: + printf (" != "); + break; + case GE: + printf (" >= "); + break; + case GT: + printf (" > "); + break; + case GEU: + printf (" >= (unsigned) "); + break; + case GTU: + printf (" > (unsigned) "); + break; + case LE: + printf (" <= "); + break; + case LT: + printf (" < "); + break; + case LEU: + printf (" <= (unsigned) "); + break; + case LTU: + printf (" < (unsigned) "); + break; + case PLUS: + printf (" + "); + break; + case MINUS: + printf (" - "); + break; + case MULT: + printf (" * "); + break; + case DIV: + printf (" / "); + break; + case MOD: + printf (" %% "); + break; + case AND: + if (flags & 1) + printf (" & "); + else + printf (" && "); + break; + case IOR: + if (flags & 1) + printf (" | "); + else + printf (" || "); + break; + case XOR: + printf (" ^ "); + break; + case ASHIFT: + printf (" << "); + break; + case LSHIFTRT: + case ASHIFTRT: + printf (" >> "); + break; + default: + gcc_unreachable (); + } + + write_test_expr (XEXP (exp, 1), flags | comparison_operator); + break; + + case NOT: + /* Special-case (not (eq_attrq "alternative" "x")) */ + if (! (flags & 1) && GET_CODE (XEXP (exp, 0)) == EQ_ATTR + && XSTR (XEXP (exp, 0), 0) == alternative_name) + { + printf ("which_alternative != %s", XSTR (XEXP (exp, 0), 1)); + break; + } + + /* Otherwise, fall through to normal unary operator. */ + + /* Unary operators. */ + case ABS: case NEG: + switch (code) + { + case NOT: + if (flags & 1) + printf ("~ "); + else + printf ("! "); + break; + case ABS: + printf ("abs "); + break; + case NEG: + printf ("-"); + break; + default: + gcc_unreachable (); + } + + write_test_expr (XEXP (exp, 0), flags); + break; + + case EQ_ATTR_ALT: + { + int set = XINT (exp, 0), bit = 0; + + if (flags & 1) + fatal ("EQ_ATTR_ALT not valid inside comparison"); + + if (!set) + fatal ("Empty EQ_ATTR_ALT should be optimized out"); + + if (!(set & (set - 1))) + { + if (!(set & 0xffff)) + { + bit += 16; + set >>= 16; + } + if (!(set & 0xff)) + { + bit += 8; + set >>= 8; + } + if (!(set & 0xf)) + { + bit += 4; + set >>= 4; + } + if (!(set & 0x3)) + { + bit += 2; + set >>= 2; + } + if (!(set & 1)) + bit++; + + printf ("which_alternative %s= %d", + XINT (exp, 1) ? "!" : "=", bit); + } + else + { + printf ("%s((1 << which_alternative) & 0x%x)", + XINT (exp, 1) ? "!" : "", set); + } + } + break; + + /* Comparison test of an attribute with a value. Most of these will + have been removed by optimization. Handle "alternative" + specially and give error if EQ_ATTR present inside a comparison. */ + case EQ_ATTR: + if (flags & 1) + fatal ("EQ_ATTR not valid inside comparison"); + + if (XSTR (exp, 0) == alternative_name) + { + printf ("which_alternative == %s", XSTR (exp, 1)); + break; + } + + attr = find_attr (&XSTR (exp, 0), 0); + gcc_assert (attr); + + /* Now is the time to expand the value of a constant attribute. */ + if (attr->is_const) + { + write_test_expr (evaluate_eq_attr (exp, attr->default_val->value, + -2, -2), + flags); + } + else + { + if (flags & 2) + printf ("attr_%s", attr->name); + else + printf ("get_attr_%s (insn)", attr->name); + printf (" == "); + write_attr_valueq (attr, XSTR (exp, 1)); + } + break; + + /* Comparison test of flags for define_delays. */ + case ATTR_FLAG: + if (flags & 1) + fatal ("ATTR_FLAG not valid inside comparison"); + printf ("(flags & ATTR_FLAG_%s) != 0", XSTR (exp, 0)); + break; + + /* See if an operand matches a predicate. */ + case MATCH_OPERAND: + /* If only a mode is given, just ensure the mode matches the operand. + If neither a mode nor predicate is given, error. */ + if (XSTR (exp, 1) == NULL || *XSTR (exp, 1) == '\0') + { + if (GET_MODE (exp) == VOIDmode) + fatal ("null MATCH_OPERAND specified as test"); + else + printf ("GET_MODE (operands[%d]) == %smode", + XINT (exp, 0), GET_MODE_NAME (GET_MODE (exp))); + } + else + printf ("%s (operands[%d], %smode)", + XSTR (exp, 1), XINT (exp, 0), GET_MODE_NAME (GET_MODE (exp))); + break; + + /* Constant integer. */ + case CONST_INT: + printf (HOST_WIDE_INT_PRINT_DEC, XWINT (exp, 0)); + break; + + /* A random C expression. */ + case SYMBOL_REF: + print_c_condition (XSTR (exp, 0)); + break; + + /* The address of the branch target. */ + case MATCH_DUP: + printf ("INSN_ADDRESSES_SET_P () ? INSN_ADDRESSES (INSN_UID (GET_CODE (operands[%d]) == LABEL_REF ? XEXP (operands[%d], 0) : operands[%d])) : 0", + XINT (exp, 0), XINT (exp, 0), XINT (exp, 0)); + break; + + case PC: + /* The address of the current insn. We implement this actually as the + address of the current insn for backward branches, but the last + address of the next insn for forward branches, and both with + adjustments that account for the worst-case possible stretching of + intervening alignments between this insn and its destination. */ + printf ("insn_current_reference_address (insn)"); + break; + + case CONST_STRING: + printf ("%s", XSTR (exp, 0)); + break; + + case IF_THEN_ELSE: + write_test_expr (XEXP (exp, 0), flags & 2); + printf (" ? "); + write_test_expr (XEXP (exp, 1), flags | 1); + printf (" : "); + write_test_expr (XEXP (exp, 2), flags | 1); + break; + + default: + fatal ("bad RTX code `%s' in attribute calculation\n", + GET_RTX_NAME (code)); + } + + printf (")"); +} + +/* Given an attribute value, return the maximum CONST_STRING argument + encountered. Set *UNKNOWNP and return INT_MAX if the value is unknown. */ + +static int +max_attr_value (rtx exp, int *unknownp) +{ + int current_max; + int i, n; + + switch (GET_CODE (exp)) + { + case CONST_STRING: + current_max = atoi (XSTR (exp, 0)); + break; + + case COND: + current_max = max_attr_value (XEXP (exp, 1), unknownp); + for (i = 0; i < XVECLEN (exp, 0); i += 2) + { + n = max_attr_value (XVECEXP (exp, 0, i + 1), unknownp); + if (n > current_max) + current_max = n; + } + break; + + case IF_THEN_ELSE: + current_max = max_attr_value (XEXP (exp, 1), unknownp); + n = max_attr_value (XEXP (exp, 2), unknownp); + if (n > current_max) + current_max = n; + break; + + default: + *unknownp = 1; + current_max = INT_MAX; + break; + } + + return current_max; +} + +/* Given an attribute value, return the minimum CONST_STRING argument + encountered. Set *UNKNOWNP and return 0 if the value is unknown. */ + +static int +min_attr_value (rtx exp, int *unknownp) +{ + int current_min; + int i, n; + + switch (GET_CODE (exp)) + { + case CONST_STRING: + current_min = atoi (XSTR (exp, 0)); + break; + + case COND: + current_min = min_attr_value (XEXP (exp, 1), unknownp); + for (i = 0; i < XVECLEN (exp, 0); i += 2) + { + n = min_attr_value (XVECEXP (exp, 0, i + 1), unknownp); + if (n < current_min) + current_min = n; + } + break; + + case IF_THEN_ELSE: + current_min = min_attr_value (XEXP (exp, 1), unknownp); + n = min_attr_value (XEXP (exp, 2), unknownp); + if (n < current_min) + current_min = n; + break; + + default: + *unknownp = 1; + current_min = INT_MAX; + break; + } + + return current_min; +} + +/* Given an attribute value, return the result of ORing together all + CONST_STRING arguments encountered. Set *UNKNOWNP and return -1 + if the numeric value is not known. */ + +static int +or_attr_value (rtx exp, int *unknownp) +{ + int current_or; + int i; + + switch (GET_CODE (exp)) + { + case CONST_STRING: + current_or = atoi (XSTR (exp, 0)); + break; + + case COND: + current_or = or_attr_value (XEXP (exp, 1), unknownp); + for (i = 0; i < XVECLEN (exp, 0); i += 2) + current_or |= or_attr_value (XVECEXP (exp, 0, i + 1), unknownp); + break; + + case IF_THEN_ELSE: + current_or = or_attr_value (XEXP (exp, 1), unknownp); + current_or |= or_attr_value (XEXP (exp, 2), unknownp); + break; + + default: + *unknownp = 1; + current_or = -1; + break; + } + + return current_or; +} + +/* Scan an attribute value, possibly a conditional, and record what actions + will be required to do any conditional tests in it. + + Specifically, set + `must_extract' if we need to extract the insn operands + `must_constrain' if we must compute `which_alternative' + `address_used' if an address expression was used + `length_used' if an (eq_attr "length" ...) was used + */ + +static void +walk_attr_value (rtx exp) +{ + int i, j; + const char *fmt; + RTX_CODE code; + + if (exp == NULL) + return; + + code = GET_CODE (exp); + switch (code) + { + case SYMBOL_REF: + if (! ATTR_IND_SIMPLIFIED_P (exp)) + /* Since this is an arbitrary expression, it can look at anything. + However, constant expressions do not depend on any particular + insn. */ + must_extract = must_constrain = 1; + return; + + case MATCH_OPERAND: + must_extract = 1; + return; + + case EQ_ATTR_ALT: + must_extract = must_constrain = 1; + break; + + case EQ_ATTR: + if (XSTR (exp, 0) == alternative_name) + must_extract = must_constrain = 1; + else if (strcmp_check (XSTR (exp, 0), length_str) == 0) + length_used = 1; + return; + + case MATCH_DUP: + must_extract = 1; + address_used = 1; + return; + + case PC: + address_used = 1; + return; + + case ATTR_FLAG: + return; + + default: + break; + } + + for (i = 0, fmt = GET_RTX_FORMAT (code); i < GET_RTX_LENGTH (code); i++) + switch (*fmt++) + { + case 'e': + case 'u': + walk_attr_value (XEXP (exp, i)); + break; + + case 'E': + if (XVEC (exp, i) != NULL) + for (j = 0; j < XVECLEN (exp, i); j++) + walk_attr_value (XVECEXP (exp, i, j)); + break; + } +} + +/* Write out a function to obtain the attribute for a given INSN. */ + +static void +write_attr_get (struct attr_desc *attr) +{ + struct attr_value *av, *common_av; + + /* Find the most used attribute value. Handle that as the `default' of the + switch we will generate. */ + common_av = find_most_used (attr); + + /* Write out start of function, then all values with explicit `case' lines, + then a `default', then the value with the most uses. */ + if (!attr->is_numeric) + printf ("enum attr_%s\n", attr->name); + else + printf ("int\n"); + + /* If the attribute name starts with a star, the remainder is the name of + the subroutine to use, instead of `get_attr_...'. */ + if (attr->name[0] == '*') + printf ("%s (rtx insn ATTRIBUTE_UNUSED)\n", &attr->name[1]); + else if (attr->is_const == 0) + printf ("get_attr_%s (rtx insn ATTRIBUTE_UNUSED)\n", attr->name); + else + { + printf ("get_attr_%s (void)\n", attr->name); + printf ("{\n"); + + for (av = attr->first_value; av; av = av->next) + if (av->num_insns == 1) + write_attr_set (attr, 2, av->value, "return", ";", + true_rtx, av->first_insn->def->insn_code, + av->first_insn->def->insn_index); + else if (av->num_insns != 0) + write_attr_set (attr, 2, av->value, "return", ";", + true_rtx, -2, 0); + + printf ("}\n\n"); + return; + } + + printf ("{\n"); + printf (" switch (recog_memoized (insn))\n"); + printf (" {\n"); + + for (av = attr->first_value; av; av = av->next) + if (av != common_av) + write_attr_case (attr, av, 1, "return", ";", 4, true_rtx); + + write_attr_case (attr, common_av, 0, "return", ";", 4, true_rtx); + printf (" }\n}\n\n"); +} + +/* Given an AND tree of known true terms (because we are inside an `if' with + that as the condition or are in an `else' clause) and an expression, + replace any known true terms with TRUE. Use `simplify_and_tree' to do + the bulk of the work. */ + +static rtx +eliminate_known_true (rtx known_true, rtx exp, int insn_code, int insn_index) +{ + rtx term; + + known_true = SIMPLIFY_TEST_EXP (known_true, insn_code, insn_index); + + if (GET_CODE (known_true) == AND) + { + exp = eliminate_known_true (XEXP (known_true, 0), exp, + insn_code, insn_index); + exp = eliminate_known_true (XEXP (known_true, 1), exp, + insn_code, insn_index); + } + else + { + term = known_true; + exp = simplify_and_tree (exp, &term, insn_code, insn_index); + } + + return exp; +} + +/* Write out a series of tests and assignment statements to perform tests and + sets of an attribute value. We are passed an indentation amount and prefix + and suffix strings to write around each attribute value (e.g., "return" + and ";"). */ + +static void +write_attr_set (struct attr_desc *attr, int indent, rtx value, + const char *prefix, const char *suffix, rtx known_true, + int insn_code, int insn_index) +{ + if (GET_CODE (value) == COND) + { + /* Assume the default value will be the default of the COND unless we + find an always true expression. */ + rtx default_val = XEXP (value, 1); + rtx our_known_true = known_true; + rtx newexp; + int first_if = 1; + int i; + + for (i = 0; i < XVECLEN (value, 0); i += 2) + { + rtx testexp; + rtx inner_true; + + testexp = eliminate_known_true (our_known_true, + XVECEXP (value, 0, i), + insn_code, insn_index); + newexp = attr_rtx (NOT, testexp); + newexp = insert_right_side (AND, our_known_true, newexp, + insn_code, insn_index); + + /* If the test expression is always true or if the next `known_true' + expression is always false, this is the last case, so break + out and let this value be the `else' case. */ + if (testexp == true_rtx || newexp == false_rtx) + { + default_val = XVECEXP (value, 0, i + 1); + break; + } + + /* Compute the expression to pass to our recursive call as being + known true. */ + inner_true = insert_right_side (AND, our_known_true, + testexp, insn_code, insn_index); + + /* If this is always false, skip it. */ + if (inner_true == false_rtx) + continue; + + write_indent (indent); + printf ("%sif ", first_if ? "" : "else "); + first_if = 0; + write_test_expr (testexp, 0); + printf ("\n"); + write_indent (indent + 2); + printf ("{\n"); + + write_attr_set (attr, indent + 4, + XVECEXP (value, 0, i + 1), prefix, suffix, + inner_true, insn_code, insn_index); + write_indent (indent + 2); + printf ("}\n"); + our_known_true = newexp; + } + + if (! first_if) + { + write_indent (indent); + printf ("else\n"); + write_indent (indent + 2); + printf ("{\n"); + } + + write_attr_set (attr, first_if ? indent : indent + 4, default_val, + prefix, suffix, our_known_true, insn_code, insn_index); + + if (! first_if) + { + write_indent (indent + 2); + printf ("}\n"); + } + } + else + { + write_indent (indent); + printf ("%s ", prefix); + write_attr_value (attr, value); + printf ("%s\n", suffix); + } +} + +/* Write a series of case statements for every instruction in list IE. + INDENT is the amount of indentation to write before each case. */ + +static void +write_insn_cases (struct insn_ent *ie, int indent) +{ + for (; ie != 0; ie = ie->next) + if (ie->def->insn_code != -1) + { + write_indent (indent); + if (GET_CODE (ie->def->def) == DEFINE_PEEPHOLE) + printf ("case %d: /* define_peephole, line %d */\n", + ie->def->insn_code, ie->def->lineno); + else + printf ("case %d: /* %s */\n", + ie->def->insn_code, XSTR (ie->def->def, 0)); + } +} + +/* Write out the computation for one attribute value. */ + +static void +write_attr_case (struct attr_desc *attr, struct attr_value *av, + int write_case_lines, const char *prefix, const char *suffix, + int indent, rtx known_true) +{ + if (av->num_insns == 0) + return; + + if (av->has_asm_insn) + { + write_indent (indent); + printf ("case -1:\n"); + write_indent (indent + 2); + printf ("if (GET_CODE (PATTERN (insn)) != ASM_INPUT\n"); + write_indent (indent + 2); + printf (" && asm_noperands (PATTERN (insn)) < 0)\n"); + write_indent (indent + 2); + printf (" fatal_insn_not_found (insn);\n"); + } + + if (write_case_lines) + write_insn_cases (av->first_insn, indent); + else + { + write_indent (indent); + printf ("default:\n"); + } + + /* See what we have to do to output this value. */ + must_extract = must_constrain = address_used = 0; + walk_attr_value (av->value); + + if (must_constrain) + { + write_indent (indent + 2); + printf ("extract_constrain_insn_cached (insn);\n"); + } + else if (must_extract) + { + write_indent (indent + 2); + printf ("extract_insn_cached (insn);\n"); + } + + if (av->num_insns == 1) + write_attr_set (attr, indent + 2, av->value, prefix, suffix, + known_true, av->first_insn->def->insn_code, + av->first_insn->def->insn_index); + else + write_attr_set (attr, indent + 2, av->value, prefix, suffix, + known_true, -2, 0); + + if (strncmp (prefix, "return", 6)) + { + write_indent (indent + 2); + printf ("break;\n"); + } + printf ("\n"); +} + +/* Utilities to write in various forms. */ + +static void +write_attr_valueq (struct attr_desc *attr, const char *s) +{ + if (attr->is_numeric) + { + int num = atoi (s); + + printf ("%d", num); + + if (num > 9 || num < 0) + printf (" /* 0x%x */", num); + } + else + { + write_upcase (attr->name); + printf ("_"); + write_upcase (s); + } +} + +static void +write_attr_value (struct attr_desc *attr, rtx value) +{ + int op; + + switch (GET_CODE (value)) + { + case CONST_STRING: + write_attr_valueq (attr, XSTR (value, 0)); + break; + + case CONST_INT: + printf (HOST_WIDE_INT_PRINT_DEC, INTVAL (value)); + break; + + case SYMBOL_REF: + print_c_condition (XSTR (value, 0)); + break; + + case ATTR: + { + struct attr_desc *attr2 = find_attr (&XSTR (value, 0), 0); + printf ("get_attr_%s (%s)", attr2->name, + (attr2->is_const ? "" : "insn")); + } + break; + + case PLUS: + op = '+'; + goto do_operator; + case MINUS: + op = '-'; + goto do_operator; + case MULT: + op = '*'; + goto do_operator; + case DIV: + op = '/'; + goto do_operator; + case MOD: + op = '%'; + goto do_operator; + + do_operator: + write_attr_value (attr, XEXP (value, 0)); + putchar (' '); + putchar (op); + putchar (' '); + write_attr_value (attr, XEXP (value, 1)); + break; + + default: + gcc_unreachable (); + } +} + +static void +write_upcase (const char *str) +{ + while (*str) + { + /* The argument of TOUPPER should not have side effects. */ + putchar (TOUPPER(*str)); + str++; + } +} + +static void +write_indent (int indent) +{ + for (; indent > 8; indent -= 8) + printf ("\t"); + + for (; indent; indent--) + printf (" "); +} + +/* Write a subroutine that is given an insn that requires a delay slot, a + delay slot ordinal, and a candidate insn. It returns nonzero if the + candidate can be placed in the specified delay slot of the insn. + + We can write as many as three subroutines. `eligible_for_delay' + handles normal delay slots, `eligible_for_annul_true' indicates that + the specified insn can be annulled if the branch is true, and likewise + for `eligible_for_annul_false'. + + KIND is a string distinguishing these three cases ("delay", "annul_true", + or "annul_false"). */ + +static void +write_eligible_delay (const char *kind) +{ + struct delay_desc *delay; + int max_slots; + char str[50]; + const char *pstr; + struct attr_desc *attr; + struct attr_value *av, *common_av; + int i; + + /* Compute the maximum number of delay slots required. We use the delay + ordinal times this number plus one, plus the slot number as an index into + the appropriate predicate to test. */ + + for (delay = delays, max_slots = 0; delay; delay = delay->next) + if (XVECLEN (delay->def, 1) / 3 > max_slots) + max_slots = XVECLEN (delay->def, 1) / 3; + + /* Write function prelude. */ + + printf ("int\n"); + printf ("eligible_for_%s (rtx delay_insn ATTRIBUTE_UNUSED, int slot, rtx candidate_insn, int flags ATTRIBUTE_UNUSED)\n", + kind); + printf ("{\n"); + printf (" rtx insn;\n"); + printf ("\n"); + printf (" gcc_assert (slot < %d);\n", max_slots); + printf ("\n"); + /* Allow dbr_schedule to pass labels, etc. This can happen if try_split + converts a compound instruction into a loop. */ + printf (" if (!INSN_P (candidate_insn))\n"); + printf (" return 0;\n"); + printf ("\n"); + + /* If more than one delay type, find out which type the delay insn is. */ + + if (num_delays > 1) + { + attr = find_attr (&delay_type_str, 0); + gcc_assert (attr); + common_av = find_most_used (attr); + + printf (" insn = delay_insn;\n"); + printf (" switch (recog_memoized (insn))\n"); + printf (" {\n"); + + sprintf (str, " * %d;\n break;", max_slots); + for (av = attr->first_value; av; av = av->next) + if (av != common_av) + write_attr_case (attr, av, 1, "slot +=", str, 4, true_rtx); + + write_attr_case (attr, common_av, 0, "slot +=", str, 4, true_rtx); + printf (" }\n\n"); + + /* Ensure matched. Otherwise, shouldn't have been called. */ + printf (" gcc_assert (slot >= %d);\n\n", max_slots); + } + + /* If just one type of delay slot, write simple switch. */ + if (num_delays == 1 && max_slots == 1) + { + printf (" insn = candidate_insn;\n"); + printf (" switch (recog_memoized (insn))\n"); + printf (" {\n"); + + attr = find_attr (&delay_1_0_str, 0); + gcc_assert (attr); + common_av = find_most_used (attr); + + for (av = attr->first_value; av; av = av->next) + if (av != common_av) + write_attr_case (attr, av, 1, "return", ";", 4, true_rtx); + + write_attr_case (attr, common_av, 0, "return", ";", 4, true_rtx); + printf (" }\n"); + } + + else + { + /* Write a nested CASE. The first indicates which condition we need to + test, and the inner CASE tests the condition. */ + printf (" insn = candidate_insn;\n"); + printf (" switch (slot)\n"); + printf (" {\n"); + + for (delay = delays; delay; delay = delay->next) + for (i = 0; i < XVECLEN (delay->def, 1); i += 3) + { + printf (" case %d:\n", + (i / 3) + (num_delays == 1 ? 0 : delay->num * max_slots)); + printf (" switch (recog_memoized (insn))\n"); + printf ("\t{\n"); + + sprintf (str, "*%s_%d_%d", kind, delay->num, i / 3); + pstr = str; + attr = find_attr (&pstr, 0); + gcc_assert (attr); + common_av = find_most_used (attr); + + for (av = attr->first_value; av; av = av->next) + if (av != common_av) + write_attr_case (attr, av, 1, "return", ";", 8, true_rtx); + + write_attr_case (attr, common_av, 0, "return", ";", 8, true_rtx); + printf (" }\n"); + } + + printf (" default:\n"); + printf (" gcc_unreachable ();\n"); + printf (" }\n"); + } + + printf ("}\n\n"); +} + +/* This page contains miscellaneous utility routines. */ + +/* Given a pointer to a (char *), return a malloc'ed string containing the + next comma-separated element. Advance the pointer to after the string + scanned, or the end-of-string. Return NULL if at end of string. */ + +static char * +next_comma_elt (const char **pstr) +{ + const char *start; + + start = scan_comma_elt (pstr); + + if (start == NULL) + return NULL; + + return attr_string (start, *pstr - start); +} + +/* Return a `struct attr_desc' pointer for a given named attribute. If CREATE + is nonzero, build a new attribute, if one does not exist. *NAME_P is + replaced by a pointer to a canonical copy of the string. */ + +static struct attr_desc * +find_attr (const char **name_p, int create) +{ + struct attr_desc *attr; + int index; + const char *name = *name_p; + + /* Before we resort to using `strcmp', see if the string address matches + anywhere. In most cases, it should have been canonicalized to do so. */ + if (name == alternative_name) + return NULL; + + index = name[0] & (MAX_ATTRS_INDEX - 1); + for (attr = attrs[index]; attr; attr = attr->next) + if (name == attr->name) + return attr; + + /* Otherwise, do it the slow way. */ + for (attr = attrs[index]; attr; attr = attr->next) + if (name[0] == attr->name[0] && ! strcmp (name, attr->name)) + { + *name_p = attr->name; + return attr; + } + + if (! create) + return NULL; + + attr = oballoc (struct attr_desc); + attr->name = DEF_ATTR_STRING (name); + attr->first_value = attr->default_val = NULL; + attr->is_numeric = attr->is_const = attr->is_special = 0; + attr->next = attrs[index]; + attrs[index] = attr; + + *name_p = attr->name; + + return attr; +} + +/* Create internal attribute with the given default value. */ + +static void +make_internal_attr (const char *name, rtx value, int special) +{ + struct attr_desc *attr; + + attr = find_attr (&name, 1); + gcc_assert (!attr->default_val); + + attr->is_numeric = 1; + attr->is_const = 0; + attr->is_special = (special & ATTR_SPECIAL) != 0; + attr->default_val = get_attr_value (value, attr, -2); +} + +/* Find the most used value of an attribute. */ + +static struct attr_value * +find_most_used (struct attr_desc *attr) +{ + struct attr_value *av; + struct attr_value *most_used; + int nuses; + + most_used = NULL; + nuses = -1; + + for (av = attr->first_value; av; av = av->next) + if (av->num_insns > nuses) + nuses = av->num_insns, most_used = av; + + return most_used; +} + +/* Return (attr_value "n") */ + +static rtx +make_numeric_value (int n) +{ + static rtx int_values[20]; + rtx exp; + char *p; + + gcc_assert (n >= 0); + + if (n < 20 && int_values[n]) + return int_values[n]; + + p = attr_printf (MAX_DIGITS, "%d", n); + exp = attr_rtx (CONST_STRING, p); + + if (n < 20) + int_values[n] = exp; + + return exp; +} + +static rtx +copy_rtx_unchanging (rtx orig) +{ + if (ATTR_IND_SIMPLIFIED_P (orig) || ATTR_CURR_SIMPLIFIED_P (orig)) + return orig; + + ATTR_CURR_SIMPLIFIED_P (orig) = 1; + return orig; +} + +/* Determine if an insn has a constant number of delay slots, i.e., the + number of delay slots is not a function of the length of the insn. */ + +static void +write_const_num_delay_slots (void) +{ + struct attr_desc *attr = find_attr (&num_delay_slots_str, 0); + struct attr_value *av; + + if (attr) + { + printf ("int\nconst_num_delay_slots (rtx insn)\n"); + printf ("{\n"); + printf (" switch (recog_memoized (insn))\n"); + printf (" {\n"); + + for (av = attr->first_value; av; av = av->next) + { + length_used = 0; + walk_attr_value (av->value); + if (length_used) + write_insn_cases (av->first_insn, 4); + } + + printf (" default:\n"); + printf (" return 1;\n"); + printf (" }\n}\n\n"); + } +} + +/* Synthetic attributes used by insn-automata.c and the scheduler. + These are primarily concerned with (define_insn_reservation) + patterns. */ + +struct insn_reserv +{ + struct insn_reserv *next; + + const char *name; + int default_latency; + rtx condexp; + + /* Sequence number of this insn. */ + int insn_num; + + /* Whether a (define_bypass) construct names this insn in its + output list. */ + bool bypassed; +}; + +static struct insn_reserv *all_insn_reservs = 0; +static struct insn_reserv **last_insn_reserv_p = &all_insn_reservs; +static size_t n_insn_reservs; + +/* Store information from a DEFINE_INSN_RESERVATION for future + attribute generation. */ +static void +gen_insn_reserv (rtx def) +{ + struct insn_reserv *decl = oballoc (struct insn_reserv); + + decl->name = DEF_ATTR_STRING (XSTR (def, 0)); + decl->default_latency = XINT (def, 1); + decl->condexp = check_attr_test (XEXP (def, 2), 0, 0); + decl->insn_num = n_insn_reservs; + decl->bypassed = false; + decl->next = 0; + + *last_insn_reserv_p = decl; + last_insn_reserv_p = &decl->next; + n_insn_reservs++; +} + +/* Store information from a DEFINE_BYPASS for future attribute + generation. The only thing we care about is the list of output + insns, which will later be used to tag reservation structures with + a 'bypassed' bit. */ + +struct bypass_list +{ + struct bypass_list *next; + const char *insn; +}; + +static struct bypass_list *all_bypasses; +static size_t n_bypasses; + +static void +gen_bypass_1 (const char *s, size_t len) +{ + struct bypass_list *b; + + if (len == 0) + return; + + s = attr_string (s, len); + for (b = all_bypasses; b; b = b->next) + if (s == b->insn) + return; /* already got that one */ + + b = oballoc (struct bypass_list); + b->insn = s; + b->next = all_bypasses; + all_bypasses = b; + n_bypasses++; +} + +static void +gen_bypass (rtx def) +{ + const char *p, *base; + + for (p = base = XSTR (def, 1); *p; p++) + if (*p == ',') + { + gen_bypass_1 (base, p - base); + do + p++; + while (ISSPACE (*p)); + base = p; + } + gen_bypass_1 (base, p - base); +} + +/* Find and mark all of the bypassed insns. */ +static void +process_bypasses (void) +{ + struct bypass_list *b; + struct insn_reserv *r; + + /* The reservation list is likely to be much longer than the bypass + list. */ + for (r = all_insn_reservs; r; r = r->next) + for (b = all_bypasses; b; b = b->next) + if (r->name == b->insn) + r->bypassed = true; +} + +/* Create all of the attributes that describe automaton properties. */ +static void +make_automaton_attrs (void) +{ + int i; + struct insn_reserv *decl; + rtx code_exp, lats_exp, byps_exp; + + if (n_insn_reservs == 0) + return; + + code_exp = rtx_alloc (COND); + lats_exp = rtx_alloc (COND); + + XVEC (code_exp, 0) = rtvec_alloc (n_insn_reservs * 2); + XVEC (lats_exp, 0) = rtvec_alloc (n_insn_reservs * 2); + + XEXP (code_exp, 1) = make_numeric_value (n_insn_reservs + 1); + XEXP (lats_exp, 1) = make_numeric_value (0); + + for (decl = all_insn_reservs, i = 0; + decl; + decl = decl->next, i += 2) + { + XVECEXP (code_exp, 0, i) = decl->condexp; + XVECEXP (lats_exp, 0, i) = decl->condexp; + + XVECEXP (code_exp, 0, i+1) = make_numeric_value (decl->insn_num); + XVECEXP (lats_exp, 0, i+1) = make_numeric_value (decl->default_latency); + } + + if (n_bypasses == 0) + byps_exp = make_numeric_value (0); + else + { + process_bypasses (); + + byps_exp = rtx_alloc (COND); + XVEC (byps_exp, 0) = rtvec_alloc (n_bypasses * 2); + XEXP (byps_exp, 1) = make_numeric_value (0); + for (decl = all_insn_reservs, i = 0; + decl; + decl = decl->next) + if (decl->bypassed) + { + XVECEXP (byps_exp, 0, i) = decl->condexp; + XVECEXP (byps_exp, 0, i+1) = make_numeric_value (1); + i += 2; + } + } + + make_internal_attr ("*internal_dfa_insn_code", code_exp, ATTR_NONE); + make_internal_attr ("*insn_default_latency", lats_exp, ATTR_NONE); + make_internal_attr ("*bypass_p", byps_exp, ATTR_NONE); +} + +int +main (int argc, char **argv) +{ + rtx desc; + struct attr_desc *attr; + struct insn_def *id; + rtx tem; + int i; + + progname = "genattrtab"; + + if (init_md_reader_args (argc, argv) != SUCCESS_EXIT_CODE) + return (FATAL_EXIT_CODE); + + obstack_init (hash_obstack); + obstack_init (temp_obstack); + + /* Set up true and false rtx's */ + true_rtx = rtx_alloc (CONST_INT); + XWINT (true_rtx, 0) = 1; + false_rtx = rtx_alloc (CONST_INT); + XWINT (false_rtx, 0) = 0; + ATTR_IND_SIMPLIFIED_P (true_rtx) = ATTR_IND_SIMPLIFIED_P (false_rtx) = 1; + ATTR_PERMANENT_P (true_rtx) = ATTR_PERMANENT_P (false_rtx) = 1; + + alternative_name = DEF_ATTR_STRING ("alternative"); + length_str = DEF_ATTR_STRING ("length"); + delay_type_str = DEF_ATTR_STRING ("*delay_type"); + delay_1_0_str = DEF_ATTR_STRING ("*delay_1_0"); + num_delay_slots_str = DEF_ATTR_STRING ("*num_delay_slots"); + + printf ("/* Generated automatically by the program `genattrtab'\n\ +from the machine description file `md'. */\n\n"); + + /* Read the machine description. */ + + while (1) + { + int lineno; + + desc = read_md_rtx (&lineno, &insn_code_number); + if (desc == NULL) + break; + + switch (GET_CODE (desc)) + { + case DEFINE_INSN: + case DEFINE_PEEPHOLE: + case DEFINE_ASM_ATTRIBUTES: + gen_insn (desc, lineno); + break; + + case DEFINE_ATTR: + gen_attr (desc, lineno); + break; + + case DEFINE_DELAY: + gen_delay (desc, lineno); + break; + + case DEFINE_INSN_RESERVATION: + gen_insn_reserv (desc); + break; + + case DEFINE_BYPASS: + gen_bypass (desc); + break; + + default: + break; + } + if (GET_CODE (desc) != DEFINE_ASM_ATTRIBUTES) + insn_index_number++; + } + + if (have_error) + return FATAL_EXIT_CODE; + + insn_code_number++; + + /* If we didn't have a DEFINE_ASM_ATTRIBUTES, make a null one. */ + if (! got_define_asm_attributes) + { + tem = rtx_alloc (DEFINE_ASM_ATTRIBUTES); + XVEC (tem, 0) = rtvec_alloc (0); + gen_insn (tem, 0); + } + + /* Expand DEFINE_DELAY information into new attribute. */ + if (num_delays) + expand_delays (); + + printf ("#include \"config.h\"\n"); + printf ("#include \"system.h\"\n"); + printf ("#include \"coretypes.h\"\n"); + printf ("#include \"tm.h\"\n"); + printf ("#include \"rtl.h\"\n"); + printf ("#include \"insn-attr.h\"\n"); + printf ("#include \"tm_p.h\"\n"); + printf ("#include \"insn-config.h\"\n"); + printf ("#include \"recog.h\"\n"); + printf ("#include \"regs.h\"\n"); + printf ("#include \"real.h\"\n"); + printf ("#include \"output.h\"\n"); + printf ("#include \"toplev.h\"\n"); + printf ("#include \"flags.h\"\n"); + printf ("#include \"function.h\"\n"); + printf ("\n"); + printf ("#define operands recog_data.operand\n\n"); + + /* Make `insn_alternatives'. */ + insn_alternatives = oballocvec (int, insn_code_number); + for (id = defs; id; id = id->next) + if (id->insn_code >= 0) + insn_alternatives[id->insn_code] = (1 << id->num_alternatives) - 1; + + /* Make `insn_n_alternatives'. */ + insn_n_alternatives = oballocvec (int, insn_code_number); + for (id = defs; id; id = id->next) + if (id->insn_code >= 0) + insn_n_alternatives[id->insn_code] = id->num_alternatives; + + /* Construct extra attributes for automata. */ + make_automaton_attrs (); + + /* Prepare to write out attribute subroutines by checking everything stored + away and building the attribute cases. */ + + check_defs (); + + for (i = 0; i < MAX_ATTRS_INDEX; i++) + for (attr = attrs[i]; attr; attr = attr->next) + attr->default_val->value + = check_attr_value (attr->default_val->value, attr); + + if (have_error) + return FATAL_EXIT_CODE; + + for (i = 0; i < MAX_ATTRS_INDEX; i++) + for (attr = attrs[i]; attr; attr = attr->next) + fill_attr (attr); + + /* Construct extra attributes for `length'. */ + make_length_attrs (); + + /* Perform any possible optimizations to speed up compilation. */ + optimize_attrs (); + + /* Now write out all the `gen_attr_...' routines. Do these before the + special routines so that they get defined before they are used. */ + + for (i = 0; i < MAX_ATTRS_INDEX; i++) + for (attr = attrs[i]; attr; attr = attr->next) + { + if (! attr->is_special && ! attr->is_const) + write_attr_get (attr); + } + + /* Write out delay eligibility information, if DEFINE_DELAY present. + (The function to compute the number of delay slots will be written + below.) */ + if (num_delays) + { + write_eligible_delay ("delay"); + if (have_annul_true) + write_eligible_delay ("annul_true"); + if (have_annul_false) + write_eligible_delay ("annul_false"); + } + + /* Write out constant delay slot info. */ + write_const_num_delay_slots (); + + write_length_unit_log (); + + fflush (stdout); + return (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE); +} -- cgit v1.2.3