| /* Generate code from machine description to compute values of attributes. |
| Copyright (C) 1991-2022 Free Software Foundation, Inc. |
| Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu) |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it under |
| the terms of the GNU General Public License as published by the Free |
| Software Foundation; either version 3, or (at your option) any later |
| version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| /* 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 "obstack.h" |
| #include "errors.h" |
| #include "read-md.h" |
| #include "gensupport.h" |
| #include "fnmatch.h" |
| |
| #define DEBUG 0 |
| |
| /* 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. */ |
| |
| class insn_def |
| { |
| public: |
| class insn_def *next; /* Next insn in chain. */ |
| rtx def; /* The DEFINE_... */ |
| int insn_code; /* Instruction number. */ |
| int insn_index; /* Expression number in file, for errors. */ |
| file_location loc; /* Where in the .md files it occurs. */ |
| 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. */ |
| class 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. */ |
| |
| class attr_desc |
| { |
| public: |
| char *name; /* Name of attribute. */ |
| const char *enum_name; /* Enum name for DEFINE_ENUM_NAME. */ |
| class 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. */ |
| file_location loc; /* Where in the .md files it occurs. */ |
| 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. */ |
| |
| class delay_desc |
| { |
| public: |
| rtx def; /* DEFINE_DELAY expression. */ |
| class delay_desc *next; /* Next DEFINE_DELAY. */ |
| file_location loc; /* Where in the .md files it occurs. */ |
| int num; /* Number of DEFINE_DELAY, starting at 1. */ |
| }; |
| |
| struct attr_value_list |
| { |
| struct attr_value *av; |
| struct insn_ent *ie; |
| class attr_desc *attr; |
| struct attr_value_list *next; |
| }; |
| |
| /* Listheads of above structures. */ |
| |
| /* This one is indexed by the first character of the attribute name. */ |
| #define MAX_ATTRS_INDEX 256 |
| static class attr_desc *attrs[MAX_ATTRS_INDEX]; |
| static class insn_def *defs; |
| static class delay_desc *delays; |
| struct attr_value_list **insn_code_values; |
| |
| /* Other variables. */ |
| |
| 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. */ |
| |
| /* Keep this in sync with recog.h. */ |
| typedef uint64_t alternative_mask; |
| static alternative_mask *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 class attr_desc *find_attr (const char **, int); |
| static rtx mk_attr_alt (alternative_mask); |
| 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); |
| static int min_attr_value (rtx); |
| static unsigned int attr_value_alignment (rtx); |
| 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 (FILE *, class attr_desc *, const char *); |
| static struct attr_value *find_most_used (class attr_desc *); |
| static void write_attr_set (FILE *, class attr_desc *, int, rtx, |
| const char *, const char *, rtx, |
| int, int, unsigned int); |
| static void write_attr_case (FILE *, class attr_desc *, |
| struct attr_value *, |
| int, const char *, const char *, int, rtx); |
| static void write_attr_value (FILE *, class attr_desc *, rtx); |
| static void write_upcase (FILE *, const char *); |
| static void write_indent (FILE *, int); |
| static rtx identity_fn (rtx); |
| static rtx zero_fn (rtx); |
| static rtx one_fn (rtx); |
| static rtx max_fn (rtx); |
| static rtx min_fn (rtx); |
| |
| #define oballoc(T) XOBNEW (hash_obstack, T) |
| #define oballocvec(T, N) XOBNEWVEC (hash_obstack, T, (N)) |
| |
| /* This gen* file is unique, in that it writes out multiple files. |
| |
| Before GCC 4.8, insn-attrtab.cc was written out containing many large |
| functions and tables. This made insn-attrtab.cc _the_ bottle-neck in |
| a parallel build, and even made it impossible to build GCC on machines |
| with relatively small RAM space (PR other/29442). Therefore, the |
| atrribute functions/tables are now written out to three separate |
| files: all "*insn_default_latency" functions go to LATENCY_FILE_NAME, |
| all "*internal_dfa_insn_code" functions go to DFA_FILE_NAME, and the |
| rest goes to ATTR_FILE_NAME. */ |
| |
| static const char *attr_file_name = NULL; |
| static const char *dfa_file_name = NULL; |
| static const char *latency_file_name = NULL; |
| |
| static FILE *attr_file, *dfa_file, *latency_file; |
| |
| /* 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. */ |
| unsigned 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) ((intptr_t) (RTL) & 0777777) |
| |
| /* Add an entry to the hash table for RTL with hash code HASHCODE. */ |
| |
| static void |
| attr_hash_add_rtx (unsigned int hashcode, rtx rtl) |
| { |
| struct attr_hash *h; |
| |
| h = XOBNEW (hash_obstack, struct attr_hash); |
| h->hashcode = hashcode; |
| h->u.rtl = rtl; |
| h->next = attr_hash_table[hashcode % RTL_HASH_SIZE]; |
| attr_hash_table[hashcode % RTL_HASH_SIZE] = h; |
| } |
| |
| /* Add an entry to the hash table for STRING with hash code HASHCODE. */ |
| |
| static void |
| attr_hash_add_string (unsigned int hashcode, char *str) |
| { |
| struct attr_hash *h; |
| |
| h = XOBNEW (hash_obstack, struct attr_hash); |
| h->hashcode = -hashcode; |
| h->u.str = str; |
| h->next = attr_hash_table[hashcode % RTL_HASH_SIZE]; |
| attr_hash_table[hashcode % RTL_HASH_SIZE] = h; |
| } |
| |
| /* Generate an RTL expression, but avoid duplicates. |
| Set the ATTR_PERMANENT_P flag for these permanent objects. |
| |
| In some cases we cannot uniquify; then we return an ordinary |
| impermanent rtx with ATTR_PERMANENT_P clear. |
| |
| Args are as follows: |
| |
| rtx attr_rtx (code, [element1, ..., elementn]) */ |
| |
| static rtx |
| attr_rtx_1 (enum rtx_code code, va_list p) |
| { |
| rtx rt_val = NULL_RTX;/* RTX to return to caller... */ |
| unsigned int hashcode; |
| struct attr_hash *h; |
| struct obstack *old_obstack = rtl_obstack; |
| int permanent_p = 1; |
| |
| /* 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); |
| |
| if (! ATTR_PERMANENT_P (arg0)) |
| permanent_p = 0; |
| |
| 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); |
| |
| if (! ATTR_PERMANENT_P (arg0) || ! ATTR_PERMANENT_P (arg1)) |
| permanent_p = 0; |
| |
| 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) |
| { |
| ATTR_CURR_SIMPLIFIED_P (h->u.rtl) = 0; |
| 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 (code == SYMBOL_REF |
| || (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; |
| if (code == SYMBOL_REF) |
| X0EXP (rt_val, 1) = NULL_RTX; |
| } |
| } |
| 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 *); |
| |
| arg0 = DEF_ATTR_STRING (arg0); |
| arg1 = DEF_ATTR_STRING (arg1); |
| |
| 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 (GET_RTX_LENGTH (code) == 2 |
| && GET_RTX_FORMAT (code)[0] == 'w' |
| && GET_RTX_FORMAT (code)[1] == 'w') |
| { |
| HOST_WIDE_INT arg0 = va_arg (p, HOST_WIDE_INT); |
| HOST_WIDE_INT arg1 = va_arg (p, HOST_WIDE_INT); |
| |
| 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 |
| && XWINT (h->u.rtl, 0) == arg0 |
| && XWINT (h->u.rtl, 1) == arg1) |
| return h->u.rtl; |
| |
| if (h == 0) |
| { |
| rtl_obstack = hash_obstack; |
| rt_val = rtx_alloc (code); |
| XWINT (rt_val, 0) = arg0; |
| XWINT (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: |
| /* Don't need to handle 'p' for attributes. */ |
| gcc_unreachable (); |
| } |
| } |
| return rt_val; |
| } |
| |
| rtl_obstack = old_obstack; |
| attr_hash_add_rtx (hashcode, rt_val); |
| ATTR_PERMANENT_P (rt_val) = permanent_p; |
| 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, name, 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; |
| unsigned int hashcode; |
| int i; |
| char *new_str; |
| |
| /* Compute the hash code. */ |
| hashcode = (len + 1) * 613U + (unsigned) str[0]; |
| for (i = 1; i < len; i += 2) |
| hashcode = ((hashcode * 613) + (unsigned) str[i]); |
| if ((int) hashcode < 0) |
| hashcode = -hashcode; |
| |
| /* Search the table for the string. */ |
| for (h = attr_hash_table[hashcode % RTL_HASH_SIZE]; h; h = h->next) |
| if (h->hashcode == -hashcode && h->u.str[0] == str[0] |
| && !strncmp (h->u.str, str, len)) |
| return h->u.str; /* <-- return if found. */ |
| |
| /* Not found; create a permanent copy and add it to the hash table. */ |
| new_str = XOBNEWVAR (hash_obstack, char, len + 1); |
| memcpy (new_str, str, len); |
| new_str[len] = '\0'; |
| attr_hash_add_string (hashcode, new_str); |
| rtx_reader_ptr->copy_md_ptr_loc (new_str, 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))); |
| } |
| |
| /* Given a test expression EXP for attribute ATTR, ensure it is validly |
| formed. LOC is the location of the .md construct that contains EXP. |
| |
| 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 (file_location loc, rtx exp, 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 (loc, |
| attr_rtx (NOT, |
| attr_eq (XSTR (exp, 0), |
| &XSTR (exp, 1)[1])), |
| attr); |
| |
| else if (n_comma_elts (XSTR (exp, 1)) == 1) |
| { |
| attr_desc *attr2 = find_attr (&XSTR (exp, 0), 0); |
| if (attr2 == NULL) |
| { |
| if (! strcmp (XSTR (exp, 0), "alternative")) |
| return mk_attr_alt (((alternative_mask) 1) |
| << atoi (XSTR (exp, 1))); |
| else |
| fatal_at (loc, "unknown attribute `%s' in definition of" |
| " attribute `%s'", XSTR (exp, 0), attr->name); |
| } |
| |
| if (attr->is_const && ! attr2->is_const) |
| fatal_at (loc, "constant attribute `%s' cannot test non-constant" |
| " attribute `%s'", attr->name, attr2->name); |
| |
| /* 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 (attr2->is_const) |
| ATTR_IND_SIMPLIFIED_P (exp) = 1; |
| |
| if (attr2->is_numeric) |
| { |
| for (p = XSTR (exp, 1); *p; p++) |
| if (! ISDIGIT (*p)) |
| fatal_at (loc, "attribute `%s' takes only numeric values", |
| attr2->name); |
| } |
| else |
| { |
| for (av = attr2->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_at (loc, "unknown value `%s' for attribute `%s'", |
| XSTR (exp, 1), attr2->name); |
| } |
| } |
| else |
| { |
| if (! strcmp (XSTR (exp, 0), "alternative")) |
| { |
| int set = 0; |
| |
| name_ptr = XSTR (exp, 1); |
| while ((p = next_comma_elt (&name_ptr)) != NULL) |
| set |= ((alternative_mask) 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 (loc, orexp, attr); |
| } |
| } |
| 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 (loc, XEXP (exp, 0), attr); |
| XEXP (exp, 1) = check_attr_test (loc, XEXP (exp, 1), attr); |
| break; |
| |
| case NOT: |
| XEXP (exp, 0) = check_attr_test (loc, XEXP (exp, 0), attr); |
| break; |
| |
| case MATCH_TEST: |
| exp = attr_rtx (MATCH_TEST, XSTR (exp, 0)); |
| ATTR_IND_SIMPLIFIED_P (exp) = 1; |
| break; |
| |
| case MATCH_OPERAND: |
| if (attr->is_const) |
| fatal_at (loc, "invalid operator `%s' in definition of constant" |
| " attribute `%s'", GET_RTX_NAME (GET_CODE (exp)), |
| attr->name); |
| /* 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 (attr->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; |
| } |
| /* FALLTHRU */ |
| default: |
| fatal_at (loc, "invalid operator `%s' in definition of attribute" |
| " `%s'", GET_RTX_NAME (GET_CODE (exp)), attr->name); |
| } |
| |
| return exp; |
| } |
| |
| /* Given an expression EXP, ensure that it is validly formed and that |
| all named attribute values are valid for ATTR. Issue an error if not. |
| LOC is the location of the .md construct that contains EXP. |
| |
| Return a perhaps modified replacement expression for the value. */ |
| |
| static rtx |
| check_attr_value (file_location loc, rtx exp, class attr_desc *attr) |
| { |
| struct attr_value *av; |
| const char *p; |
| int i; |
| |
| switch (GET_CODE (exp)) |
| { |
| case CONST_INT: |
| if (!attr->is_numeric) |
| { |
| error_at (loc, |
| "CONST_INT not valid for non-numeric attribute `%s'", |
| attr->name); |
| break; |
| } |
| |
| if (INTVAL (exp) < 0) |
| { |
| error_at (loc, |
| "negative numeric value specified for attribute `%s'", |
| attr->name); |
| break; |
| } |
| break; |
| |
| case CONST_STRING: |
| if (! strcmp (XSTR (exp, 0), "*")) |
| break; |
| |
| if (attr->is_numeric) |
| { |
| p = XSTR (exp, 0); |
| for (; *p; p++) |
| if (! ISDIGIT (*p)) |
| { |
| error_at (loc, |
| "non-numeric value specified for numeric" |
| " attribute `%s'", attr->name); |
| 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) |
| error_at (loc, "unknown value `%s' for attribute `%s'", |
| XSTR (exp, 0), attr->name); |
| break; |
| |
| case IF_THEN_ELSE: |
| XEXP (exp, 0) = check_attr_test (loc, XEXP (exp, 0), attr); |
| XEXP (exp, 1) = check_attr_value (loc, XEXP (exp, 1), attr); |
| XEXP (exp, 2) = check_attr_value (loc, XEXP (exp, 2), attr); |
| break; |
| |
| case PLUS: |
| case MINUS: |
| case MULT: |
| case DIV: |
| case MOD: |
| if (!attr->is_numeric) |
| { |
| error_at (loc, "invalid operation `%s' for non-numeric" |
| " attribute `%s'", GET_RTX_NAME (GET_CODE (exp)), |
| attr->name); |
| break; |
| } |
| /* Fall through. */ |
| |
| case IOR: |
| case AND: |
| XEXP (exp, 0) = check_attr_value (loc, XEXP (exp, 0), attr); |
| XEXP (exp, 1) = check_attr_value (loc, XEXP (exp, 1), attr); |
| break; |
| |
| case FFS: |
| case CLZ: |
| case CTZ: |
| case POPCOUNT: |
| case PARITY: |
| case BSWAP: |
| XEXP (exp, 0) = check_attr_value (loc, XEXP (exp, 0), attr); |
| break; |
| |
| case COND: |
| if (XVECLEN (exp, 0) % 2 != 0) |
| { |
| error_at (loc, "first operand of COND must have even length"); |
| break; |
| } |
| |
| for (i = 0; i < XVECLEN (exp, 0); i += 2) |
| { |
| XVECEXP (exp, 0, i) = check_attr_test (attr->loc, |
| XVECEXP (exp, 0, i), |
| attr); |
| XVECEXP (exp, 0, i + 1) |
| = check_attr_value (loc, XVECEXP (exp, 0, i + 1), attr); |
| } |
| |
| XEXP (exp, 1) = check_attr_value (loc, XEXP (exp, 1), attr); |
| break; |
| |
| case ATTR: |
| { |
| class attr_desc *attr2 = find_attr (&XSTR (exp, 0), 0); |
| if (attr2 == NULL) |
| error_at (loc, "unknown attribute `%s' in ATTR", |
| XSTR (exp, 0)); |
| else if (attr->is_const && ! attr2->is_const) |
| error_at (attr->loc, |
| "constant attribute `%s' cannot refer to non-constant" |
| " attribute `%s'", attr->name, attr2->name); |
| else if (attr->is_numeric != attr2->is_numeric) |
| error_at (loc, |
| "numeric attribute mismatch calling `%s' from `%s'", |
| attr2->name, attr->name); |
| } |
| 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: |
| error_at (loc, "invalid operator `%s' in definition of attribute `%s'", |
| GET_RTX_NAME (GET_CODE (exp)), attr->name); |
| 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, class insn_def *id) |
| { |
| int num_alt = id->num_alternatives; |
| rtx condexp; |
| int i; |
| |
| if (XVECLEN (exp, 1) != num_alt) |
| { |
| error_at (id->loc, "bad number of entries in SET_ATTR_ALTERNATIVE," |
| " was %d expected %d", XVECLEN (exp, 1), num_alt); |
| 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, class 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) |
| { |
| class insn_def *id; |
| class 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) |
| { |
| error_at (id->loc, "bad attribute set"); |
| 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: |
| error_at (id->loc, "invalid attribute code %s", |
| GET_RTX_NAME (GET_CODE (value))); |
| value = NULL_RTX; |
| } |
| if (value == NULL_RTX) |
| continue; |
| |
| if ((attr = find_attr (&XSTR (XEXP (value, 0), 0), 0)) == NULL) |
| { |
| error_at (id->loc, "unknown attribute %s", |
| XSTR (XEXP (value, 0), 0)); |
| continue; |
| } |
| |
| XVECEXP (id->def, id->vec_idx, i) = value; |
| XEXP (value, 1) = check_attr_value (id->loc, 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. LOC is the location to use for error reporting. */ |
| |
| static rtx |
| make_canonical (file_location loc, class 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->default_val == 0) |
| fatal_at (loc, "(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 (loc, 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. */ |
| gcc_fallthrough (); |
| |
| case COND: |
| { |
| int allsame = 1; |
| rtx defval; |
| |
| /* First, check for degenerate COND. */ |
| if (XVECLEN (exp, 0) == 0) |
| return make_canonical (loc, attr, XEXP (exp, 1)); |
| defval = XEXP (exp, 1) = make_canonical (loc, 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 (loc, attr, XVECEXP (exp, 0, i + 1)); |
| if (! attr_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))); |
| else if (GET_CODE (exp) == NOT) |
| return attr_rtx (NOT, copy_boolean (XEXP (exp, 0))); |
| 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. LOC is the location to use for error reporting. */ |
| |
| static struct attr_value * |
| get_attr_value (file_location loc, rtx value, class attr_desc *attr, |
| int insn_code) |
| { |
| struct attr_value *av; |
| alternative_mask num_alt = 0; |
| |
| value = make_canonical (loc, attr, value); |
| if (compares_alternatives_p (value)) |
| { |
| if (insn_code < 0 || insn_alternatives == NULL) |
| fatal_at (loc, "(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 (attr_equal_p (value, av->value) |
| && (num_alt == 0 || av->first_insn == NULL |
| || insn_alternatives[av->first_insn->def->insn_code])) |
| return av; |
| |
| av = oballoc (struct attr_value); |
| av->value = value; |
| av->next = attr->first_value; |
| attr->first_value = av; |
| av->first_insn = NULL; |
| av->num_insns = 0; |
| av->has_asm_insn = 0; |
| |
| return av; |
| } |
| |
| /* After all DEFINE_DELAYs have been read in, create internal attributes |
| to generate the required routines. |
| |
| First, we compute the number of delay slots for each insn (as a COND of |
| each of the test expressions in DEFINE_DELAYs). Then, if more than one |
| delay type is specified, we compute a similar function giving the |
| DEFINE_DELAY ordinal for each insn. |
| |
| Finally, for each [DEFINE_DELAY, slot #] pair, we compute an attribute that |
| tells whether a given insn can be in that delay slot. |
| |
| Normal attribute filling and optimization expands these to contain the |
| information needed to handle delay slots. */ |
| |
| static void |
| expand_delays (void) |
| { |
| class 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 (class attr_desc *attr) |
| { |
| struct attr_value *av; |
| struct insn_ent *ie; |
| class 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 (id->loc, value, attr, id->insn_code); |
| |
| ie = oballoc (struct insn_ent); |
| ie->def = id; |
| insert_insn_ent (av, ie); |
| } |
| } |
| |
| /* Given an expression EXP, see if it is a COND or IF_THEN_ELSE that has a |
| test that checks relative positions of insns (uses MATCH_DUP or PC). |
| If so, replace it with what is obtained by passing the expression to |
| ADDRESS_FN. If not but it is a COND or IF_THEN_ELSE, call this routine |
| recursively on each value (including the default value). Otherwise, |
| return the value returned by NO_ADDRESS_FN applied to EXP. */ |
| |
| static rtx |
| substitute_address (rtx exp, rtx (*no_address_fn) (rtx), |
| rtx (*address_fn) (rtx)) |
| { |
| int i; |
| rtx newexp; |
| |
| if (GET_CODE (exp) == COND) |
| { |
| /* See if any tests use addresses. */ |
| address_used = 0; |
| for (i = 0; i < XVECLEN (exp, 0); i += 2) |
| walk_attr_value (XVECEXP (exp, 0, i)); |
| |
| if (address_used) |
| return (*address_fn) (exp); |
| |
| /* Make a new copy of this COND, replacing each element. */ |
| newexp = rtx_alloc (COND); |
| XVEC (newexp, 0) = rtvec_alloc (XVECLEN (exp, 0)); |
| for (i = 0; i < XVECLEN (exp, 0); i += 2) |
| { |
| XVECEXP (newexp, 0, i) = XVECEXP (exp, 0, i); |
| XVECEXP (newexp, 0, i + 1) |
| = substitute_address (XVECEXP (exp, 0, i + 1), |
| no_address_fn, address_fn); |
| } |
| |
| XEXP (newexp, 1) = substitute_address (XEXP (exp, 1), |
| no_address_fn, address_fn); |
| |
| return newexp; |
| } |
| |
| else if (GET_CODE (exp) == IF_THEN_ELSE) |
| { |
| address_used = 0; |
| walk_attr_value (XEXP (exp, 0)); |
| if (address_used) |
| return (*address_fn) (exp); |
| |
| return attr_rtx (IF_THEN_ELSE, |
| substitute_address (XEXP (exp, 0), |
| no_address_fn, address_fn), |
| substitute_address (XEXP (exp, 1), |
| no_address_fn, address_fn), |
| substitute_address (XEXP (exp, 2), |
| no_address_fn, address_fn)); |
| } |
| |
| return (*no_address_fn) (exp); |
| } |
| |
| /* Make new attributes from the `length' attribute. The following are made, |
| each corresponding to a function called from `shorten_branches' or |
| `get_attr_length': |
| |
| *insn_default_length This is the length of the insn to be returned |
| by `get_attr_length' before `shorten_branches' |
| has been called. In each case where the length |
| depends on relative addresses, the largest |
| possible is used. This routine is also used |
| to compute the initial size of the insn. |
| |
| *insn_variable_length_p This returns 1 if the insn's length depends |
| on relative addresses, zero otherwise. |
| |
| *insn_current_length This is only called when it is known that the |
| insn has a variable length and returns the |
| current length, based on relative addresses. |
| */ |
| |
| static void |
| make_length_attrs (void) |
| { |
| static const char *new_names[] = |
| { |
| "*insn_default_length", |
| "*insn_min_length", |
| "*insn_variable_length_p", |
| "*insn_current_length" |
| }; |
| static rtx (*const no_address_fn[]) (rtx) |
| = {identity_fn,identity_fn, zero_fn, zero_fn}; |
| static rtx (*const address_fn[]) (rtx) |
| = {max_fn, min_fn, one_fn, identity_fn}; |
| size_t i; |
| class attr_desc *length_attr, *new_attr; |
| struct attr_value *av, *new_av; |
| struct insn_ent *ie, *new_ie; |
| |
| /* See if length attribute is defined. If so, it must be numeric. Make |
| it special so we don't output anything for it. */ |
| length_attr = find_attr (&length_str, 0); |
| if (length_attr == 0) |
| return; |
| |
| if (! length_attr->is_numeric) |
| fatal_at (length_attr->loc, "length attribute must be numeric"); |
| |
| length_attr->is_const = 0; |
| length_attr->is_special = 1; |
| |
| /* Make each new attribute, in turn. */ |
| for (i = 0; i < ARRAY_SIZE (new_names); i++) |
| { |
| make_internal_attr (new_names[i], |
| substitute_address (length_attr->default_val->value, |
| no_address_fn[i], address_fn[i]), |
| ATTR_NONE); |
| new_attr = find_attr (&new_names[i], 0); |
| for (av = length_attr->first_value; av; av = av->next) |
| for (ie = av->first_insn; ie; ie = ie->next) |
| { |
| new_av = get_attr_value (ie->def->loc, |
| substitute_address (av->value, |
| no_address_fn[i], |
| address_fn[i]), |
| new_attr, ie->def->insn_code); |
| new_ie = oballoc (struct insn_ent); |
| new_ie->def = ie->def; |
| insert_insn_ent (new_av, new_ie); |
| } |
| } |
| } |
| |
| /* Utility functions called from above routine. */ |
| |
| static rtx |
| identity_fn (rtx exp) |
| { |
| return exp; |
| } |
| |
| static rtx |
| zero_fn (rtx exp ATTRIBUTE_UNUSED) |
| { |
| return make_numeric_value (0); |
| } |
| |
| static rtx |
| one_fn (rtx exp ATTRIBUTE_UNUSED) |
| { |
| return make_numeric_value (1); |
| } |
| |
| static rtx |
| max_fn (rtx exp) |
| { |
| return make_numeric_value (max_attr_value (exp)); |
| } |
| |
| static rtx |
| min_fn (rtx exp) |
| { |
| return make_numeric_value (min_attr_value (exp)); |
| } |
| |
| static void |
| write_length_unit_log (FILE *outf) |
| { |
| class attr_desc *length_attr = find_attr (&length_str, 0); |
| struct attr_value *av; |
| struct insn_ent *ie; |
| unsigned int length_unit_log, length_or; |
| |
| if (length_attr) |
| { |
| length_or = attr_value_alignment (length_attr->default_val->value); |
| for (av = length_attr->first_value; av; av = av->next) |
| for (ie = av->first_insn; ie; ie = ie->next) |
| length_or |= attr_value_alignment (av->value); |
| |
| length_or = ~length_or; |
| for (length_unit_log = 0; length_or & 1; length_or >>= 1) |
| length_unit_log++; |
| } |
| else |
| length_unit_log = 0; |
| |
| fprintf (outf, "EXPORTED_CONST int length_unit_log = %u;\n", length_unit_log); |
| } |
| |
| /* 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 = 1; |
| 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 1; |
| |
| case EQ_ATTR_ALT: |
| return 1; |
| |
| case EQ_ATTR: |
| /* Alternatives don't result into function call. */ |
| if (!strcmp_check (XSTR (x, 0), alternative_name)) |
| return 1; |
| 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; |
| } |
| |
| /* Take a COND expression and see if any of the conditions in it can be |
| simplified. If any are known true or known false for the particular insn |
| code, the COND can be further simplified. |
| |
| Also call ourselves on any COND operations that are values of this COND. |
| |
| We do not modify EXP; rather, we make and return a new rtx. */ |
| |
| static rtx |
| simplify_cond (rtx exp, int insn_code, int insn_index) |
| { |
| int i, j; |
| /* We store the desired contents here, |
| then build a new expression if they don't match EXP. */ |
| rtx defval = XEXP (exp, 1); |
| rtx new_defval = XEXP (exp, 1); |
| int len = XVECLEN (exp, 0); |
| rtx *tests = XNEWVEC (rtx, len); |
| int allsame = 1; |
| rtx ret; |
| |
| /* This lets us free all storage allocated below, if appropriate. */ |
| obstack_finish (rtl_obstack); |
| |
| memcpy (tests, XVEC (exp, 0)->elem, len * sizeof (rtx)); |
| |
| /* See if default value needs simplification. */ |
| if (GET_CODE (defval) == COND) |
| new_defval = simplify_cond (defval, insn_code, insn_index); |
| |
| /* Simplify the subexpressions, and see what tests we can get rid of. */ |
| |
| for (i = 0; i < len; i += 2) |
| { |
| rtx newtest, newval; |
| |
| /* Simplify this test. */ |
| newtest = simplify_test_exp_in_temp (tests[i], insn_code, insn_index); |
| tests[i] = newtest; |
| |
| newval = tests[i + 1]; |
| /* See if this value may need simplification. */ |
| if (GET_CODE (newval) == COND) |
| newval = simplify_cond (newval, insn_code, insn_index); |
| |
| /* Look for ways to delete or combine this test. */ |
| if (newtest == true_rtx) |
| { |
| /* If test is true, make this value the default |
| and discard this + any following tests. */ |
| len = i; |
| defval = tests[i + 1]; |
| new_defval = newval; |
| } |
| |
| else if (newtest == false_rtx) |
| { |
| /* If test is false, discard it and its value. */ |
| for (j = i; j < len - 2; j++) |
| tests[j] = tests[j + 2]; |
| i -= 2; |
| len -= 2; |
| } |
| |
| else if (i > 0 && attr_equal_p (newval, tests[i - 1])) |
| { |
| /* If this value and the value for the prev test are the same, |
| merge the tests. */ |
| |
| tests[i - 2] |
| = insert_right_side (IOR, tests[i - 2], newtest, |
| insn_code, insn_index); |
| |
| /* Delete this test/value. */ |
| for (j = i; j < len - 2; j++) |
| tests[j] = tests[j + 2]; |
| len -= 2; |
| i -= 2; |
| } |
| |
| else |
| tests[i + 1] = newval; |
| } |
| |
| /* If the last test in a COND has the same value |
| as the default value, that test isn't needed. */ |
| |
| while (len > 0 && attr_equal_p (tests[len - 1], new_defval)) |
| len -= 2; |
| |
| /* See if we changed anything. */ |
| if (len != XVECLEN (exp, 0) || new_defval != XEXP (exp, 1)) |
| allsame = 0; |
| else |
| for (i = 0; i < len; i++) |
| if (! attr_equal_p (tests[i], XVECEXP (exp, 0, i))) |
| { |
| allsame = 0; |
| break; |
| } |
| |
| if (len == 0) |
| { |
| if (GET_CODE (defval) == COND) |
| ret = simplify_cond (defval, insn_code, insn_index); |
| else |
| ret = defval; |
| } |
| else if (allsame) |
| ret = exp; |
| else |
| { |
| rtx newexp = rtx_alloc (COND); |
| |
| XVEC (newexp, 0) = rtvec_alloc (len); |
| memcpy (XVEC (newexp, 0)->elem, tests, len * sizeof (rtx)); |
| XEXP (newexp, 1) = new_defval; |
| ret = newexp; |
| } |
| free (tests); |
| return ret; |
| } |
| |
| /* Remove an insn entry from an attribute value. */ |
| |
| static void |
| remove_insn_ent (struct attr_value *av, struct insn_ent *ie) |
| { |
| struct insn_ent *previe; |
| |
| if (av->first_insn == ie) |
| av->first_insn = ie->next; |
| else |
| { |
| for (previe = av->first_insn; previe->next != ie; previe = previe->next) |
| ; |
| previe->next = ie->next; |
| } |
| |
| av->num_insns--; |
| if (ie->def->insn_code == -1) |
| av->has_asm_insn = 0; |
| |
| num_insn_ents--; |
| } |
| |
| /* Insert an insn entry in an attribute value list. */ |
| |
| static void |
| insert_insn_ent (struct attr_value *av, struct insn_ent *ie) |
| { |
| ie->next = av->first_insn; |
| av->first_insn = ie; |
| av->num_insns++; |
| if (ie->def->insn_code == -1) |
| av->has_asm_insn = 1; |
| |
| num_insn_ents++; |
| } |
| |
| /* This is a utility routine to take an expression that is a tree of either |
| AND or IOR expressions and insert a new term. The new term will be |
| inserted at the right side of the first node whose code does not match |
| the root. A new node will be created with the root's code. Its left |
| side will be the old right side and its right side will be the new |
| term. |
| |
| If the `term' is itself a tree, all its leaves will be inserted. */ |
| |
| static rtx |
| insert_right_side (enum rtx_code code, rtx exp, rtx term, int insn_code, int insn_index) |
| { |
| rtx newexp; |
| |
| /* Avoid consing in some special cases. */ |
| if (code == AND && term == true_rtx) |
| return exp; |
| if (code == AND && term == false_rtx) |
| return false_rtx; |
| if (code == AND && exp == true_rtx) |
| return term; |
| if (code == AND && exp == false_rtx) |
| return false_rtx; |
| if (code == IOR && term == true_rtx) |
| return true_rtx; |
| if (code == IOR && term == false_rtx) |
| return exp; |
| if (code == IOR && exp == true_rtx) |
| return true_rtx; |
| if (code == IOR && exp == false_rtx) |
| return term; |
| if (attr_equal_p (exp, term)) |
| return exp; |
| |
| if (GET_CODE (term) == code) |
| { |
| exp = insert_right_side (code, exp, XEXP (term, 0), |
| insn_code, insn_index); |
| exp = insert_right_side (code, exp, XEXP (term, 1), |
| insn_code, insn_index); |
| |
| return exp; |
| } |
| |
| if (GET_CODE (exp) == code) |
| { |
| rtx new_rtx = insert_right_side (code, XEXP (exp, 1), |
| term, insn_code, insn_index); |
| if (new_rtx != XEXP (exp, 1)) |
| /* Make a copy of this expression and call recursively. */ |
| newexp = attr_rtx (code, XEXP (exp, 0), new_rtx); |
| else |
| newexp = exp; |
| } |
| else |
| { |
| /* Insert the new term. */ |
| newexp = attr_rtx (code, exp, term); |
| } |
| |
| return simplify_test_exp_in_temp (newexp, insn_code, insn_index); |
| } |
| |
| /* If we have an expression which AND's a bunch of |
| (not (eq_attrq "alternative" "n")) |
| terms, we may have covered all or all but one of the possible alternatives. |
| If so, we can optimize. Similarly for IOR's of EQ_ATTR. |
| |
| This routine is passed an expression and either AND or IOR. It returns a |
| bitmask indicating which alternatives are mentioned within EXP. */ |
| |
| static alternative_mask |
| 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 && XWINT (exp, 1)) |
| return XWINT (exp, 0); |
| |
| if (code == IOR && !XWINT (exp, 1)) |
| return XWINT (exp, 0); |
| |
| return 0; |
| } |
| else |
| return 0; |
| |
| if (string[1] == 0) |
| return ((alternative_mask) 1) << (string[0] - '0'); |
| return ((alternative_mask) 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 (alternative_mask 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 ATTR is the attribute to which |
| it refers. VALUE is the value of that attribute for the insn |
| corresponding to INSN_CODE and INSN_INDEX. */ |
| |
| static rtx |
| evaluate_eq_attr (rtx exp, class attr_desc *attr, rtx value, |
| int insn_code, int insn_index) |
| { |
| rtx orexp, andexp; |
| rtx right; |
| rtx newexp; |
| int i; |
| |
| while (GET_CODE (value) == ATTR) |
| { |
| struct attr_value *av = NULL; |
| |
| attr = find_attr (&XSTR (value, 0), 0); |
| |
| if (insn_code_values) |
| { |
| struct attr_value_list *iv; |
| for (iv = insn_code_values[insn_code]; iv; iv = iv->next) |
| if (iv->attr == attr) |
| { |
| av = iv->av; |
| break; |
| } |
| } |
| else |
| { |
| struct insn_ent *ie; |
| for (av = attr->first_value; av; av = av->next) |
| for (ie = av->first_insn; ie; ie = ie->next) |
| if (ie->def->insn_code == insn_code) |
| goto got_av; |
| } |
| if (av) |
| { |
| got_av: |
| value = av->value; |
| } |
| } |
| |
| 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: |
| { |
| const char *prefix; |
| char *string, *p; |
| |
| gcc_assert (GET_CODE (exp) == EQ_ATTR); |
| prefix = attr->enum_name ? attr->enum_name : attr->name; |
| string = ACONCAT ((prefix, "_", XSTR (exp, 1), NULL)); |
| for (p = string; *p; p++) |
| *p = TOUPPER (*p); |
| |
| newexp = attr_rtx (EQ, value, |
| attr_rtx (SYMBOL_REF, |
| DEF_ATTR_STRING (string))); |
| break; |
| } |
| |
| case COND: |
| /* We construct an IOR of all the cases for which the |
| requested attribute value is present. Since we start with |
| FALSE, if it is not present, FALSE will be returned. |
| |
| Each case is the AND of the NOT's of the previous conditions with the |
| current condition; in the default case the current condition is TRUE. |
| |
| For each possible COND value, call ourselves recursively. |
| |
| The extra TRUE and FALSE expressions will be eliminated by another |
| call to the simplification routine. */ |
| |
| orexp = false_rtx; |
| andexp = true_rtx; |
| |
| for (i = 0; i < XVECLEN (value, 0); i += 2) |
| { |
| rtx this_cond = simplify_test_exp_in_temp (XVECEXP (value, 0, i), |
| insn_code, insn_index); |
| |
| right = insert_right_side (AND, andexp, this_cond, |
| insn_code, insn_index); |
| right = insert_right_side (AND, right, |
| evaluate_eq_attr (exp, attr, |
| XVECEXP (value, 0, |
| i + 1), |
| insn_code, insn_index), |
| insn_code, insn_index); |
| orexp = insert_right_side (IOR, orexp, right, |
| insn_code, insn_index); |
| |
| /* Add this condition into the AND expression. */ |
| newexp = attr_rtx (NOT, this_cond); |
| andexp = insert_right_side (AND, andexp, newexp, |
| insn_code, insn_index); |
| } |
| |
| /* Handle the default case. */ |
| right = insert_right_side (AND, andexp, |
| evaluate_eq_attr (exp, attr, 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; |
| } |
| |
| /* 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; |
| return x; |
| } |
| |
| /* Returns true if S1 is a subset of S2. */ |
| |
| static bool |
| attr_alt_subset_p (rtx s1, rtx s2) |
| { |
| switch ((XWINT (s1, 1) << 1) | XWINT (s2, 1)) |
| { |
| case (0 << 1) | 0: |
| return !(XWINT (s1, 0) &~ XWINT (s2, 0)); |
| |
| case (0 << 1) | 1: |
| return !(XWINT (s1, 0) & XWINT (s2, 0)); |
| |
| case (1 << 1) | 0: |
| return false; |
| |
| case (1 << 1) | 1: |
| return !(XWINT (s2, 0) &~ XWINT (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 ((XWINT (s1, 1) << 1) | XWINT (s2, 1)) |
| { |
| case (0 << 1) | 0: |
| return !(XWINT (s1, 0) & XWINT (s2, 0)); |
| |
| case (0 << 1) | 1: |
| return !(XWINT (s1, 0) & ~XWINT (s2, 0)); |
| |
| case (1 << 1) | 0: |
| return !(XWINT (s2, 0) &~ XWINT (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) |
| { |
| alternative_mask result; |
| |
| switch ((XWINT (s1, 1) << 1) | XWINT (s2, 1)) |
| { |
| case (0 << 1) | 0: |
| result = XWINT (s1, 0) & XWINT (s2, 0); |
| break; |
| case (0 << 1) | 1: |
| result = XWINT (s1, 0) & ~XWINT (s2, 0); |
| break; |
| case (1 << 1) | 0: |
| result = XWINT (s2, 0) & ~XWINT (s1, 0); |
| break; |
| case (1 << 1) | 1: |
| result = XWINT (s1, 0) | XWINT (s2, 0); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| return attr_rtx (EQ_ATTR_ALT, result, XWINT (s1, 1) & XWINT (s2, 1)); |
| } |
| |
| /* Return EQ_ATTR_ALT expression representing union of S1 and S2. */ |
| |
| static rtx |
| attr_alt_union (rtx s1, rtx s2) |
| { |
| alternative_mask result; |
| |
| switch ((XWINT (s1, 1) << 1) | XWINT (s2, 1)) |
| { |
| case (0 << 1) | 0: |
| result = XWINT (s1, 0) | XWINT (s2, 0); |
| break; |
| case (0 << 1) | 1: |
| result = XWINT (s2, 0) & ~XWINT (s1, 0); |
| break; |
| case (1 << 1) | 0: |
| result = XWINT (s1, 0) & ~XWINT (s2, 0); |
| break; |
| case (1 << 1) | 1: |
| result = XWINT (s1, 0) & XWINT (s2, 0); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| return attr_rtx (EQ_ATTR_ALT, result, XWINT (s1, 1) | XWINT (s2, 1)); |
| } |
| |
| /* Return EQ_ATTR_ALT expression representing complement of S. */ |
| |
| static rtx |
| attr_alt_complement (rtx s) |
| { |
| return attr_rtx (EQ_ATTR_ALT, XWINT (s, 0), |
| ((HOST_WIDE_INT) 1) - XWINT (s, 1)); |
| } |
| |
| /* Return EQ_ATTR_ALT expression representing set containing elements set |
| in E. */ |
| |
| static rtx |
| mk_attr_alt (alternative_mask e) |
| { |
| return attr_rtx (EQ_ATTR_ALT, (HOST_WIDE_INT) e, (HOST_WIDE_INT) 0); |
| } |
| |
| /* 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; |
| class attr_desc *attr; |
| struct attr_value *av; |
| struct insn_ent *ie; |
| struct attr_value_list *iv; |
| alternative_mask 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) |
| std::swap (left, right); |
| |
| 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 |
| && XWINT (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 |
| && XWINT (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); |
| } |
| |
| /* Similarly, |
| convert (ior (and (y) (x)) |
| (and (z) (x))) |
| to (and (ior (y) (z)) |
| (x)) |
| Note that we want the common term to stay at the end. |
| */ |
| |
| else if (GET_CODE (left) == AND && GET_CODE (right) == AND |
| && attr_equal_p (XEXP (left, 1), XEXP (right, 1))) |
| { |
| newexp = attr_rtx (IOR, XEXP (left, 0), XEXP (right, 0)); |
| |
| left = newexp; |
| right = XEXP (right, 1); |
| 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 |
| && !XWINT (left, 1)) |
| || (GET_CODE (left) == EQ_ATTR |
| && XSTR (left, 0) == alternative_name) |
| || GET_CODE (right) == IOR |
| || (GET_CODE (right) == EQ_ATTR_ALT |
| && !XWINT (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 (!XWINT (exp, 0)) |
| return XWINT (exp, 1) ? true_rtx : false_rtx; |
| break; |
| |
| case EQ_ATTR: |
| if (XSTR (exp, 0) == alternative_name) |
| { |
| newexp = mk_attr_alt (((alternative_mask) 1) |
| << atoi (XSTR (exp, 1))); |
| break; |
| } |
| |
| /* Look at the value for this insn code in the specified attribute. |
| We normally can replace this comparison with the condition that |
| would give this insn the values being tested for. */ |
| if (insn_code >= 0 |
| && (attr = find_attr (&XSTR (exp, 0), 0)) != NULL) |
| { |
| rtx x; |
| |
| av = NULL; |
| if (insn_code_values) |
| { |
| for (iv = insn_code_values[insn_code]; iv; iv = iv->next) |
| if (iv->attr == attr) |
| { |
| av = iv->av; |
| break; |
| } |
| } |
| else |
| { |
| for (av = attr->first_value; av; av = av->next) |
| for (ie = av->first_insn; ie; ie = ie->next) |
| if (ie->def->insn_code == insn_code) |
| goto got_av; |
| } |
| |
| if (av) |
| { |
| got_av: |
| x = evaluate_eq_attr (exp, attr, av->value, |
| insn_code, insn_index); |
| x = SIMPLIFY_TEST_EXP (x, insn_code, insn_index); |
| if (attr_rtx_cost (x) < 7) |
| 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; |
| } |
| |
| /* Return 1 if any EQ_ATTR subexpression of P refers to ATTR, |
| otherwise return 0. */ |
| |
| static int |
| tests_attr_p (rtx p, class 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; |
| 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 (tests_attr_p (XEXP (p, i), attr)) |
| return 1; |
| break; |
| |
| case 'E': |
| je = XVECLEN (p, i); |
| for (j = 0; j < je; ++j) |
| if (tests_attr_p (XVECEXP (p, i, j), attr)) |
| return 1; |
| break; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* Calculate a topological sorting of all attributes so that |
| all attributes only depend on attributes in front of it. |
| Place the result in *RET (which is a pointer to an array of |
| attr_desc pointers), and return the size of that array. */ |
| |
| static int |
| get_attr_order (class attr_desc ***ret) |
| { |
| int i, j; |
| int num = 0; |
| class attr_desc *attr; |
| class attr_desc **all, **sorted; |
| char *handled; |
| for (i = 0; i < MAX_ATTRS_INDEX; i++) |
| for (attr = attrs[i]; attr; attr = attr->next) |
| num++; |
| all = XNEWVEC (class attr_desc *, num); |
| sorted = XNEWVEC (class attr_desc *, num); |
| handled = XCNEWVEC (char, num); |
| num = 0; |
| for (i = 0; i < MAX_ATTRS_INDEX; i++) |
| for (attr = attrs[i]; attr; attr = attr->next) |
| all[num++] = attr; |
| |
| j = 0; |
| for (i = 0; i < num; i++) |
| if (all[i]->is_const) |
| handled[i] = 1, sorted[j++] = all[i]; |
| |
| /* We have only few attributes hence we can live with the inner |
| loop being O(n^2), unlike the normal fast variants of topological |
| sorting. */ |
| while (j < num) |
| { |
| for (i = 0; i < num; i++) |
| if (!handled[i]) |
| { |
| /* Let's see if I depends on anything interesting. */ |
| int k; |
| for (k = 0; k < num; k++) |
| if (!handled[k]) |
| { |
| struct attr_value *av; |
| for (av = all[i]->first_value; av; av = av->next) |
| if (av->num_insns != 0) |
| if (tests_attr_p (av->value, all[k])) |
| break; |
| |
| if (av) |
| /* Something in I depends on K. */ |
| break; |
| } |
| if (k == num) |
| { |
| /* Nothing in I depended on anything intersting, so |
| it's done. */ |
| handled[i] = 1; |
| sorted[j++] = all[i]; |
| } |
| } |
| } |
| |
| if (DEBUG) |
| for (j = 0; j < num; j++) |
| { |
| class attr_desc *attr2; |
| struct attr_value *av; |
| |
| attr = sorted[j]; |
| fprintf (stderr, "%s depends on: ", attr->name); |
| for (i = 0; i < MAX_ATTRS_INDEX; ++i) |
| for (attr2 = attrs[i]; attr2; attr2 = attr2->next) |
| if (!attr2->is_const) |
| for (av = attr->first_value; av; av = av->next) |
| if (av->num_insns != 0) |
| if (tests_attr_p (av->value, attr2)) |
| { |
| fprintf (stderr, "%s, ", attr2->name); |
| break; |
| } |
| fprintf (stderr, "\n"); |
| } |
| |
| free (all); |
| *ret = sorted; |
| return num; |
| } |
| |
| /* 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. NUM_INSN_CODES is the number of unique |
| instruction codes. */ |
| |
| static void |
| optimize_attrs (int num_insn_codes) |
| { |
| class attr_desc *attr; |
| struct attr_value *av; |
| struct insn_ent *ie; |
| rtx newexp; |
| int i; |
| struct attr_value_list *ivbuf; |
| struct attr_value_list *iv; |
| class attr_desc **topsort; |
| int topnum; |
| |
| /* 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 *, num_insn_codes + 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); |
| |
| /* Create the chain of insn*attr values such that we see dependend |
| attributes after their dependencies. As we use a stack via the |
| next pointers start from the end of the topological order. */ |
| topnum = get_attr_order (&topsort); |
| for (i = topnum - 1; i >= 0; i--) |
| for (av = topsort[i]->first_value; av; av = av->next) |
| for (ie = av->first_insn; ie; ie = ie->next) |
| { |
| iv->attr = topsort[i]; |
| iv->av = av; |
| iv->ie = ie; |
| iv->next = insn_code_values[ie->def->insn_code]; |
| insn_code_values[ie->def->insn_code] = iv; |
| iv++; |
| } |
| free (topsort); |
| |
| /* Sanity check on num_insn_ents. */ |
| gcc_assert (iv == ivbuf + num_insn_ents); |
| |
| /* Process one insn code at a time. */ |
| for (i = -2; i < num_insn_codes; 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, |
|