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 指向目的状态。
图 73 :构建 DFA ,阶段 1
通过这个方式,找出,并由 arc 记录所有的迁移机会。就通常的认识而言,我们已经构建了这个自动机。不过,如果出现的替代, NDFA 可能被通过使用“ -ndfa ”选项构建了。 NDFA 不是我们所希望的,接着我们需要把它转换为 DFA 。