-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;
- const 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, unit->first_lineno);
-
- 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, op->lineno);
- }
- }
-
- /* 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);
-
- /* If we have just a few units, we may be all right expanding the whole
- thing. But the expansion is 2**N in space on the number of opclasses,
- so we can't do this for very long -- Alpha and MIPS in particular have
- problems with this. So in that situation, we fall back on an alternate
- implementation method. */
-#define NUM_UNITOP_CUTOFF 20
-
- if (num_unit_opclasses < NUM_UNITOP_CUTOFF)
- {
- /* 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);
- }
- }
- else
- {
- /* 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 (ORX_OP, unitsmask, attr_copy_rtx (newexp));
- }
- }
-
- /* Simplify the unit mask expression, encode it, and make an attribute
- for the function_units_used function. */
- unitsmask = simplify_by_exploding (unitsmask);
-
- if (num_unit_opclasses < NUM_UNITOP_CUTOFF)
- unitsmask = encode_units_mask (unitsmask);
- else
- {
- /* We can no longer encode unitsmask at compile time, so emit code to
- calculate it at runtime. Rather, put a marker for where we'd do
- the code, and actually output it in write_attr_get(). */
- unitsmask = attr_rtx (FFS, unitsmask);
- }
-
- make_internal_attr ("*function_units_used", unitsmask, 10);
-
- /* 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 ***)
- xmalloc ((num_units + 1) * sizeof (struct function_unit_op **));
- unit_num = (struct function_unit **)
- xmalloc ((num_units + 1) * sizeof (struct function_unit *));
-
- unit_num[num_units] = unit = (struct function_unit *)
- xmalloc (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 **)
- xmalloc (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 **)
- xmalloc (unit_num[num_units]->num_opclasses
- * sizeof (struct function_unit_op *));
-
- for (unit = units, i = 0; unit; i += unit->num_opclasses, unit = unit->next)
- memcpy (&op_array[i], unit_ops[unit->num],
- 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 = 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 (*,*)). */
- {
- int unknown;
- unit->max_blockage = max_attr_value (max_blockage, &unknown);
- }
-
- /* 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 its 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, 20);
- }
-
- 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)
- {
- int unknown = 0, max;
- max = max_attr_value (exp, &unknown);
- if (! unknown)
- {
- exp = attr_rtx (IF_THEN_ELSE, known_true, exp,
- make_numeric_value (max));
- 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 const char *fmt;
-
- code = GET_CODE (x);
-
- switch (code)
- {
- case CONST_STRING:
- i = atoi (XSTR (x, 0));
- if (i < 0)
- /* The sign bit encodes a one's compliment mask. */
- abort ();
- 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;
-
- default:
- break;
- }
-
- /* Compare the elements. If any pair of corresponding elements
- fail to match, return 0 for the whole things. */
-
- fmt = GET_RTX_FORMAT (code);
- for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
- {
- switch (fmt[i])
- {
- case 'V':
- case 'E':
- for (j = 0; j < XVECLEN (x, i); j++)
- 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;
-}
-\f
-/* 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);
- }
-}
-\f
-/* 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) PARAMS ((rtx));
- rtx (*address_fn) PARAMS ((rtx));
-{
- int i;
- rtx newexp;
-
- if (GET_CODE (exp) == COND)
- {
- /* See if any tests use addresses. */
- address_used = 0;
- for (i = 0; i < XVECLEN (exp, 0); i += 2)
- walk_attr_value (XVECEXP (exp, 0, i));
-
- if (address_used)
- return (*address_fn) (exp);
-
- /* Make a new copy of this COND, replacing each element. */
- newexp = rtx_alloc (COND);
- XVEC (newexp, 0) = rtvec_alloc (XVECLEN (exp, 0));
- for (i = 0; i < XVECLEN (exp, 0); i += 2)
- {
- XVECEXP (newexp, 0, i) = XVECEXP (exp, 0, i);
- XVECEXP (newexp, 0, i + 1)
- = substitute_address (XVECEXP (exp, 0, i + 1),
- no_address_fn, address_fn);
- }
-
- XEXP (newexp, 1) = substitute_address (XEXP (exp, 1),
- no_address_fn, address_fn);
-
- return newexp;
- }
-
- else if (GET_CODE (exp) == IF_THEN_ELSE)
- {
- address_used = 0;
- walk_attr_value (XEXP (exp, 0));
- if (address_used)
- return (*address_fn) (exp);
-
- return attr_rtx (IF_THEN_ELSE,
- substitute_address (XEXP (exp, 0),
- no_address_fn, address_fn),
- substitute_address (XEXP (exp, 1),
- no_address_fn, address_fn),
- substitute_address (XEXP (exp, 2),
- no_address_fn, address_fn));
- }
-
- return (*no_address_fn) (exp);
-}
-\f
-/* Make new attributes from the `length' attribute. The following are made,
- each corresponding to a function called from `shorten_branches' or
- `get_attr_length':