| /* Generate code from machine description to compute values of attributes. |
| Copyright (C) 1991, 1993, 1994, 1995, 1996 Free Software Foundation, Inc. |
| Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu) |
| |
| This file is part of GNU CC. |
| |
| GNU CC is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU CC is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU CC; see the file COPYING. If not, write to |
| the Free Software Foundation, 59 Temple Place - Suite 330, |
| Boston, MA 02111-1307, USA. */ |
| |
| /* This program handles insn attributes and the DEFINE_DELAY and |
| DEFINE_FUNCTION_UNIT 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, `insn_extract' 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_FUNCTION_UNIT. 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 and DEFINE_FUNCTION_UNIT |
| 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' (RTX_UNCHANGING_P): This rtx is fully simplified |
| independent of the insn code. |
| `in_struct' (MEM_IN_STRUCT_P): This rtx is fully simplified |
| for the insn code currently being processed (see optimize_attrs). |
| `integrated' (RTX_INTEGRATED_P): This rtx is permanent and unique |
| (see attr_rtx). |
| `volatil' (MEM_VOLATILE_P): During simplify_by_exploding the value of an |
| EQ_ATTR rtx is true if !volatil and false if volatil. */ |
| |
| |
| #include "hconfig.h" |
| /* varargs must always be included after *config.h. */ |
| #ifdef __STDC__ |
| #include <stdarg.h> |
| #else |
| #include <varargs.h> |
| #endif |
| #include "rtl.h" |
| #include "insn-config.h" /* For REGISTER_CONSTRAINTS */ |
| #include <stdio.h> |
| |
| #ifndef VMS |
| #ifndef USG |
| #include <sys/time.h> |
| #include <sys/resource.h> |
| #endif |
| #endif |
| |
| /* We must include obstack.h after <sys/time.h>, to avoid lossage with |
| /usr/include/sys/stdtypes.h on Sun OS 4.x. */ |
| #include "obstack.h" |
| |
| static struct obstack obstack, obstack1, obstack2; |
| struct obstack *rtl_obstack = &obstack; |
| struct obstack *hash_obstack = &obstack1; |
| struct obstack *temp_obstack = &obstack2; |
| |
| #define obstack_chunk_alloc xmalloc |
| #define obstack_chunk_free free |
| |
| /* Define this so we can link with print-rtl.o to get debug_rtx function. */ |
| char **insn_name_ptr = 0; |
| |
| extern void free (); |
| extern rtx read_rtx (); |
| |
| static void fatal (); |
| void fancy_abort (); |
| |
| /* 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 |
| { |
| int insn_code; /* Instruction number. */ |
| int insn_index; /* Expression numer in file, for errors. */ |
| struct insn_def *next; /* Next insn in chain. */ |
| rtx def; /* The DEFINE_... */ |
| 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 |
| { |
| int insn_code; /* Instruction number. */ |
| int insn_index; /* Index of definition in file */ |
| struct insn_ent *next; /* Next in chain. */ |
| }; |
| |
| /* 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. */ |
| int is_numeric; /* Values of this attribute are numeric. */ |
| int negative_ok; /* Allow negative numeric values. */ |
| int unsigned_p; /* Make the output function unsigned int. */ |
| int is_const; /* Attribute value constant for each run. */ |
| int is_special; /* Don't call `write_attr_set'. */ |
| struct attr_value *first_value; /* First value of this attribute. */ |
| struct attr_value *default_val; /* Default value for this attribute. */ |
| }; |
| |
| #define NULL_ATTR (struct attr_desc *) NULL |
| |
| /* A range of values. */ |
| |
| struct range |
| { |
| int min; |
| int max; |
| }; |
| |
| /* 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. */ |
| }; |
| |
| /* Record information about each DEFINE_FUNCTION_UNIT. */ |
| |
| struct function_unit_op |
| { |
| rtx condexp; /* Expression TRUE for applicable insn. */ |
| struct function_unit_op *next; /* Next operation for this function unit. */ |
| int num; /* Ordinal for this operation type in unit. */ |
| int ready; /* Cost until data is ready. */ |
| int issue_delay; /* Cost until unit can accept another insn. */ |
| rtx conflict_exp; /* Expression TRUE for insns incurring issue delay. */ |
| rtx issue_exp; /* Expression computing issue delay. */ |
| }; |
| |
| /* Record information about each function unit mentioned in a |
| DEFINE_FUNCTION_UNIT. */ |
| |
| struct function_unit |
| { |
| char *name; /* Function unit name. */ |
| struct function_unit *next; /* Next function unit. */ |
| int num; /* Ordinal of this unit type. */ |
| int multiplicity; /* Number of units of this type. */ |
| int simultaneity; /* Maximum number of simultaneous insns |
| on this function unit or 0 if unlimited. */ |
| rtx condexp; /* Expression TRUE for insn needing unit. */ |
| int num_opclasses; /* Number of different operation types. */ |
| struct function_unit_op *ops; /* Pointer to first operation type. */ |
| int needs_conflict_function; /* Nonzero if a conflict function required. */ |
| int needs_blockage_function; /* Nonzero if a blockage function required. */ |
| int needs_range_function; /* Nonzero if blockage range function needed.*/ |
| rtx default_cost; /* Conflict cost, if constant. */ |
| struct range issue_delay; /* Range of issue delay values. */ |
| int max_blockage; /* Maximum time an insn blocks the unit. */ |
| }; |
| |
| /* 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; |
| static struct function_unit *units; |
| |
| /* An expression where all the unknown terms are EQ_ATTR tests can be |
| rearranged into a COND provided we can enumerate all possible |
| combinations of the unknown values. The set of combinations become the |
| tests of the COND; the value of the expression given that combination is |
| computed and becomes the corresponding value. To do this, we must be |
| able to enumerate all values for each attribute used in the expression |
| (currently, we give up if we find a numeric attribute). |
| |
| If the set of EQ_ATTR tests used in an expression tests the value of N |
| different attributes, the list of all possible combinations can be made |
| by walking the N-dimensional attribute space defined by those |
| attributes. We record each of these as a struct dimension. |
| |
| The algorithm relies on sharing EQ_ATTR nodes: if two nodes in an |
| expression are the same, the will also have the same address. We find |
| all the EQ_ATTR nodes by marking them MEM_VOLATILE_P. This bit later |
| represents the value of an EQ_ATTR node, so once all nodes are marked, |
| they are also given an initial value of FALSE. |
| |
| We then separate the set of EQ_ATTR nodes into dimensions for each |
| attribute and put them on the VALUES list. Terms are added as needed by |
| `add_values_to_cover' so that all possible values of the attribute are |
| tested. |
| |
| Each dimension also has a current value. This is the node that is |
| currently considered to be TRUE. If this is one of the nodes added by |
| `add_values_to_cover', all the EQ_ATTR tests in the original expression |
| will be FALSE. Otherwise, only the CURRENT_VALUE will be true. |
| |
| NUM_VALUES is simply the length of the VALUES list and is there for |
| convenience. |
| |
| Once the dimensions are created, the algorithm enumerates all possible |
| values and computes the current value of the given expression. */ |
| |
| struct dimension |
| { |
| struct attr_desc *attr; /* Attribute for this dimension. */ |
| rtx values; /* List of attribute values used. */ |
| rtx current_value; /* Position in the list for the TRUE value. */ |
| int num_values; /* Length of the values list. */ |
| }; |
| |
| /* 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_units; |
| static int num_insn_ents; |
| |
| /* Used as operand to `operate_exp': */ |
| |
| enum operator {PLUS_OP, MINUS_OP, POS_MINUS_OP, EQ_OP, OR_OP, MAX_OP, MIN_OP, RANGE_OP}; |
| |
| /* 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; |
| |
| /* If nonzero, assume that the `alternative' attr has this value. |
| This is the hashed, unique string for the numeral |
| whose value is chosen alternative. */ |
| |
| static char *current_alternative_string; |
| |
| /* Used to simplify expressions. */ |
| |
| static rtx true_rtx, false_rtx; |
| |
| /* Used to reduce calls to `strcmp' */ |
| |
| static char *alternative_name; |
| |
| /* Simplify an expression. Only call the routine if there is something to |
| simplify. */ |
| #define SIMPLIFY_TEST_EXP(EXP,INSN_CODE,INSN_INDEX) \ |
| (RTX_UNCHANGING_P (EXP) || MEM_IN_STRUCT_P (EXP) ? (EXP) \ |
| : simplify_test_exp (EXP, INSN_CODE, INSN_INDEX)) |
| |
| /* Simplify (eq_attr ("alternative") ...) |
| when we are working with a particular alternative. */ |
| #define SIMPLIFY_ALTERNATIVE(EXP) \ |
| if (current_alternative_string \ |
| && GET_CODE ((EXP)) == EQ_ATTR \ |
| && XSTR ((EXP), 0) == alternative_name) \ |
| (EXP) = (XSTR ((EXP), 1) == current_alternative_string \ |
| ? true_rtx : false_rtx); |
| |
| /* These are referenced by rtlanal.c and hence need to be defined somewhere. |
| They won't actually be used. */ |
| |
| rtx frame_pointer_rtx, hard_frame_pointer_rtx, stack_pointer_rtx; |
| rtx arg_pointer_rtx; |
| |
| static rtx attr_rtx PVPROTO((enum rtx_code, ...)); |
| #ifdef HAVE_VPRINTF |
| static char *attr_printf PVPROTO((int, char *, ...)); |
| #else |
| static char *attr_printf (); |
| #endif |
| |
| static char *attr_string PROTO((char *, int)); |
| static rtx check_attr_test PROTO((rtx, int)); |
| static rtx check_attr_value PROTO((rtx, struct attr_desc *)); |
| static rtx convert_set_attr_alternative PROTO((rtx, int, int, int)); |
| static rtx convert_set_attr PROTO((rtx, int, int, int)); |
| static void check_defs PROTO((void)); |
| static rtx convert_const_symbol_ref PROTO((rtx, struct attr_desc *)); |
| static rtx make_canonical PROTO((struct attr_desc *, rtx)); |
| static struct attr_value *get_attr_value PROTO((rtx, struct attr_desc *, int)); |
| static rtx copy_rtx_unchanging PROTO((rtx)); |
| static rtx copy_boolean PROTO((rtx)); |
| static void expand_delays PROTO((void)); |
| static rtx operate_exp PROTO((enum operator, rtx, rtx)); |
| static void expand_units PROTO((void)); |
| static rtx simplify_knowing PROTO((rtx, rtx)); |
| static rtx encode_units_mask PROTO((rtx)); |
| static void fill_attr PROTO((struct attr_desc *)); |
| /* dpx2 compiler chokes if we specify the arg types of the args. */ |
| static rtx substitute_address PROTO((rtx, rtx (*) (), rtx (*) ())); |
| static void make_length_attrs PROTO((void)); |
| static rtx identity_fn PROTO((rtx)); |
| static rtx zero_fn PROTO((rtx)); |
| static rtx one_fn PROTO((rtx)); |
| static rtx max_fn PROTO((rtx)); |
| static rtx simplify_cond PROTO((rtx, int, int)); |
| static rtx simplify_by_alternatives PROTO((rtx, int, int)); |
| static rtx simplify_by_exploding PROTO((rtx)); |
| static int find_and_mark_used_attributes PROTO((rtx, rtx *, int *)); |
| static void unmark_used_attributes PROTO((rtx, struct dimension *, int)); |
| static int add_values_to_cover PROTO((struct dimension *)); |
| static int increment_current_value PROTO((struct dimension *, int)); |
| static rtx test_for_current_value PROTO((struct dimension *, int)); |
| static rtx simplify_with_current_value PROTO((rtx, struct dimension *, int)); |
| static rtx simplify_with_current_value_aux PROTO((rtx)); |
| static void clear_struct_flag PROTO((rtx)); |
| static int count_sub_rtxs PROTO((rtx, int)); |
| static void remove_insn_ent PROTO((struct attr_value *, struct insn_ent *)); |
| static void insert_insn_ent PROTO((struct attr_value *, struct insn_ent *)); |
| static rtx insert_right_side PROTO((enum rtx_code, rtx, rtx, int, int)); |
| static rtx make_alternative_compare PROTO((int)); |
| static int compute_alternative_mask PROTO((rtx, enum rtx_code)); |
| static rtx evaluate_eq_attr PROTO((rtx, rtx, int, int)); |
| static rtx simplify_and_tree PROTO((rtx, rtx *, int, int)); |
| static rtx simplify_or_tree PROTO((rtx, rtx *, int, int)); |
| static rtx simplify_test_exp PROTO((rtx, int, int)); |
| static void optimize_attrs PROTO((void)); |
| static void gen_attr PROTO((rtx)); |
| static int count_alternatives PROTO((rtx)); |
| static int compares_alternatives_p PROTO((rtx)); |
| static int contained_in_p PROTO((rtx, rtx)); |
| static void gen_insn PROTO((rtx)); |
| static void gen_delay PROTO((rtx)); |
| static void gen_unit PROTO((rtx)); |
| static void write_test_expr PROTO((rtx, int)); |
| static int max_attr_value PROTO((rtx)); |
| static void walk_attr_value PROTO((rtx)); |
| static void write_attr_get PROTO((struct attr_desc *)); |
| static rtx eliminate_known_true PROTO((rtx, rtx, int, int)); |
| static void write_attr_set PROTO((struct attr_desc *, int, rtx, char *, |
| char *, rtx, int, int)); |
| static void write_attr_case PROTO((struct attr_desc *, struct attr_value *, |
| int, char *, char *, int, rtx)); |
| static void write_attr_valueq PROTO((struct attr_desc *, char *)); |
| static void write_attr_value PROTO((struct attr_desc *, rtx)); |
| static void write_upcase PROTO((char *)); |
| static void write_indent PROTO((int)); |
| static void write_eligible_delay PROTO((char *)); |
| static void write_function_unit_info PROTO((void)); |
| static void write_complex_function PROTO((struct function_unit *, char *, |
| char *)); |
| static int n_comma_elts PROTO((char *)); |
| static char *next_comma_elt PROTO((char **)); |
| static struct attr_desc *find_attr PROTO((char *, int)); |
| static void make_internal_attr PROTO((char *, rtx, int)); |
| static struct attr_value *find_most_used PROTO((struct attr_desc *)); |
| static rtx find_single_value PROTO((struct attr_desc *)); |
| static rtx make_numeric_value PROTO((int)); |
| static void extend_range PROTO((struct range *, int, int)); |
| char *xrealloc PROTO((char *, unsigned)); |
| char *xmalloc PROTO((unsigned)); |
| |
| #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 |
| 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) ((HOST_WIDE_INT) (RTL) & 0777777) |
| |
| /* Add an entry to the hash table for RTL with hash code HASHCODE. */ |
| |
| static void |
| attr_hash_add_rtx (hashcode, rtl) |
| int hashcode; |
| rtx rtl; |
| { |
| register struct attr_hash *h; |
| |
| h = (struct attr_hash *) 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 (hashcode, str) |
| int hashcode; |
| char *str; |
| { |
| register struct attr_hash *h; |
| |
| h = (struct attr_hash *) 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 RTX_INTEGRATED_P flag for these permanent objects. |
| |
| In some cases we cannot uniquify; then we return an ordinary |
| impermanent rtx with RTX_INTEGRATED_P clear. |
| |
| Args are like gen_rtx, but without the mode: |
| |
| rtx attr_rtx (code, [element1, ..., elementn]) */ |
| |
| /*VARARGS1*/ |
| static rtx |
| attr_rtx VPROTO((enum rtx_code code, ...)) |
| { |
| #ifndef __STDC__ |
| enum rtx_code code; |
| #endif |
| va_list p; |
| register int i; /* Array indices... */ |
| register char *fmt; /* Current rtx's format... */ |
| register rtx rt_val; /* RTX to return to caller... */ |
| int hashcode; |
| register struct attr_hash *h; |
| struct obstack *old_obstack = rtl_obstack; |
| |
| VA_START (p, code); |
| |
| #ifndef __STDC__ |
| code = va_arg (p, enum rtx_code); |
| #endif |
| |
| /* 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) == '1') |
| { |
| rtx arg0 = va_arg (p, rtx); |
| |
| /* A permanent object cannot point to impermanent ones. */ |
| if (! RTX_INTEGRATED_P (arg0)) |
| { |
| rt_val = rtx_alloc (code); |
| XEXP (rt_val, 0) = arg0; |
| va_end (p); |
| 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) |
| goto found; |
| |
| if (h == 0) |
| { |
| rtl_obstack = hash_obstack; |
| rt_val = rtx_alloc (code); |
| XEXP (rt_val, 0) = arg0; |
| } |
| } |
| else if (GET_RTX_CLASS (code) == 'c' |
| || GET_RTX_CLASS (code) == '2' |
| || GET_RTX_CLASS (code) == '<') |
| { |
| rtx arg0 = va_arg (p, rtx); |
| rtx arg1 = va_arg (p, rtx); |
| |
| /* A permanent object cannot point to impermanent ones. */ |
| if (! RTX_INTEGRATED_P (arg0) || ! RTX_INTEGRATED_P (arg1)) |
| { |
| rt_val = rtx_alloc (code); |
| XEXP (rt_val, 0) = arg0; |
| XEXP (rt_val, 1) = arg1; |
| va_end (p); |
| 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) |
| goto found; |
| |
| 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 *); |
| |
| if (code == SYMBOL_REF) |
| arg0 = attr_string (arg0, strlen (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) |
| goto found; |
| |
| 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) |
| goto found; |
| |
| 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; |
| if (arg0 == 1) |
| return true_rtx; |
| goto nohash; |
| } |
| else |
| { |
| 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: |
| abort(); |
| } |
| } |
| va_end (p); |
| return rt_val; |
| } |
| |
| rtl_obstack = old_obstack; |
| va_end (p); |
| attr_hash_add_rtx (hashcode, rt_val); |
| RTX_INTEGRATED_P (rt_val) = 1; |
| return rt_val; |
| |
| found: |
| va_end (p); |
| return h->u.rtl; |
| } |
| |
| /* 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]) */ |
| |
| #ifdef HAVE_VPRINTF |
| |
| /*VARARGS2*/ |
| static char * |
| attr_printf VPROTO((register int len, char *fmt, ...)) |
| { |
| #ifndef __STDC__ |
| register int len; |
| char *fmt; |
| #endif |
| va_list p; |
| register char *str; |
| |
| VA_START (p, fmt); |
| |
| #ifndef __STDC__ |
| len = va_arg (p, int); |
| fmt = va_arg (p, char *); |
| #endif |
| |
| /* Print the string into a temporary location. */ |
| str = (char *) alloca (len); |
| vsprintf (str, fmt, p); |
| va_end (p); |
| |
| return attr_string (str, strlen (str)); |
| } |
| |
| #else /* not HAVE_VPRINTF */ |
| |
| static char * |
| attr_printf (len, fmt, arg1, arg2, arg3) |
| int len; |
| char *fmt; |
| char *arg1, *arg2, *arg3; /* also int */ |
| { |
| register char *str; |
| |
| /* Print the string into a temporary location. */ |
| str = (char *) alloca (len); |
| sprintf (str, fmt, arg1, arg2, arg3); |
| |
| return attr_string (str, strlen (str)); |
| } |
| #endif /* not HAVE_VPRINTF */ |
| |
| rtx |
| attr_eq (name, value) |
| char *name, *value; |
| { |
| return attr_rtx (EQ_ATTR, attr_string (name, strlen (name)), |
| attr_string (value, strlen (value))); |
| } |
| |
| char * |
| attr_numeral (n) |
| 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 (str, len) |
| char *str; |
| int len; |
| { |
| register struct attr_hash *h; |
| int hashcode; |
| int i; |
| register 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 = (char *) obstack_alloc (hash_obstack, len + 1); |
| bcopy (str, new_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. */ |
| |
| int |
| attr_equal_p (x, y) |
| rtx x, y; |
| { |
| return (x == y || (! (RTX_INTEGRATED_P (x) && RTX_INTEGRATED_P (y)) |
| && rtx_equal_p (x, y))); |
| } |
| |
| /* Copy an attribute value expression, |
| descending to all depths, but not copying any |
| permanent hashed subexpressions. */ |
| |
| rtx |
| attr_copy_rtx (orig) |
| register rtx orig; |
| { |
| register rtx copy; |
| register int i, j; |
| register RTX_CODE code; |
| register char *format_ptr; |
| |
| /* No need to copy a permanent object. */ |
| if (RTX_INTEGRATED_P (orig)) |
| return orig; |
| |
| code = GET_CODE (orig); |
| |
| switch (code) |
| { |
| case REG: |
| case QUEUED: |
| case CONST_INT: |
| case CONST_DOUBLE: |
| case SYMBOL_REF: |
| case CODE_LABEL: |
| case PC: |
| case CC0: |
| return orig; |
| } |
| |
| copy = rtx_alloc (code); |
| PUT_MODE (copy, GET_MODE (orig)); |
| copy->in_struct = orig->in_struct; |
| copy->volatil = orig->volatil; |
| copy->unchanging = orig->unchanging; |
| copy->integrated = orig->integrated; |
| |
| format_ptr = GET_RTX_FORMAT (GET_CODE (copy)); |
| |
| for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++) |
| { |
| switch (*format_ptr++) |
| { |
| case 'e': |
| XEXP (copy, i) = 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: |
| abort (); |
| } |
| } |
| 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 (exp, is_const) |
| rtx exp; |
| int is_const; |
| { |
| struct attr_desc *attr; |
| struct attr_value *av; |
| 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); |
| |
| 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")) |
| { |
| XSTR (exp, 0) = alternative_name; |
| /* This can't be simplified any further. */ |
| RTX_UNCHANGING_P (exp) = 1; |
| return exp; |
| } |
| else |
| fatal ("Unknown attribute `%s' in EQ_ATTR", XEXP (exp, 0)); |
| } |
| |
| if (is_const && ! attr->is_const) |
| fatal ("Constant expression uses insn attribute `%s' in EQ_ATTR", |
| XEXP (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) |
| RTX_UNCHANGING_P (exp) = 1; |
| |
| if (attr->is_numeric) |
| { |
| for (p = XSTR (exp, 1); *p; p++) |
| if (*p < '0' || *p > '9') |
| fatal ("Attribute `%s' takes only numeric values", |
| XEXP (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", |
| XEXP (exp, 1), XEXP (exp, 0)); |
| } |
| } |
| 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); |
| } |
| 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); |
| XEXP (exp, 1) = check_attr_test (XEXP (exp, 1), is_const); |
| break; |
| |
| case NOT: |
| XEXP (exp, 0) = check_attr_test (XEXP (exp, 0), is_const); |
| break; |
| |
| case MATCH_OPERAND: |
| if (is_const) |
| fatal ("RTL operator \"%s\" not valid in constant attribute test", |
| GET_RTX_NAME (MATCH_OPERAND)); |
| /* These cases can't be simplified. */ |
| RTX_UNCHANGING_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. */ |
| RTX_UNCHANGING_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)); |
| RTX_UNCHANGING_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 (exp, attr) |
| rtx exp; |
| struct attr_desc *attr; |
| { |
| struct attr_value *av; |
| char *p; |
| int i; |
| |
| switch (GET_CODE (exp)) |
| { |
| case CONST_INT: |
| if (attr && ! attr->is_numeric) |
| fatal ("CONST_INT not valid for non-numeric `%s' attribute", |
| attr->name); |
| |
| if (INTVAL (exp) < 0) |
| fatal ("Negative numeric value specified for `%s' attribute", |
| attr->name); |
| |
| break; |
| |
| case CONST_STRING: |
| if (! strcmp (XSTR (exp, 0), "*")) |
| break; |
| |
| if (attr == 0 || attr->is_numeric) |
| { |
| p = XSTR (exp, 0); |
| if (attr && attr->negative_ok && *p == '-') |
| p++; |
| for (; *p; p++) |
| if (*p > '9' || *p < '0') |
| fatal ("Non-numeric value for numeric `%s' attribute", |
| attr ? attr->name : "internal"); |
| 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) |
| fatal ("Unknown value `%s' for `%s' attribute", |
| XSTR (exp, 0), attr ? attr->name : "internal"); |
| |
| break; |
| |
| case IF_THEN_ELSE: |
| XEXP (exp, 0) = check_attr_test (XEXP (exp, 0), |
| attr ? attr->is_const : 0); |
| XEXP (exp, 1) = check_attr_value (XEXP (exp, 1), attr); |
| XEXP (exp, 2) = check_attr_value (XEXP (exp, 2), attr); |
| break; |
| |
| case COND: |
| if (XVECLEN (exp, 0) % 2 != 0) |
| fatal ("First operand of COND must have even length"); |
| |
| 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); |
| 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 SYMBOL_REF: |
| if (attr && attr->is_const) |
| /* A constant SYMBOL_REF is valid as a constant attribute test and |
| is expanded later by make_canonical into a COND. */ |
| return attr_rtx (SYMBOL_REF, XSTR (exp, 0)); |
| /* Otherwise, fall through... */ |
| |
| default: |
| fatal ("Invalid operation `%s' for attribute value", |
| GET_RTX_NAME (GET_CODE (exp))); |
| } |
| |
| 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 (exp, num_alt, insn_code, insn_index) |
| rtx exp; |
| int num_alt; |
| int insn_code, insn_index; |
| { |
| rtx condexp; |
| int i; |
| |
| if (XVECLEN (exp, 1) != num_alt) |
| fatal ("Bad number of entries in SET_ATTR_ALTERNATIVE for insn %d", |
| insn_index); |
| |
| /* 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++) |
| { |
| char *p; |
| p = attr_numeral (i); |
| |
| XVECEXP (condexp, 0, 2 * i) = attr_eq (alternative_name, p); |
| #if 0 |
| /* Sharing this EQ_ATTR rtl causes trouble. */ |
| XVECEXP (condexp, 0, 2 * i) = rtx_alloc (EQ_ATTR); |
| XSTR (XVECEXP (condexp, 0, 2 * i), 0) = alternative_name; |
| XSTR (XVECEXP (condexp, 0, 2 * i), 1) = p; |
| #endif |
| 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 (exp, num_alt, insn_code, insn_index) |
| rtx exp; |
| int num_alt; |
| int insn_code, insn_index; |
| { |
| rtx newexp; |
| 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, num_alt, insn_code, insn_index); |
| } |
| |
| /* 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 () |
| { |
| 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) |
| fatal ("Bad attribute set in pattern %d", id->insn_index); |
| break; |
| |
| case SET_ATTR_ALTERNATIVE: |
| value = convert_set_attr_alternative (value, |
| id->num_alternatives, |
| id->insn_code, |
| id->insn_index); |
| break; |
| |
| case SET_ATTR: |
| value = convert_set_attr (value, id->num_alternatives, |
| id->insn_code, id->insn_index); |
| break; |
| |
| default: |
| fatal ("Invalid attribute code `%s' for pattern %d", |
| GET_RTX_NAME (GET_CODE (value)), id->insn_index); |
| } |
| |
| if ((attr = find_attr (XSTR (XEXP (value, 0), 0), 0)) == NULL) |
| fatal ("Unknown attribute `%s' for pattern number %d", |
| XSTR (XEXP (value, 0), 0), id->insn_index); |
| |
| XVECEXP (id->def, id->vec_idx, i) = value; |
| XEXP (value, 1) = check_attr_value (XEXP (value, 1), attr); |
| } |
| } |
| } |
| |
| /* Given a constant SYMBOL_REF expression, convert to a COND that |
| explicitly tests each enumerated value. */ |
| |
| static rtx |
| convert_const_symbol_ref (exp, attr) |
| rtx exp; |
| struct attr_desc *attr; |
| { |
| rtx condexp; |
| struct attr_value *av; |
| int i; |
| int num_alt = 0; |
| |
| for (av = attr->first_value; av; av = av->next) |
| num_alt++; |
| |
| /* Make a COND with all tests but the last, and in the original order. |
| Select the last value via the default. Note that the attr values |
| are constructed in reverse order. */ |
| |
| condexp = rtx_alloc (COND); |
| XVEC (condexp, 0) = rtvec_alloc ((num_alt - 1) * 2); |
| av = attr->first_value; |
| XEXP (condexp, 1) = av->value; |
| |
| for (i = num_alt - 2; av = av->next, i >= 0; i--) |
| { |
| char *p, *string; |
| rtx value; |
| |
| string = p = (char *) oballoc (2 |
| + strlen (attr->name) |
| + strlen (XSTR (av->value, 0))); |
| strcpy (p, attr->name); |
| strcat (p, "_"); |
| strcat (p, XSTR (av->value, 0)); |
| for (; *p != '\0'; p++) |
| if (*p >= 'a' && *p <= 'z') |
| *p -= 'a' - 'A'; |
| |
| value = attr_rtx (SYMBOL_REF, string); |
| RTX_UNCHANGING_P (value) = 1; |
| |
| XVECEXP (condexp, 0, 2 * i) = attr_rtx (EQ, exp, value); |
| |
| XVECEXP (condexp, 0, 2 * i + 1) = av->value; |
| } |
| |
| return condexp; |
| } |
| |
| /* 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 (attr, exp) |
| 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; |
| } |
| |
| break; |
| |
| case SYMBOL_REF: |
| if (!attr->is_const || RTX_UNCHANGING_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. */ |
| RTX_UNCHANGING_P (exp) = 1; |
| exp = convert_const_symbol_ref (exp, attr); |
| RTX_UNCHANGING_P (exp) = 1; |
| exp = check_attr_value (exp, attr); |
| /* Goto COND case since this is now a COND. Note that while the |
| new expression is rescanned, all symbol_ref notes are mared as |
| unchanging. */ |
| goto cond; |
| |
| 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: |
| 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; |
| } |
| } |
| |
| return exp; |
| } |
| |
| static rtx |
| copy_boolean (exp) |
| 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))); |
| 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 (value, attr, insn_code) |
| 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->insn_code])) |
| return av; |
| |
| av = (struct attr_value *) 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 () |
| { |
| 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", condexp, 0); |
| |
| /* 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", condexp, 1); |
| } |
| |
| /* 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, 1); |
| |
| 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, 1); |
| } |
| |
| 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, 1); |
| } |
| } |
| } |
| } |
| |
| /* This function is given a left and right side expression and an operator. |
| Each side is a conditional expression, each alternative of which has a |
| numerical value. The function returns another conditional expression |
| which, for every possible set of condition values, returns a value that is |
| the operator applied to the values of the two sides. |
| |
| Since this is called early, it must also support IF_THEN_ELSE. */ |
| |
| static rtx |
| operate_exp (op, left, right) |
| enum operator op; |
| rtx left, right; |
| { |
| int left_value, right_value; |
| rtx newexp; |
| int i; |
| |
| /* If left is a string, apply operator to it and the right side. */ |
| if (GET_CODE (left) == CONST_STRING) |
| { |
| /* If right is also a string, just perform the operation. */ |
| if (GET_CODE (right) == CONST_STRING) |
| { |
| left_value = atoi (XSTR (left, 0)); |
| right_value = atoi (XSTR (right, 0)); |
| switch (op) |
| { |
| case PLUS_OP: |
| i = left_value + right_value; |
| break; |
| |
| case MINUS_OP: |
| i = left_value - right_value; |
| break; |
| |
| case POS_MINUS_OP: /* The positive part of LEFT - RIGHT. */ |
| if (left_value > right_value) |
| i = left_value - right_value; |
| else |
| i = 0; |
| break; |
| |
| case OR_OP: |
| i = left_value | right_value; |
| break; |
| |
| case EQ_OP: |
| i = left_value == right_value; |
| break; |
| |
| case RANGE_OP: |
| i = (left_value << (HOST_BITS_PER_INT / 2)) | right_value; |
| break; |
| |
| case MAX_OP: |
| if (left_value > right_value) |
| i = left_value; |
| else |
| i = right_value; |
| break; |
| |
| case MIN_OP: |
| if (left_value < right_value) |
| i = left_value; |
| else |
| i = right_value; |
| break; |
| |
| default: |
| abort (); |
| } |
| |
| return make_numeric_value (i); |
| } |
| else if (GET_CODE (right) == IF_THEN_ELSE) |
| { |
| /* Apply recursively to all values within. */ |
| rtx newleft = operate_exp (op, left, XEXP (right, 1)); |
| rtx newright = operate_exp (op, left, XEXP (right, 2)); |
| if (rtx_equal_p (newleft, newright)) |
| return newleft; |
| return attr_rtx (IF_THEN_ELSE, XEXP (right, 0), newleft, newright); |
| } |
| else if (GET_CODE (right) == COND) |
| { |
| int allsame = 1; |
| rtx defval; |
| |
| newexp = rtx_alloc (COND); |
| XVEC (newexp, 0) = rtvec_alloc (XVECLEN (right, 0)); |
| defval = XEXP (newexp, 1) = operate_exp (op, left, XEXP (right, 1)); |
| |
| for (i = 0; i < XVECLEN (right, 0); i += 2) |
| { |
| XVECEXP (newexp, 0, i) = XVECEXP (right, 0, i); |
| XVECEXP (newexp, 0, i + 1) |
| = operate_exp (op, left, XVECEXP (right, 0, i + 1)); |
| if (! rtx_equal_p (XVECEXP (newexp, 0, i + 1), |
| defval)) |
| allsame = 0; |
| } |
| |
| /* If the resulting cond is trivial (all alternatives |
| give the same value), optimize it away. */ |
| if (allsame) |
| { |
| obstack_free (rtl_obstack, newexp); |
| return operate_exp (op, left, XEXP (right, 1)); |
| } |
| |
| /* If the result is the same as the RIGHT operand, |
| just use that. */ |
| if (rtx_equal_p (newexp, right)) |
| { |
| obstack_free (rtl_obstack, newexp); |
| return right; |
| } |
| |
| return newexp; |
| } |
| else |
| fatal ("Badly formed attribute value"); |
| } |
| |
| /* Otherwise, do recursion the other way. */ |
| else if (GET_CODE (left) == IF_THEN_ELSE) |
| { |
| rtx newleft = operate_exp (op, XEXP (left, 1), right); |
| rtx newright = operate_exp (op, XEXP (left, 2), right); |
| if (rtx_equal_p (newleft, newright)) |
| return newleft; |
| return attr_rtx (IF_THEN_ELSE, XEXP (left, 0), newleft, newright); |
| } |
| else if (GET_CODE (left) == COND) |
| { |
| int allsame = 1; |
| rtx defval; |
| |
| newexp = rtx_alloc (COND); |
| XVEC (newexp, 0) = rtvec_alloc (XVECLEN (left, 0)); |
| defval = XEXP (newexp, 1) = operate_exp (op, XEXP (left, 1), right); |
| |
| for (i = 0; i < XVECLEN (left, 0); i += 2) |
| { |
| XVECEXP (newexp, 0, i) = XVECEXP (left, 0, i); |
| XVECEXP (newexp, 0, i + 1) |
| = operate_exp (op, XVECEXP (left, 0, i + 1), right); |
| if (! rtx_equal_p (XVECEXP (newexp, 0, i + 1), |
| defval)) |
| allsame = 0; |
| } |
| |
| /* If the cond is trivial (all alternatives give the same value), |
| optimize it away. */ |
| if (allsame) |
| { |
| obstack_free (rtl_obstack, newexp); |
| return operate_exp (op, XEXP (left, 1), right); |
| } |
| |
| /* If the result is the same as the LEFT operand, |
| just use that. */ |
| if (rtx_equal_p (newexp, left)) |
| { |
| obstack_free (rtl_obstack, newexp); |
| return left; |
| } |
| |
| return newexp; |
| } |
| |
| else |
| fatal ("Badly formed attribute value."); |
| /* NOTREACHED */ |
| return NULL; |
| } |
| |
| /* Once all attributes and DEFINE_FUNCTION_UNITs have been read, we |
| construct a number of attributes. |
| |
| The first produces a function `function_units_used' which is given an |
| insn and produces an encoding showing which function units are required |
| for the execution of that insn. If the value is non-negative, the insn |
| uses that unit; otherwise, the value is a one's compliment mask of units |
| used. |
| |
| The second produces a function `result_ready_cost' which is used to |
| determine the time that the result of an insn will be ready and hence |
| a worst-case schedule. |
| |
| Both of these produce quite complex expressions which are then set as the |
| default value of internal attributes. Normal attribute simplification |
| should produce reasonable expressions. |
| |
| For each unit, a `<name>_unit_ready_cost' function will take an |
| insn and give the delay until that unit will be ready with the result |
| and a `<name>_unit_conflict_cost' function is given an insn already |
| executing on the unit and a candidate to execute and will give the |
| cost from the time the executing insn started until the candidate |
| can start (ignore limitations on the number of simultaneous insns). |
| |
| For each unit, a `<name>_unit_blockage' function is given an insn |
| already executing on the unit and a candidate to execute and will |
| give the delay incurred due to function unit conflicts. The range of |
| blockage cost values for a given executing insn is given by the |
| `<name>_unit_blockage_range' function. These values are encoded in |
| an int where the upper half gives the minimum value and the lower |
| half gives the maximum value. */ |
| |
| static void |
| expand_units () |
| { |
| struct function_unit *unit, **unit_num; |
| struct function_unit_op *op, **op_array, ***unit_ops; |
| rtx unitsmask; |
| rtx readycost; |
| rtx newexp; |
| char *str; |
| int i, j, u, num, nvalues; |
| |
| /* Rebuild the condition for the unit to share the RTL expressions. |
| Sharing is required by simplify_by_exploding. Build the issue delay |
| expressions. Validate the expressions we were given for the conditions |
| and conflict vector. Then make attributes for use in the conflict |
| function. */ |
| |
| for (unit = units; unit; unit = unit->next) |
| { |
| unit->condexp = check_attr_test (unit->condexp, 0); |
| |
| for (op = unit->ops; op; op = op->next) |
| { |
| rtx issue_delay = make_numeric_value (op->issue_delay); |
| rtx issue_exp = issue_delay; |
| |
| /* Build, validate, and simplify the issue delay expression. */ |
| if (op->conflict_exp != true_rtx) |
| issue_exp = attr_rtx (IF_THEN_ELSE, op->conflict_exp, |
| issue_exp, make_numeric_value (0)); |
| issue_exp = check_attr_value (make_canonical (NULL_ATTR, |
| issue_exp), |
| NULL_ATTR); |
| issue_exp = simplify_knowing (issue_exp, unit->condexp); |
| op->issue_exp = issue_exp; |
| |
| /* Make an attribute for use in the conflict function if needed. */ |
| unit->needs_conflict_function = (unit->issue_delay.min |
| != unit->issue_delay.max); |
| if (unit->needs_conflict_function) |
| { |
| str = attr_printf (strlen (unit->name) + sizeof ("*_cost_") + MAX_DIGITS, |
| "*%s_cost_%d", unit->name, op->num); |
| make_internal_attr (str, issue_exp, 1); |
| } |
| |
| /* Validate the condition. */ |
| op->condexp = check_attr_test (op->condexp, 0); |
| } |
| } |
| |
| /* Compute the mask of function units used. Initially, the unitsmask is |
| zero. Set up a conditional to compute each unit's contribution. */ |
| unitsmask = make_numeric_value (0); |
| newexp = rtx_alloc (IF_THEN_ELSE); |
| XEXP (newexp, 2) = make_numeric_value (0); |
| |
| /* Merge each function unit into the unit mask attributes. */ |
| for (unit = units; unit; unit = unit->next) |
| { |
| XEXP (newexp, 0) = unit->condexp; |
| XEXP (newexp, 1) = make_numeric_value (1 << unit->num); |
| unitsmask = operate_exp (OR_OP, unitsmask, newexp); |
| } |
| |
| /* Simplify the unit mask expression, encode it, and make an attribute |
| for the function_units_used function. */ |
| unitsmask = simplify_by_exploding (unitsmask); |
| unitsmask = encode_units_mask (unitsmask); |
| make_internal_attr ("*function_units_used", unitsmask, 2); |
| |
| /* Create an array of ops for each unit. Add an extra unit for the |
| result_ready_cost function that has the ops of all other units. */ |
| unit_ops = (struct function_unit_op ***) |
| alloca ((num_units + 1) * sizeof (struct function_unit_op **)); |
| unit_num = (struct function_unit **) |
| alloca ((num_units + 1) * sizeof (struct function_unit *)); |
| |
| unit_num[num_units] = unit = (struct function_unit *) |
| alloca (sizeof (struct function_unit)); |
| unit->num = num_units; |
| unit->num_opclasses = 0; |
| |
| for (unit = units; unit; unit = unit->next) |
| { |
| unit_num[num_units]->num_opclasses += unit->num_opclasses; |
| unit_num[unit->num] = unit; |
| unit_ops[unit->num] = op_array = (struct function_unit_op **) |
| alloca (unit->num_opclasses * sizeof (struct function_unit_op *)); |
| |
| for (op = unit->ops; op; op = op->next) |
| op_array[op->num] = op; |
| } |
| |
| /* Compose the array of ops for the extra unit. */ |
| unit_ops[num_units] = op_array = (struct function_unit_op **) |
| alloca (unit_num[num_units]->num_opclasses |
| * sizeof (struct function_unit_op *)); |
| |
| for (unit = units, i = 0; unit; i += unit->num_opclasses, unit = unit->next) |
| bcopy ((char *) unit_ops[unit->num], (char *) &op_array[i], |
| unit->num_opclasses * sizeof (struct function_unit_op *)); |
| |
| /* Compute the ready cost function for each unit by computing the |
| condition for each non-default value. */ |
| for (u = 0; u <= num_units; u++) |
| { |
| rtx orexp; |
| int value; |
| |
| unit = unit_num[u]; |
| op_array = unit_ops[unit->num]; |
| num = unit->num_opclasses; |
| |
| /* Sort the array of ops into increasing ready cost order. */ |
| for (i = 0; i < num; i++) |
| for (j = num - 1; j > i; j--) |
| if (op_array[j-1]->ready < op_array[j]->ready) |
| { |
| op = op_array[j]; |
| op_array[j] = op_array[j-1]; |
| op_array[j-1] = op; |
| } |
| |
| /* Determine how many distinct non-default ready cost values there |
| are. We use a default ready cost value of 1. */ |
| nvalues = 0; value = 1; |
| for (i = num - 1; i >= 0; i--) |
| if (op_array[i]->ready > value) |
| { |
| value = op_array[i]->ready; |
| nvalues++; |
| } |
| |
| if (nvalues == 0) |
| readycost = make_numeric_value (1); |
| else |
| { |
| /* Construct the ready cost expression as a COND of each value from |
| the largest to the smallest. */ |
| readycost = rtx_alloc (COND); |
| XVEC (readycost, 0) = rtvec_alloc (nvalues * 2); |
| XEXP (readycost, 1) = make_numeric_value (1); |
| |
| nvalues = 0; orexp = false_rtx; value = op_array[0]->ready; |
| for (i = 0; i < num; i++) |
| { |
| op = op_array[i]; |
| if (op->ready <= 1) |
| break; |
| else if (op->ready == value) |
| orexp = insert_right_side (IOR, orexp, op->condexp, -2, -2); |
| else |
| { |
| XVECEXP (readycost, 0, nvalues * 2) = orexp; |
| XVECEXP (readycost, 0, nvalues * 2 + 1) |
| = make_numeric_value (value); |
| nvalues++; |
| value = op->ready; |
| orexp = op->condexp; |
| } |
| } |
| XVECEXP (readycost, 0, nvalues * 2) = orexp; |
| XVECEXP (readycost, 0, nvalues * 2 + 1) = make_numeric_value (value); |
| } |
| |
| if (u < num_units) |
| { |
| rtx max_blockage = 0, min_blockage = 0; |
| |
| /* Simplify the readycost expression by only considering insns |
| that use the unit. */ |
| readycost = simplify_knowing (readycost, unit->condexp); |
| |
| /* Determine the blockage cost the executing insn (E) given |
| the candidate insn (C). This is the maximum of the issue |
| delay, the pipeline delay, and the simultaneity constraint. |
| Each function_unit_op represents the characteristics of the |
| candidate insn, so in the expressions below, C is a known |
| term and E is an unknown term. |
| |
| We compute the blockage cost for each E for every possible C. |
| Thus OP represents E, and READYCOST is a list of values for |
| every possible C. |
| |
| The issue delay function for C is op->issue_exp and is used to |
| write the `<name>_unit_conflict_cost' function. Symbolicly |
| this is "ISSUE-DELAY (E,C)". |
| |
| The pipeline delay results form the FIFO constraint on the |
| function unit and is "READY-COST (E) + 1 - READY-COST (C)". |
| |
| The simultaneity constraint is based on how long it takes to |
| fill the unit given the minimum issue delay. FILL-TIME is the |
| constant "MIN (ISSUE-DELAY (*,*)) * (SIMULTANEITY - 1)", and |
| the simultaneity constraint is "READY-COST (E) - FILL-TIME" |
| if SIMULTANEITY is non-zero and zero otherwise. |
| |
| Thus, BLOCKAGE (E,C) when SIMULTANEITY is zero is |
| |
| MAX (ISSUE-DELAY (E,C), |
| READY-COST (E) - (READY-COST (C) - 1)) |
| |
| and otherwise |
| |
| MAX (ISSUE-DELAY (E,C), |
| READY-COST (E) - (READY-COST (C) - 1), |
| READY-COST (E) - FILL-TIME) |
| |
| The `<name>_unit_blockage' function is computed by determining |
| this value for each candidate insn. As these values are |
| computed, we also compute the upper and lower bounds for |
| BLOCKAGE (E,*). These are combined to form the function |
| `<name>_unit_blockage_range'. Finally, the maximum blockage |
| cost, MAX (BLOCKAGE (*,*)), is computed. */ |
| |
| for (op = unit->ops; op; op = op->next) |
| { |
| rtx blockage = operate_exp (POS_MINUS_OP, readycost, |
| make_numeric_value (1)); |
| |
| if (unit->simultaneity != 0) |
| { |
| rtx filltime = make_numeric_value ((unit->simultaneity - 1) |
| * unit->issue_delay.min); |
| blockage = operate_exp (MIN_OP, blockage, filltime); |
| } |
| |
| blockage = operate_exp (POS_MINUS_OP, |
| make_numeric_value (op->ready), |
| blockage); |
| |
| blockage = operate_exp (MAX_OP, blockage, op->issue_exp); |
| blockage = simplify_knowing (blockage, unit->condexp); |
| |
| /* Add this op's contribution to MAX (BLOCKAGE (E,*)) and |
| MIN (BLOCKAGE (E,*)). */ |
| if (max_blockage == 0) |
| max_blockage = min_blockage = blockage; |
| else |
| { |
| max_blockage |
| = simplify_knowing (operate_exp (MAX_OP, max_blockage, |
| blockage), |
| unit->condexp); |
| min_blockage |
| = simplify_knowing (operate_exp (MIN_OP, min_blockage, |
| blockage), |
| unit->condexp); |
| } |
| |
| /* Make an attribute for use in the blockage function. */ |
| str = attr_printf (strlen (unit->name) + sizeof ("*_block_") + MAX_DIGITS, |
| "*%s_block_%d", unit->name, op->num); |
| make_internal_attr (str, blockage, 1); |
| } |
| |
| /* Record MAX (BLOCKAGE (*,*)). */ |
| unit->max_blockage = max_attr_value (max_blockage); |
| |
| /* See if the upper and lower bounds of BLOCKAGE (E,*) are the |
| same. If so, the blockage function carries no additional |
| information and is not written. */ |
| newexp = operate_exp (EQ_OP, max_blockage, min_blockage); |
| newexp = simplify_knowing (newexp, unit->condexp); |
| unit->needs_blockage_function |
| = (GET_CODE (newexp) != CONST_STRING |
| || atoi (XSTR (newexp, 0)) != 1); |
| |
| /* If the all values of BLOCKAGE (E,C) have the same value, |
| neither blockage function is written. */ |
| unit->needs_range_function |
| = (unit->needs_blockage_function |
| || GET_CODE (max_blockage) != CONST_STRING); |
| |
| if (unit->needs_range_function) |
| { |
| /* Compute the blockage range function and make an attribute |
| for writing it's value. */ |
| newexp = operate_exp (RANGE_OP, min_blockage, max_blockage); |
| newexp = simplify_knowing (newexp, unit->condexp); |
| |
| str = attr_printf (strlen (unit->name) + sizeof ("*_unit_blockage_range"), |
| "*%s_unit_blockage_range", unit->name); |
| make_internal_attr (str, newexp, 4); |
| } |
| |
| str = attr_printf (strlen (unit->name) + sizeof ("*_unit_ready_cost"), |
| "*%s_unit_ready_cost", unit->name); |
| } |
| else |
| str = "*result_ready_cost"; |
| |
| /* Make an attribute for the ready_cost function. Simplifying |
| further with simplify_by_exploding doesn't win. */ |
| make_internal_attr (str, readycost, 0); |
| } |
| |
| /* For each unit that requires a conflict cost function, make an attribute |
| that maps insns to the operation number. */ |
| for (unit = units; unit; unit = unit->next) |
| { |
| rtx caseexp; |
| |
| if (! unit->needs_conflict_function |
| && ! unit->needs_blockage_function) |
| continue; |
| |
| caseexp = rtx_alloc (COND); |
| XVEC (caseexp, 0) = rtvec_alloc ((unit->num_opclasses - 1) * 2); |
| |
| for (op = unit->ops; op; op = op->next) |
| { |
| /* Make our adjustment to the COND being computed. If we are the |
| last operation class, place our values into the default of the |
| COND. */ |
| if (op->num == unit->num_opclasses - 1) |
| { |
| XEXP (caseexp, 1) = make_numeric_value (op->num); |
| } |
| else |
| { |
| XVECEXP (caseexp, 0, op->num * 2) = op->condexp; |
| XVECEXP (caseexp, 0, op->num * 2 + 1) |
| = make_numeric_value (op->num); |
| } |
| } |
| |
| /* Simplifying caseexp with simplify_by_exploding doesn't win. */ |
| str = attr_printf (strlen (unit->name) + sizeof ("*_cases"), |
| "*%s_cases", unit->name); |
| make_internal_attr (str, caseexp, 1); |
| } |
| } |
| |
| /* Simplify EXP given KNOWN_TRUE. */ |
| |
| static rtx |
| simplify_knowing (exp, known_true) |
| rtx exp, known_true; |
| { |
| if (GET_CODE (exp) != CONST_STRING) |
| { |
| exp = attr_rtx (IF_THEN_ELSE, known_true, exp, |
| make_numeric_value (max_attr_value (exp))); |
| exp = simplify_by_exploding (exp); |
| } |
| return exp; |
| } |
| |
| /* Translate the CONST_STRING expressions in X to change the encoding of |
| value. On input, the value is a bitmask with a one bit for each unit |
| used; on output, the value is the unit number (zero based) if one |
| and only one unit is used or the one's compliment of the bitmask. */ |
| |
| static rtx |
| encode_units_mask (x) |
| rtx x; |
| { |
| register int i; |
| register int j; |
| register enum rtx_code code; |
| register char *fmt; |
| |
| code = GET_CODE (x); |
| |
| switch (code) |
| { |
| case CONST_STRING: |
| i = atoi (XSTR (x, 0)); |
| if (i < 0) |
| abort (); /* The sign bit encodes a one's compliment mask. */ |
| else if (i != 0 && i == (i & -i)) |
| /* Only one bit is set, so yield that unit number. */ |
| for (j = 0; (i >>= 1) != 0; j++) |
| ; |
| else |
| j = ~i; |
| return attr_rtx (CONST_STRING, attr_printf (MAX_DIGITS, "%d", j)); |
| |
| case REG: |
| case QUEUED: |
| case CONST_INT: |
| case CONST_DOUBLE: |
| case SYMBOL_REF: |
| case CODE_LABEL: |
| case PC: |
| case CC0: |
| case EQ_ATTR: |
| return x; |
| } |
| |
| /* 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++) |
| XVECEXP (x, i, j) = encode_units_mask (XVECEXP (x, i, j)); |
| break; |
| |
| case 'e': |
| XEXP (x, i) = encode_units_mask (XEXP (x, i)); |
| break; |
| } |
| } |
| return x; |
| } |
| |
| /* 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 (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 (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 = (struct insn_ent *) oballoc (sizeof (struct insn_ent)); |
| ie->insn_code = id->insn_code; |
| ie->insn_index = id->insn_code; |
| 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 (exp, no_address_fn, address_fn) |
| rtx exp; |
| rtx (*no_address_fn) (); |
| rtx (*address_fn) (); |
| { |
| 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 () |
| { |
| static char *new_names[] = {"*insn_default_length", |
| "*insn_variable_length_p", |
| "*insn_current_length"}; |
| static rtx (*no_address_fn[]) PROTO((rtx)) = {identity_fn, zero_fn, zero_fn}; |
| static rtx (*address_fn[]) PROTO((rtx)) = {max_fn, one_fn, identity_fn}; |
| int i; |
| struct attr_desc *length_attr, *new_attr; |
| struct attr_value *av, *new_av; |
| struct insn_ent *ie, *new_ie; |
| |
| /* See if length attribute is defined. If so, it must be numeric. Make |
| it special so we don't output anything for it. */ |
| length_attr = find_attr ("length", 0); |
| if (length_attr == 0) |
| return; |
| |
| if (! length_attr->is_numeric) |
| fatal ("length attribute must be numeric."); |
| |
| length_attr->is_const = 0; |
| length_attr->is_special = 1; |
| |
| /* Make each new attribute, in turn. */ |
| for (i = 0; i < sizeof new_names / sizeof new_names[0]; i++) |
| { |
| make_internal_attr (new_names[i], |
| substitute_address (length_attr->default_val->value, |
| no_address_fn[i], address_fn[i]), |
| 0); |
| 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->insn_code); |
| new_ie = (struct insn_ent *) oballoc (sizeof (struct insn_ent)); |
| new_ie->insn_code = ie->insn_code; |
| new_ie->insn_index = ie->insn_index; |
| insert_insn_ent (new_av, new_ie); |
| } |
| } |
| } |
| |
| /* Utility functions called from above routine. */ |
| |
| static rtx |
| identity_fn (exp) |
| rtx exp; |
| { |
| return exp; |
| } |
| |
| static rtx |
| zero_fn (exp) |
| rtx exp; |
| { |
| return make_numeric_value (0); |
| } |
| |
| static rtx |
| one_fn (exp) |
| rtx exp; |
| { |
| return make_numeric_value (1); |
| } |
| |
| static rtx |
| max_fn (exp) |
| rtx exp; |
| { |
| return make_numeric_value (max_attr_value (exp)); |
| } |
| |
| /* 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 (exp, insn_code, insn_index) |
| rtx exp; |
| int insn_code, 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); |
| rtunion *tests = (rtunion *) alloca (len * sizeof (rtunion)); |
| int allsame = 1; |
| char *first_spacer; |
| |
| /* This lets us free all storage allocated below, if appropriate. */ |
| first_spacer = (char *) obstack_finish (rtl_obstack); |
| |
| bcopy ((char *) XVEC (exp, 0)->elem, (char *) tests, len * sizeof (rtunion)); |
| |
| /* 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 (tests[i].rtx, insn_code, insn_index); |
| tests[i].rtx = newtest; |
| |
| newval = tests[i + 1].rtx; |
| /* 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].rtx; |
| 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].rtx = tests[j + 2].rtx; |
| len -= 2; |
| } |
| |
| else if (i > 0 && attr_equal_p (newval, tests[i - 1].rtx)) |
| { |
| /* If this value and the value for the prev test are the same, |
| merge the tests. */ |
| |
| tests[i - 2].rtx |
| = insert_right_side (IOR, tests[i - 2].rtx, newtest, |
| insn_code, insn_index); |
| |
| /* Delete this test/value. */ |
| for (j = i; j < len - 2; j++) |
| tests[j].rtx = tests[j + 2].rtx; |
| len -= 2; |
| } |
| |
| else |
| tests[i + 1].rtx = 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].rtx, 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].rtx, XVECEXP (exp, 0, i))) |
| { |
| allsame = 0; |
| break; |
| } |
| |
| if (len == 0) |
| { |
| obstack_free (rtl_obstack, first_spacer); |
| if (GET_CODE (defval) == COND) |
| return simplify_cond (defval, insn_code, insn_index); |
| return defval; |
| } |
| else if (allsame) |
| { |
| obstack_free (rtl_obstack, first_spacer); |
| return exp; |
| } |
| else |
| { |
| rtx newexp = rtx_alloc (COND); |
| |
| XVEC (newexp, 0) = rtvec_alloc (len); |
| bcopy ((char *) tests, (char *) XVEC (newexp, 0)->elem, |
| len * sizeof (rtunion)); |
| XEXP (newexp, 1) = new_defval; |
| return newexp; |
| } |
| } |
| |
| /* Remove an insn entry from an attribute value. */ |
| |
| static void |
| remove_insn_ent (av, ie) |
| 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->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 (av, ie) |
| struct attr_value *av; |
| struct insn_ent *ie; |
| { |
| ie->next = av->first_insn; |
| av->first_insn = ie; |
| av->num_insns++; |
| if (ie->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 (code, exp, term, insn_code, insn_index) |
| enum rtx_code code; |
| rtx exp; |
| rtx term; |
| int insn_code, 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 (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 (exp, code) |
| rtx exp; |
| enum rtx_code code; |
| { |
| 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 |
| 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 (mask) |
| int mask; |
| { |
| rtx newexp; |
| int i; |
| |
| /* Find the bit. */ |
| for (i = 0; (mask & (1 << i)) == 0; i++) |
| ; |
| |
| newexp = attr_rtx (EQ_ATTR, alternative_name, attr_numeral (i)); |
| RTX_UNCHANGING_P (newexp) = 1; |
| |
| return newexp; |
| } |
| |
| /* 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 (exp, value, insn_code, insn_index) |
| rtx exp; |
| rtx value; |
| int insn_code, insn_index; |
| { |
| rtx orexp, andexp; |
| rtx right; |
| rtx newexp; |
| int i; |
| |
| if (GET_CODE (value) == CONST_STRING) |
| { |
| if (! strcmp (XSTR (value, 0), XSTR (exp, 1))) |
| newexp = true_rtx; |
| else |
| newexp = false_rtx; |
| } |
| else if (GET_CODE (value) == 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; |
| |
| if (current_alternative_string) |
| clear_struct_flag (value); |
| |
| for (i = 0; i < XVECLEN (value, 0); i += 2) |
| { |
| rtx this = SIMPLIFY_TEST_EXP (XVECEXP (value, 0, i), |
| insn_code, insn_index); |
| |
| SIMPLIFY_ALTERNATIVE (this); |
| |
| 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); |
| } |
| else |
| abort (); |
| |
| /* If uses an address, must return original expression. But set the |
| RTX_UNCHANGING_P bit so we don't try to simplify it again. */ |
| |
| address_used = 0; |
| walk_attr_value (newexp); |
| |
| if (address_used) |
| { |
| /* This had `&& current_alternative_string', which seems to be wrong. */ |
| if (! RTX_UNCHANGING_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 (exp, pterm, insn_code, insn_index) |
| rtx exp; |
| rtx *pterm; |
| int insn_code, 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 (GET_CODE (exp), left, right); |
| |
| exp = SIMPLIFY_TEST_EXP (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 (GET_CODE (exp), left, right); |
| |
| exp = SIMPLIFY_TEST_EXP (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 && GET_CODE (*pterm) == EQ_ATTR) |
| { |
| if (XSTR (exp, 0) != XSTR (*pterm, 0)) |
| return exp; |
| |
| if (! strcmp (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 (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 (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 (exp, pterm, insn_code, insn_index) |
| rtx exp; |
| rtx *pterm; |
| int insn_code, 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 (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 (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; |
| } |
| |
| /* 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 (exp, insn_code, insn_index) |
| rtx exp; |
| int insn_code, insn_index; |
| { |
| rtx left, right; |
| struct attr_desc *attr; |
| struct attr_value *av; |
| struct insn_ent *ie; |
| int i; |
| rtx newexp = exp; |
| char *spacer = (char *) obstack_finish (rtl_obstack); |
| |
| /* Don't re-simplify something we already simplified. */ |
| if (RTX_UNCHANGING_P (exp) || MEM_IN_STRUCT_P (exp)) |
| return exp; |
| |
| switch (GET_CODE (exp)) |
| { |
| case AND: |
| left = SIMPLIFY_TEST_EXP (XEXP (exp, 0), insn_code, insn_index); |
| SIMPLIFY_ALTERNATIVE (left); |
| if (left == false_rtx) |
| { |
| obstack_free (rtl_obstack, spacer); |
| return false_rtx; |
| } |
| right = SIMPLIFY_TEST_EXP (XEXP (exp, 1), insn_code, insn_index); |
| SIMPLIFY_ALTERNATIVE (right); |