📄 genattrtab.c
字号:
/* 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 voidexpand_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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -