GCC后端及汇编发布(34)

9.6.3. 输出功能单元数据

在产生了为指令提取属性的函数之后,用于调度器的数据及辅助函数将被生成如下。

 

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的序号。记住opcondexp将使用known_true,及这里的unitcondexp(该表达式表示这个单元被选中但不不知道是谁)表达式来简化。这个输出函数看起来如下(绿色的内容不是工具输出的)。

 

       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产生。

你可能感兴趣的:(function,汇编,struct,gcc,conflict,delay)