| /* 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 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 2, 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 COPYING. If not, write to the Free |
| Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301, USA. */ |
| |
| /* 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 numer 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. */ |
| }; |
| |
| /* 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; |
| |
| /* 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); |
| /* LLVM LOCAL begin */ |
| #ifndef ENABLE_LLVM |
| 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); |
| #endif |
| /* LLVM LOCAL end */ |
| |
| #define oballoc(size) obstack_alloc (hash_obstack, size) |
| |
| /* 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 = obstack_alloc (hash_obstack, sizeof (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 = obstack_alloc (hash_obstack, sizeof (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 = obstack_alloc (hash_obstack, 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: |
| 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 (sizeof (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 (sizeof (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) |
| { |
| /* LLVM LOCAL begin */ |
| #ifndef ENABLE_LLVM |
| 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 *new_attr; |
| struct attr_value *av, *new_av; |
| struct insn_ent *ie, *new_ie; |
| #endif |
| struct attr_desc *length_attr; |
| /* LLVM LOCAL end */ |
| |
| /* 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; |
| |
| /* LLVM LOCAL begin we don't need these for llvm */ |
| #ifndef ENABLE_LLVM |
| /* 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 (sizeof (struct insn_ent)); |
| new_ie->def = ie->def; |
| insert_insn_ent (new_av, new_ie); |
| } |
| } |
| #endif |
| /* LLVM LOCAL end */ |
| } |
| |
| /* LLVM LOCAL begin */ |
| #ifndef ENABLE_LLVM |
| /* 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)); |
| } |
| #endif |
| /* LLVM LOCAL end */ |
| |
| 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 = insert_right_side (code, XEXP (exp, 1), |
| term, insn_code, insn_index); |
| if (new != XEXP (exp, 1)) |
| /* Make a copy of this expression and call recursively. */ |
| newexp = attr_rtx (code, XEXP (exp, 0), new); |
| 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 = simplify_test_exp_in_temp (XVECEXP (value, 0, i), |
| insn_code, insn_index); |
| |
| right = insert_right_side (AND, andexp, this, |
| 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); |
| 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; |
| 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) |
| 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) |
| { |
| rtx x; |
| 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 |
| { |
| struct attr_value *av; |
| struct insn_ent *ie; |
| struct attr_desc *attr; |
| struct attr_value_list *next; |
| }; |
| struct attr_value_list **insn_code_values; |
| 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); |
| } |
| |
| /* 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 (sizeof (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; |
| } |
| |
| /* Returns nonzero is INNER is contained in EXP. */ |
| |
| static int |
| contained_in_p (rtx inner, rtx exp) |
| { |
| int i, j; |
| const char *fmt; |
| |
| if (rtx_equal_p (inner, exp)) |
| 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 (contained_in_p (inner, XEXP (exp, i))) |
| return 1; |
| break; |
| |
| case 'E': |
| for (j = 0; j < XVECLEN (exp, i); j++) |
| if (contained_in_p (inner, 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 (sizeof (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 (sizeof (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"); |
| } |
| |
| /* Search for uses of non-const attributes and write code to cache them. */ |
| |
| static int |
| write_expr_attr_cache (rtx p, struct attr_desc *attr) |
| { |
| const char *fmt; |
| int i, ie, j, je; |
| |
| if (GET_CODE (p) == EQ_ATTR) |
| { |
| if (XSTR (p, 0) != attr->name) |
| return 0; |
| |
| if (!attr->is_numeric) |
| printf (" enum attr_%s ", attr->name); |
| else |
| printf (" int "); |
| |
| printf ("attr_%s = get_attr_%s (insn);\n", attr->name, attr->name); |
| return 1; |
| } |
| |
| fmt = GET_RTX_FORMAT (GET_CODE (p)); |
| ie = GET_RTX_LENGTH (GET_CODE (p)); |
| for (i = 0; i < ie; i++) |
| { |
| switch (*fmt++) |
| { |
| case 'e': |
| if (write_expr_attr_cache (XEXP (p, i), attr)) |
| return 1; |
| break; |
| |
| case 'E': |
| je = XVECLEN (p, i); |
| for (j = 0; j < je; ++j) |
| if (write_expr_attr_cache (XVECEXP (p, i, j), attr)) |
| return 1; |
| break; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* 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 (sizeof (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 (sizeof (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 (sizeof (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; |
| } |
| } |
| |
| /* LLVM LOCAL begin */ |
| #ifndef ENABLE_LLVM |
| 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); |
| #endif |
| /* LLVM LOCAL end */ |
| } |
| |
| 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 \"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 \"insn-attr.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 = oballoc (insn_code_number * sizeof (int)); |
| 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 = oballoc (insn_code_number * sizeof (int)); |
| 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); |
| } |