在产生了为指令提取属性的函数之后,用于调度器的数据及辅助函数将被生成如下。
main (continued)
6224 if (num_units|| num_dfa_decls)
6225 {
6226 /*Write out information about function units. */
6227 write_function_unit_info ();
6228 /*Output code for pipeline hazards recognition based on DFA
6229 (deterministicfinite state automata. */
6230 write_automata ();
6231 }
至于指令之间的接口,考虑以下来自gccinfo的段落。
在现代处理器中,主要有两种连锁延迟(interlock delay)。第一种是一个数据依赖延迟(data dependence delay)确定的“指令休眠时间(instruction latency time)”。该指令的执行直到所有的源数据都被之前的指令所评估后,才开始(有更复杂的情形,即当该指令开始执行时,即便这个数据还不可用,但数据会在该指令开始执行后的给定时间内就绪)。把数据依赖延迟考虑入内是简单的。两条指令之间的数据依赖(真依赖,输出依赖,反向依赖)延迟由一个常量给定。在绝大多数情形中,这个方法就足够了。第二种连锁延迟是一个预订延迟(reservationdelay)。预订延迟意味着两条执行中的指令将需要共享的处理器资源,即,总线,内部寄存器,及/或功能单元,它们会被预订一段时间。把这种延迟考虑入内是复杂的,尤其对于现代的RISC处理器。
在处理DEFINE_FUNCTION_UNIT模式一节中,我们已经看到expand_units将构建属性来为功能单元保存信息。这些属性列出如下:
*`unit-name`_cost_`op-num` (可选,指令功能单元的issue-cost)
*function_units_used (为指令所使用的功能单元)
*`unit-name`_block_`op-num` (指令阻塞功能单元)
*`unit-name`_unit_blockage_range (可选)
*`unit-name`_ready_cost (对于指令功能单元的就绪代价)
*result_ready_cost (对于所有指令功能单元的就绪代价)
*`unit-name`_cases (为使用指令绑定binds op->condexp与op->num)
对于上面的属性,op是代表使用这个功能单元的指令。
注意除了下面指定的属性以外的属性,将被输出作为函数。生成的函数result_ready_cost描述了上面的“指令休眠时间(instruction latencytime)“,而阻塞的属性描述了预订的延迟。
其中,*`unit-name`_cost_`op-num`,*`unit-name`_block_`op-num`,*`unit-name`_cases都设置了特殊的标记(is_special)。这些属性将不会被write_attr_get所输出(参见main的6197行),而其它属性将被上面的write_attr_get所输出。这些属性所包含的信息将在这里用于产生函数。
5616 static void
5617 write_function_unit_info (void) ingenattrtab.c
5618 {
5619 struct function_unit *unit;
5620 int i;
5621
5622 /* Write outconflict routines for function units. Don't bother writing
5623 one if there is only one issue delayvalue. */
5624
5625 for (unit = units;unit; unit = unit->next)
5626 {
5627 if (unit->needs_blockage_function)
5628 write_complex_function (unit,"blockage", "block");
5629
5630 /* If the minimumand maximum conflict costs are the same, there
5631 is only one value, so we don't need afunction. */
5632 if (! unit->needs_conflict_function)
5633 {
5634 unit->default_cost = make_numeric_value (unit->issue_delay.max);
5635 continue;
5636 }
5637
5638 /* The functionfirst computes the case from the candidate insn. */
5639 unit->default_cost = make_numeric_value (0);
5640 write_complex_function (unit,"conflict_cost", "cost");
5641 }
5642
5643 /* Now that allfunctions have been written, write the table describing
5644 the function units. The name is includedfor documentation purposes
5645 only. */
5646
5647 printf ("const struct function_unit_descfunction_units[] = {\n");
5648
5649 /* Write out thedescriptions in numeric order, but don't force that order
5650 on the list. Doing so increases the runtimeof genattrtab.c. */
5651 for (i = 0; i< num_units;i++)
5652 {
5653 for (unit =units;unit; unit = unit->next)
5654 if (unit->num == i)
5655 break;
5656
5657 printf (" {\"%s\", %d, %d, %d, %s, %d,%s_unit_ready_cost, ",
5658 unit->name, 1 <<unit->num, unit->multiplicity,
5659 unit->simultaneity, XSTR(unit->default_cost, 0),
5660 unit->issue_delay.max,unit->name);
5661
5662 if (unit->needs_conflict_function)
5663 printf ("%s_unit_conflict_cost,", unit->name);
5664 else
5665 printf ("0, ");
5666
5667 printf ("%d, ",unit->max_blockage);
5668
5669 if (unit->needs_range_function)
5670 printf ("%s_unit_blockage_range,", unit->name);
5671 else
5672 printf ("0, ");
5673
5674 if (unit->needs_blockage_function)
5675 printf ("%s_unit_blockage",unit->name);
5676 else
5677 printf ("0");
5678
5679 printf ("}, \n");
5680 }
5681
5682 if (num_units == 0)
5683 printf ("{\"dummy\", 0, 0, 0,0, 0, 0, 0, 0, 0, 0} /* a dummy element */");
5684 printf ("};\n\n");
5685 }
在5628行的调用中,参数name是“blockage”,而connection是“block”,在5640行,参数name是“conflict_cost”,而connection则是“cost”。在expand_units中,可以看到当最大阻塞不等于最小阻塞时(2096 ~ 2100行),设置标记needs_blockage_function,而当最大issue_delay不等于最小issue_delay时(1849行),设置标记needs_conflict_function。不过,注意当设置了needs_conflict_function时,needs_blockage_function也要设置。
因此在write_complex_function中的5750行,对于第一次调用(在write_function_unit_info的5628行,我们为每个单元查找属性*`unit-name`_block_`op-num`(该属性与单元一一对应),而对于第二次调用(在write_function_unit_info的5640行)我们为每个单元查找属性*`unit-name`_cost_`op-num`(该属性与单元一一对应)。
5687 static void
5688 write_complex_function (struct function_unit *unit, ingenattrtab.c
5689 constchar *name,
5690 constchar *connection)
5691 {
5692 struct attr_desc*case_attr, *attr;
5693 struct attr_value*av, *common_av;
5694 rtx value;
5695 char str[256];
5696 const char*pstr;
5697 int using_case;
5698 int i;
5699
5700 printf ("static int\n");
5701 printf ("%s_unit_%s (rtx executing_insn,rtx candidate_insn)\n",
5702 unit->name, name);
5703 printf ("{\n");
5704 printf (" rtx insn;\n");
5705 printf (" int casenum;\n\n");
5706 printf (" insn = executing_insn;\n");
5707 printf (" switch (recog_memoized (insn))\n");
5708 printf (" {\n");
5709
5710 /* Write the`switch' statement to get the case value. */
5711 if (strlen (unit->name) + sizeof "*_cases" > 256)
5712 abort ();
5713 sprintf (str, "*%s_cases",unit->name);
5714 pstr = str;
5715 case_attr = find_attr(&pstr, 0);
5716 if (! case_attr)
5717 abort ();
5718 common_av = find_most_used (case_attr);
5719
5720 for (av = case_attr->first_value;av; av = av->next)
5721 if (av != common_av)
5722 write_attr_case (case_attr, av, 1,
5723 "casenum =",";", 4, unit->condexp);
5724
5725 write_attr_case (case_attr, common_av, 0,
5726 "casenum =",";", 4, unit->condexp);
5727 printf (" }\n\n");
5728
5729 /* Now write anouter switch statement on each case. Then write
5730 the tests on the executing function withineach. */
5731 printf (" insn = candidate_insn;\n");
5732 printf (" switch (casenum)\n");
5733 printf (" {\n");
5734
5735 for (i = 0; i < unit->num_opclasses; i++)
5736 {
5737 /* Ensure usingthis case. */
5738 using_case = 0;
5739 for (av =case_attr->first_value; av; av = av->next)
5740 if (av->num_insns
5741 && contained_in_p (make_numeric_value (i), av->value))
5742 using_case = 1;
5743
5744 if (! using_case)
5745 continue;
5746
5747 printf (" case %d:\n", i);
5748 sprintf (str, "*%s_%s_%d",unit->name, connection, i);
5749 pstr = str;
5750 attr = find_attr(&pstr, 0);
5751 if (! attr)
5752 abort ();
5753
5754 /* If singlevalue, just write it. */
5755 value = find_single_value (attr);
5756 if (value)
5757 write_attr_set (attr, 6, value, "return", ";\n", true_rtx,-2, -2);
5758 else
5759 {
5760 common_av = find_most_used (attr);
5761 printf (" switch (recog_memoized (insn))\n");
5762 printf ("\t{\n");
5763
5764 for (av =attr->first_value; av; av = av->next)
5765 if (av != common_av)
5766 write_attr_case (attr, av, 1,
5767 "return",";", 8, unit->condexp);
5768
5769 write_attr_case (attr, common_av, 0,
5770 "return", ";", 8,unit->condexp);
5771 printf (" }\n\n");
5772 }
5773 }
5774
5775 /* This defaultcase should not be needed, but gcc's analysis is not
5776 good enough to realize that the defaultcase is not needed for the
5777 secondswitch statement. */
5778 printf (" default:\n abort ();\n");
5779 printf (" }\n}\n\n");
5780 }
在5718行,属性*`unit-name`_cases首先被输出。对于这个属性,其内容是COND对象,以op的condexp(用于指令的匹配模式)作为测试表达式,并且在跳转中为op的序号。记住op的condexp将使用known_true,及这里的unit的condexp(该表达式表示这个单元被选中但不不知道是谁)表达式来简化。这个输出函数看起来如下(绿色的内容不是工具输出的)。
static int
`unit-name`_units_blockage (rtxexecuting_insn, rtx candidate_insn)
{
rtx insn;
int casenum;
insn = executing_insn;
switch (recog_memoized (insn))
{
case insn-code-n : //instructions that use this value
…
extract_constrain_insn_cached (insn); // used according to attr characteristic.
extract_insn_cached (insn); // used according to attr characteristic.
if (`op1->condexp` ) // simplified op->condexp for insn n
{
casenum = `op1->num`; // op number for insn n
}
else if (…) // for attribute value of cond
…
else // for attribute value of cond
…
break;
… // other & default cases, default casewritten by line 5725
}
insn = candidate_insn;
switch (casenum)
{
case 0 : // attribute of `unit-name`_block_0
switch (recog_memoized (insn))
{
case insn-code-m :
…
extract_constrain_insn_cached(insn); // used according to attr characteristic
extract_insn_cached(insn); //used according to attr characteristic.
return `op0->issue_delay`; // often it is typical case
…
}
…
}
}
`unit-name`_units_blockage,给定一个已经运行在该单元上的指令,以及一个将要运行的候选,将给出由于功能单元冲突造成的延迟。对于write_complex_function的第二次调用,我们可以得到如下生成的函数(绿色内容不是工具输出的)。
staticint
`unit-name`_units_conflict_cost (rtxexecuting_insn, rtx candidate_insn)
{
rtx insn;
int casenum;
insn = executing_insn;
switch (recog_memoized (insn))
{
caseinsn-code-n : // instructions that use this value
…
extract_constrain_insn_cached(insn); // used accordingto attr characteristic.
extract_insn_cached(insn); //used according to attr characteristic.
if(`op1->condexp` ) // simplified op->condexp for insn n
{
casenum = `op1->num`; // op number for insn n
}
else if (…) // for attribute value of cond
…
else // for attribute value of cond
…
break;
… // other & default cases, default casewritten by line 5725
}
insn= candidate_insn;
switch(casenum)
{
case 0 : // attribute of `unit-name`_block_0
switch (recog_memoized (insn))
{
caseinsn-code-m :
…
extract_constrain_insn_cached(insn); // used according to attr characteristic
extract_insn_cached(insn); //used according to attr characteristic.
if (`op0->conflict_exp`) // typical style for conflict case
return `op0->issue_delay`;
else
return 0 ;
…
}
…
}
}
`unit-name`_units_conflict_cost,给定一个已经运行在这个单元的指令,以及一个将要运行的候选,将给出从这个正在执行指令开始的时间到该候选可以开始的时间代价(忽略并发指令数目的限制)。
这两个函数组听起来很相似。阻塞与冲突代价的区别在于阻塞应用于使用该单元的指令,而冲突代价仅为被冲突列表指出的指令所承担,其它没有。
实际上,`unit-name`_units_blockage组从目的意义上,包含`unit-name`_units_conflict_cost组。在代码中,因为属性*`unit-name`_block_`op-num`及*`unit-name`_cost_`op-num`含有相同的值(参见expand_units,1856及2084行)。并且设置标记needs_conflict_function,同时还会设置needs_blockage_function(反之则不然)。
因此稍后,我们可以看到仅使用`unit-name`_units_blockage。
在write_function_unit_info余下部分里,填充function_units数组,至于function_unit的定义,参见genattr工具,由函数write_units产生。