GCC后端及汇编发布(27)

9.5.8.4.    构建有限状态自动机

有限状态自动机( FSA )被分类为确定性( DFA )及非确定性( NDFA )。对于确定性的自动机,通过初始状态及触发事件,我们可以确切地知道将要迁移到的状态;而对于非确定性自动机,则将通向多个目标。在机器描述文件里,对于指令而言,包含替代(“ | ”操作符)的单元预订,将引入非确定性 FSA

不过,我们可以在产生这个自动机的过程中,通过使用“ -ndfa ”选项来控制这些替代的输出。默认的,没有这个选项,替代以确定性的方式来处理,仅考虑到第一个合格替代的迁移。而使用这个选项,所有合格的替代都被用于构造非确定性自动机。不过在这之后,执行一个 NDFA DFA 的转换,使自动机成为 DFA 。显然,由 ndfa_flag 0 所生成的自动机,效率上不如由 ndfa_flag 1 所生成的自动机,不过前者可以更快地生成,而且效率的损失没有想象的那么大。

 

6401 static void

6402 build_automaton (automaton_t automaton)                                                  in genautomata.c

6403 {

6404   int states_num;

6405   int arcs_num;

6406

6407   ticker_on (&NDFA_time );

6408   if (progress_flag )

6409   {

6410     if (automaton->corresponding_automaton_decl == NULL)

6411       fprintf (stderr , "Create anonymous automaton");

6412     else

6413       fprintf (stderr , "Create automaton `%s'",

6414             automaton->corresponding_automaton_decl->name);

6415       fprintf (stderr , " (1 dot is 100 new states):");

6416   }

6417   make_automaton (automaton);

 

make_automaton 被调用来构建自动机,它将根据“ -ndfa ”选项,依据出现的替代(在 define_insn_reservation 中的操作符“ | ”)构建非确定性或确定性自动机。

 

5693 static void

5694 make_automaton (automaton_t automaton)                                                  in genautomata.c

5695 {

5696   ainsn_t ainsn;

5697   struct insn_reserv_decl *insn_reserv_decl;

5698   alt_state_t alt_state;

5699   state_t state;

5700   state_t start_state;

5701   state_t state2;

5702   ainsn_t advance_cycle_ainsn;

5703   arc_t added_arc;

5704   vla_ptr_t state_stack;

5705   int states_n;

5706   reserv_sets_t reservs_matter = form_reservs_matter (automaton);

5707

5708   VLA_PTR_CREATE (state_stack, 150, "state stack");

5709   /* Create the start state (empty state).  */

5710   start_state = insert_state (get_free_state (1, automaton));

5711   automaton->start_state = start_state;

5712   start_state->it_was_placed_in_stack_for_NDFA_forming = 1;

5713   VLA_PTR_ADD (state_stack, start_state);

5714   states_n = 1;

 

在这里, form_reserv_matter 将再次填充一个单元预订位图。不过,这一次的位图是与自动机一一对应的。在 DEFINE_INSN_RESERVATION模式的概览 一节,我们看到功能单元是需要分配给自动机的(通过 define_cpu_unit 模式分配,每个功能单元只能属于一个自动机),而同一个自动机的不同功能单元之间,又存在依存或互斥的关系(通过 EXCLUSION_SET 等模式定义)。

另外, define_insn_reservation 描述了每个指令类别(注意,严格来讲,名字中 insn 代表的是指令类别)对功能单元的占用情况,汇总每个类别的信息,就能大致知道每个单元的总体预订的情况,这正是前一节所做的事情。

前面反映每个指令类别 CPU 周期中单元预订情况的位图,无法显示这些信息。这里需要把这些信息收集到与自动机对应的位图。

 

5669 static reserv_sets_t

5670 form_reservs_matter (automaton_t automaton)                                      in genautomata.c

5671 {

5672   int cycle, unit;

5673   reserv_sets_t reservs_matter = alloc_empty_reserv_sets();

5674

5675   for (cycle = 0; cycle < max_cycles_num ; cycle++)

5676     for (unit = 0; unit < description ->units_num; unit++)

5677       if (units_array [unit]->automaton_decl

5678             == automaton->corresponding_automaton_decl

5679           && (cycle >= units_array [unit]->min_occ_cycle_num

5680               /* We can not remove queried unit from reservations.  */

5681               || units_array [unit]->query_p

5682               /* We can not remove units which are used

5683                   `exclusion_set', `presence_set',

5684                   `final_presence_set', `absence_set', and

5685                  `final_absence_set'.  */

5686               || units_array [unit]->in_set_p))

5687         set_unit_reserv (reservs_matter, cycle, unit);

5688   return reservs_matter;

5689 }

 

5679 min_occ_cycle_num 反映了该单元所有预订中,最小的预订周期。在这里的处理中,之后的周期,该单元都将被所属自动机视为已预订。这样做显然很保守,但足够简单并且正确。

接着在 make_automaton 5710 一个新的 start_state 被保存入 state_stack 。它将是这个构建中的自动机的起始点。因此 5715 行条件一开始就满足了。

 

make_automaton (continued)

 

5715   while (VLA_PTR_LENGTH (state_stack) != 0)

5716   {

5717     state = VLA_PTR (state_stack, VLA_PTR_LENGTH (state_stack) - 1);

5718     VLA_PTR_SHORTEN (state_stack, 1);

5719     advance_cycle_ainsn = NULL;

5720     for (ainsn = automaton->ainsn_list;

5721         ainsn != NULL;

5722         ainsn = ainsn->next_ainsn)

5723       if (ainsn->first_insn_with_same_reservs)

5724       {

5725         insn_reserv_decl = ainsn->insn_reserv_decl;

5726         if (insn_reserv_decl != DECL_INSN_RESERV (advance_cycle_insn_decl ))

5727         {

5728           /* We process alt_states in the same order as they are

5729             present in the description.  */

5730           added_arc = NULL;

5731           for (alt_state = ainsn->alt_states;

5732               alt_state != NULL;

5733               alt_state = alt_state->next_alt_state)

5734           {

5735             state2 = alt_state->state;

5736             if (!intersected_state_reservs_p (state, state2))

5737              {

5738               state2 = states_union (state, state2, reservs_matter);

5739               if (!state2->it_was_placed_in_stack_for_NDFA_forming)

5740               {

5741                 state2->it_was_placed_in_stack_for_NDFA_forming

5742                         = 1;

5743                 VLA_PTR_ADD (state_stack, state2);

5744                 states_n++;

5745                 if (progress_flag && states_n % 100 == 0)

5746                   fprintf (stderr , ".");

5747               }

5748               added_arc = add_arc (state, state2, ainsn, 1);

5749               if (!ndfa_flag )

5750                 break ;

5751             }

5752           }

5753           if (!ndfa_flag && added_arc != NULL)

5754           {

5755             added_arc->state_alts = 0;

5756             for (alt_state = ainsn->alt_states;

5757                 alt_state != NULL;

5758                 alt_state = alt_state->next_alt_state)

5759             {

5760                state2 = alt_state->state;

5761               if (!intersected_state_reservs_p (state, state2))

5762                 added_arc->state_alts++;

5763             }

5764           }

5765         }

5766          else

5767           advance_cycle_ainsn = ainsn;

5768       }

5769       /* Add transition to advance cycle.  */

5770       state2 = state_shift (state, reservs_matter);

5771       if (!state2->it_was_placed_in_stack_for_NDFA_forming)

5772       {

5773         state2->it_was_placed_in_stack_for_NDFA_forming = 1;

5774         VLA_PTR_ADD (state_stack, state2);

5775         states_n++;

5776         if (progress_flag && states_n % 100 == 0)

5777           fprintf (stderr , ".");

5778       }

5779       if (advance_cycle_ainsn == NULL)

5780         abort ();

5781       add_arc (state, state2, advance_cycle_ainsn, 1);

5782   }

5783   VLA_PTR_DELETE (state_stack);

5784 }

 

在这个自动机中,我们使用状态来追踪资源的使用,状态的迁移显示指令的发布。有两种状态迁移。第一种,某些指令可以在这个状态下发布(即,该指令不会与已发布的指令争夺资源)。另一种,如果没有指令可以被发布,我们不能做任何事,等待 CPU 前进一个周期。

在前面的小节中,已经看到 ainsn 如果设置了 first_insn_with_same_reservs ,表明它是使用该单元预订的在机器描述文件中第一个出现的 define_insn_reservation 模式。这意味着这个模式所预订的单元可能可用,值得做一次查找。而如果 first_insn_with_same_reservs 0 ,则意味着已经安排了使用同样单元的其它的指令类别,只能等下一个周期了(跳到 5770 行)。

上面在 5761 行, state 代表源状态, state2 则是可能的目标状态,注意这两个状态在同一个周期中,它们必须不争夺相同的 CPU 单元,迁移才有可能。 intersected_state_reservs_p 就是检查这两个状态是否会争夺相同的资源,它返回 0 ,如果没有发现资源的争夺。

 

4168 static int

4169 intersected_state_reservs_p (state_t state1, state_t state2)                        in genautomata.c

4170 {

4171   if (state1->automaton != state2->automaton)

4172     abort ();

4173   return reserv_sets_are_intersected (state1->reservs, state2->reservs);

4174 }

 

显而易见,这两个状态必须属于同一个自动机。在 state reservs 域是记录这个状态下各个周期里对功能单元的占用情况的位图(周期数取决于所有 define_insn_reservation 模式中出现的最大周期数),因此满足 3881 行条件,表示这两个状态有冲突。

 

3867 static int

3868 reserv_sets_are_intersected (reserv_sets_t operand_1,                             in genautomata.c

3869                           reserv_sets_t operand_2)

3870 {

3871   set_el_t *el_ptr_1;

3872   set_el_t *el_ptr_2;

3873   set_el_t *cycle_ptr_1;

3874   set_el_t *cycle_ptr_2;

3875

3876   if (operand_1 == NULL || operand_2 == NULL)

3877     abort ();

3878   for (el_ptr_1 = operand_1, el_ptr_2 = operand_2;

3879       el_ptr_1 < operand_1 + els_in_reservs ;

3880       el_ptr_1++, el_ptr_2++)

3881     if (*el_ptr_1 & *el_ptr_2)

3882       return 1;

3883   reserv_sets_or (temp_reserv , operand_1, operand_2);

3884   for (cycle_ptr_1 = operand_1, cycle_ptr_2 = operand_2;

3885       cycle_ptr_1 < operand_1 + els_in_reservs ;

3886       cycle_ptr_1 += els_in_cycle_reserv , cycle_ptr_2 += els_in_cycle_reserv )

3887   {

3888     for (el_ptr_1 = cycle_ptr_1, el_ptr_2 = get_excl_set (cycle_ptr_2);

3889         el_ptr_1 < cycle_ptr_1 + els_in_cycle_reserv ;

3890         el_ptr_1++, el_ptr_2++)

3891     if (*el_ptr_1 & *el_ptr_2)

3892       return 1;

3893     if (!check_presence_pattern_sets (cycle_ptr_1, cycle_ptr_2, FALSE))

3894       return 1;

3895     if (!check_presence_pattern_sets (temp_reserv + (cycle_ptr_2

3896                                  - operand_2),

3897                                  cycle_ptr_2, TRUE))

3898       return 1;

3899     if (!check_absence_pattern_sets (cycle_ptr_1, cycle_ptr_2, FALSE))

3900       return 1;

3901     if (!check_absence_pattern_sets (temp_reserv + (cycle_ptr_2 - operand_2),

3902                                  cycle_ptr_2, TRUE))

3903       return 1;

3904   }

3905   return 0;

3906 }

 

如果这两个状态不竞争资源,那么离下结论还为时过早,我们需要进一步检查功能单元之间是否由于依存、互斥关系导致冲突。

首先检查单元互斥的状况。在 initiate_excl_sets 中根据出现的 EXCLUDE_SET 模式(参见 数据初始化 一节)设置 unit_excl_set_table 。这也是个位图,它的实现是一个 unit_num * unit_num 的二维数组(因为互斥是对称关系,它实际上是一个对称矩阵),互斥的单元 1 2 ,在数组 1*2 处为 1

 

4582 static reserv_sets_t

4583 get_excl_set (reserv_sets_t in_set)

4584 {

4585   int excl_char_num;

4586   int chars_num;

4587   int i;

4588   int start_unit_num;

4589   int unit_num;

4590

4591   chars_num = els_in_cycle_reserv * sizeof (set_el_t);

4592   memset (excl_set , 0, chars_num);

4593   for (excl_char_num = 0; excl_char_num < chars_num; excl_char_num++)

4594     if (((unsigned char *) in_set) [excl_char_num])

4595       for (i = CHAR_BIT - 1; i >= 0; i--)

4596         if ((((unsigned char *) in_set) [excl_char_num] >> i) & 1)

4597          {

4598            start_unit_num = excl_char_num * CHAR_BIT + i;

4599            if (start_unit_num >= description ->units_num)

4600              return excl_set ;

4601            for (unit_num = 0; unit_num < els_in_cycle_reserv ; unit_num++)

4602            {

4603             excl_set [unit_num]

4604                |= unit_excl_set_table [start_unit_num] [unit_num];

4605           }

4606          }

4607   return excl_set ;

4608 }

 

4594 4596 行,只有被预订的单元才去查找与其互斥的单元,结果放在数组 excl_set 中,这个数组的元素也是位图。这个位图是编号为其索引的单元与其它单元的互斥位图。显然如果相斥的单元出现在另一个状态中,这两个状态是不可以合并的。

除了互斥关系,还要检查依赖关系。在 3883 行,首先获取这两个状态的合并状态,结果保存在 temp_reserv 中。在 DEFINE_INSN_RESERVATION式的概览 一节里,我们知道 presence absence 依赖关系有 final 及非 final 的区别。 Final 版本的要求在目的状态进行检查,非 final 则在源状态进行检查, temp_reserv 就作为假定的目的状态。上面表达式“ temp_reserv + (cycle_ptr_2 - operand_2) ”是相应周期假定的目的状态对单元占用位图。

check_presence_pattern_sets 的任务相当清楚,根据在 original_set 中被预订的单元,在 unit_final_presence_set_table unit_presence_set_table 中查找其所依赖的单元。如果所依赖的单元都出现在 checked_set 中,那么这两个状态可能是相容的。

 

4692 static int

4693 check_presence_pattern_sets (reserv_sets_t checked_set,

4694                           reserv_sets_t origional_set,

4695                           int final_p)

4696 {

4697   int char_num;

4698   int chars_num;

4699   int i;

4700   int start_unit_num;

4701   int unit_num;

4702   int presence_p;

4703   pattern_reserv_t pat_reserv;

4704

4705   chars_num = els_in_cycle_reserv * sizeof (set_el_t);

4706   for (char_num = 0; char_num < chars_num; char_num++)

4707     if (((unsigned char *) origional_set) [char_num])

4708       for (i = CHAR_BIT - 1; i >= 0; i--)

4709         if ((((unsigned char *) origional_set) [char_num] >> i) & 1)

4710         {

4711             start_unit_num = char_num * CHAR_BIT + i;

4712            if (start_unit_num >= description ->units_num)

4713              break ;

4714            if ((final_p

4715                && unit_final_presence_set_table [start_unit_num] == NULL)

4716              || (!final_p

4717                  && unit_presence_set_table [start_unit_num] == NULL))

4718              continue ;

4719            presence_p = FALSE;

4720              for (pat_reserv = (final_p

4721                           ? unit_final_presence_set_table [start_unit_num]

4722                           : unit_presence_set_table [start_unit_num]);

4723                pat_reserv != NULL;

4724                pat_reserv = pat_reserv->next_pattern_reserv)

4725             {

4726             for (unit_num = 0; unit_num < els_in_cycle_reserv ; unit_num++)

4727                  if ((checked_set [unit_num] & pat_reserv->reserv [unit_num])

4728                     != pat_reserv->reserv [unit_num])

4729                   break ;

4730             presence_p = presence_p || unit_num >= els_in_cycle_reserv ;

4731            }

4732             if (!presence_p)

4733               return FALSE;

4734          }

4735   return TRUE;

4736 }

 

check_absence_pattern_sets 的处理亦如是,只有所有不能出现的单元都不在 checked_set 中出现,这两个状态才可能相容。

 

4741 static int

4742 check_absence_pattern_sets (reserv_sets_t checked_set,

4743                          reserv_sets_t origional_set,

4744                          int final_p)

4745 {

4746   int char_num;

4747   int chars_num;

4748   int i;

4749   int start_unit_num;

4750   int unit_num;

4751   pattern_reserv_t pat_reserv;

4752

4753   chars_num = els_in_cycle_reserv * sizeof (set_el_t);

4754   for (char_num = 0; char_num < chars_num; char_num++)

4755     if (((unsigned char *) origional_set) [char_num])

4756       for (i = CHAR_BIT - 1; i >= 0; i--)

4757         if ((((unsigned char *) origional_set) [char_num] >> i) & 1)

4758          {

4759            start_unit_num = char_num * CHAR_BIT + i;

4760            if (start_unit_num >= description ->units_num)

4761              break ;

4762            for (pat_reserv = (final_p

4763                           ? unit_final_absence_set_table [start_unit_num]

4764                           : unit_absence_set_table [start_unit_num]);

4765                pat_reserv != NULL;

4766                pat_reserv = pat_reserv->next_pattern_reserv)

4767            {

4768             for (unit_num = 0; unit_num < els_in_cycle_reserv ; unit_num++)

4769                if ((checked_set [unit_num] & pat_reserv->reserv [unit_num])

4770                     != pat_reserv->reserv [unit_num]

4771                        && pat_reserv->reserv [unit_num])

4772                  break ;

4773             if (unit_num >= els_in_cycle_reserv )

4774                return FALSE;

4775            }

4776           }

4777   return TRUE;

4778 }

 

如果状态可以合并,在 5738 行, state_union 把它们合并为一个新状态。然后这个新状态放入 state_stack ,作为下一步的起点。

 

4179 static state_t

4180 states_union (state_t state1, state_t state2, reserv_sets_t reservs)

4181 {

4182   state_t result;

4183   state_t state_in_table;

4184

4185   if (state1->automaton != state2->automaton)

4186     abort ();

4187   result = get_free_state (1, state1->automaton);

4188   reserv_sets_or (result->reservs, state1->reservs, state2->reservs);

4189   reserv_sets_and (result->reservs, result->reservs, reservs);

4190   state_in_table = insert_state (result);

4191   if (result != state_in_table)

4192   {

4193     free_state (result);

4194     result = state_in_table;

4195   }

4196   return result;

4197 }

 

上面参数 reservs 是由 form_reservs_matter 构建的,自动机对功能单元的占用情况。两个状态的合集可能超出了自动机占用的单元,所以要在 4189 行进行与操作。

接着,我们需要记录到这个新状态的迁移。

 

4319 static arc_t

4320 add_arc (state_t from_state, state_t to_state, ainsn_t ainsn,                      in genautomata.c

4321        int state_alts)

4322 {

4323   arc_t new_arc;

4324

4325   new_arc = find_arc (from_state, to_state, ainsn);

4326   if (new_arc != NULL)

4327     return new_arc;

4328   if (first_free_arc == NULL)

4329   {

4330 #ifndef NDEBUG

4331     allocated_arcs_num ++;

4332 #endif

4333     new_arc = create_node (sizeof (struct arc ));

4334     new_arc->to_state = NULL;

4335     new_arc->insn = NULL;

4336     new_arc->next_out_arc = NULL;

4337   }

4338   else

4339   {

4340     new_arc = first_free_arc ;

4341     first_free_arc = first_free_arc ->next_out_arc;

4342   }

4343   new_arc->to_state = to_state;

4344   new_arc->insn = ainsn;

4345   ainsn->arc_exists_p = 1;

4346   new_arc->next_out_arc = from_state->first_out_arc;

4347   from_state->first_out_arc = new_arc;

4348   new_arc->next_arc_marked_by_insn = NULL;

4349   new_arc->state_alts = state_alts;

4350   return new_arc;

4351 }

 

Arc 被定义为如下。对于所有外出的 to_state ,它们由 next_out_arc 链接起来,而 to_state 代表迁移的目标。

 

1140 struct arc                                                                                             in genautomata.c

1141 {

1142   /* The following field refers for the state into which given arc

1143     enters.  */

1144   state_t to_state;

1145   /* The following field describes that the insn issue (with cycle

1146     advancing for special insn `cycle advancing' and without cycle

1147     advancing for others) makes transition from given state to

1148     another given state.  */

1149   ainsn_t insn;

1150   /* The following field value is the next arc output from the same

1151     state.  */

1152   arc_t next_out_arc;

1153   /* List of arcs marked given insn is formed with the following

1154     field. The field is used in transformation NDFA -> DFA.  */

1155   arc_t next_arc_marked_by_insn;

1156   /* The following field is defined if NDFA_FLAG is zero. The member

1157     value is number of alternative reservations which can be used for

1158     transition for given state by given insn.  */

1159   int state_alts;

1160 };

 

Arc 的对象必须与( source state target state insn )对严格的一一对应。这一决定对后面自动机最小化有深刻的影响。这个唯一性由 find_arc 保证。

 

4305 static arc_t

4306 find_arc (state_t from_state, state_t to_state, ainsn_t insn)

4307 {

4308   arc_t arc;

4309

4310   for (arc = first_out_arc (from_state); arc != NULL; arc = next_out_arc (arc))

4311     if (arc->to_state == to_state && arc->insn == insn)

4312       return arc;

4313   return NULL;

4314 }

 

上面在 5749 行, ndfa_flag 如果非 0 ,表明使用“ -ndfa ”选项(以非确定性方式处理替代)。在非确定性方式中,对于一个 define_insn_reservation 的所有替代,将记录每个可能迁移;而在确定性方式中,对于一个 define_insn_reservation 的所有替代,仅记录第一个合格的替代,及可能迁移的数目(不过第一个以外的迁移是不会被采用的)。例如,假定一个 define_insn_reservation 具有三个替代: A B C 。从开始状态 S 开始,我们得到以下的 arcs

S à A (可以迁移,构建状态 SA ,压入 state_stack

S à B (可以迁移,构建状态 SB ,压入 state_stack

S à C (可以迁移,构建状态 SC ,压入 state_stack

而在确定性自动机中,将为第一个可能的迁移创建一个 arc 。在这个例子中,我们可以得到:

S à A (可以迁移,构建状态 SA ,压入 state_stack ),及 arc state_alts 的值为 3

假定接下来的 define_insn_reservation 具有两个替代: E F 。对于非确定性自动机,我们得到:

S à E (可以迁移,构建状态 SE ,压入 state_stack

S à F (可以迁移,构建状态 SF ,压入 state_stack

而对于确定性自动机,我们得到:

S à E (可以迁移,构建状态 SE ,压入 state_stack ),及 arc state_alts 的值为 2

5720 行的 FOR 循环尝试找出所有可能的迁移。那么在 make_automaton 5769 行, state_shift 将通过移动 reservs 域的内容,把当前状态推进一个周期——当然,应该为这个迁移构建一个新状态及 arc 对象。一旦建立,这个状态(这里是 state2 )将被加入 state_stack ,作为后面的开始状态(现在在 state_stack 中有:对非确定性自动机, SA SB SC SE SF ;对确定性自动机, SA SE 。注意这里没有 state2 ,因为 start_state SA = SA )。

5715 WHILE 循环的第二次运行中,从获取 state_stack 状态 SF 对于非确定性自动机,我们得到以下迁移,并且记住,替代必须在有效的自动机声明中共享某些 CPU 单元:

F à E (不可能, intersected_state_reservs_p 返回 1

F à A (构建状态 FA ,压入 state_stack ,假定没有 CPU 单元冲突)

F à B (构建状态 FB ,压入 state_stack ,假定没有 CPU 单元冲突)

F à C (构建状态 FC ,压入 state_stack ,假定没有 CPU 单元冲突)

F à F1 (前进一个周期,构建状态 F1 ,压入 state_stack

而对于确定性自动机我们得到以下迁移:

F à E (不可能, intersected_state_reservs_p 返回 1

F à A (构建状态 FA ,压入 state_stack ,假定没有 CPU 单元冲突)

F à F1 (前进一个周期,构建状态 F1 ,压入 state_stack

那么在下一次循环中,获取了 F1 状态,我们将得到如下迁移:

F1 à A (不可能,假定 intersected_state_reservs_p 返回 1

F1 à B (不可能,假定 intersected_state_reservs_p 返回 1

F1 à C (构建状态 F1C ,压入 state_stack ,假定没有 CPU 单元冲突)

F1 à E (不可能,假定 intersected_state_reservs_p 返回 1

F1 à F (不可能,假定 intersected_state_reservs_p 返回 1

F1 à F2 (推进 F1 一个周期,构建状态 F2 ,压入 state_stack

而对于确定性自动机,获取状态 E ,我们将得到如下迁移:

F1 à A (不可能,假定 intersected_state_reservs_p 返回 1

F1 à B (不可能,假定 intersected_state_reservs_p 返回 1

F1 à C (构建状态 F1C ,压入 state_stack ,假定没有 CPU 单元冲突)

F1 à E (不可能,假定 intersected_state_reservs_p 返回 1

F1 à F (不可能,假定 intersected_state_reservs_p 返回 1

F1 à F2 (推进 F1 一个周期,构建状态 F2 ,压入 state_stack

此时, arc 被由 add_arc 链接入 state ,我们可以得到如下视图。在这个图形中, arc* 记录了迁移的信息,域 next_out_arc 链接起所有从相同状态出发的迁移,而域 to_state 指向目的状态。

t73

73 :构建 DFA ,阶段 1

通过这个方式,找出,并由 arc 记录所有的迁移机会。就通常的认识而言,我们已经构建了这个自动机。不过,如果出现的替代, NDFA 可能被通过使用“ -ndfa ”选项构建了。 NDFA 不是我们所希望的,接着我们需要把它转换为 DFA

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值