9.5.8.5. 有限状态自动机的最小化
用来把 NDFA 转换到 DFA 的算法不保证生成最小的 DFA 。因此,需要尝试最小化所产生的自动机。在这里最小化的算法是,首先假定所有的状态都是相同的,然后一个个地访问这些状态,并选出那些与第一个状态不同的状态。把这些状态放入另一个集合中,再次假定它们是相同的。然后重复上述步骤访问这些状态,直到在假定相同的集合中找不出不同的状态。接着合并相同的状态来产生最小自动机。
6357 static void
6358 minimize_DFA ( automaton_t automaton) in genautomata.c
6359 {
6360 vla_ptr_t equiv_classes;
6361
6362 VLA_PTR_CREATE (equiv_classes, 1500, "equivalence classes");
6363 evaluate_equiv_classes (automaton, &equiv_classes);
6364 merge_states (automaton, &equiv_classes);
6365 pass_states (automaton, set_new_cycle_flags);
6366 VLA_PTR_DELETE (equiv_classes);
6367 }
在 6363 行的 evaluate_equiv_class ,如上面所说的那样,把状态组合为相同的集合。
6201 static void
6202 evaluate_equiv_classes (automaton_t automaton, vla_ptr_t *equiv_classes) in genautomata.c
6203 {
6204 state_t new_equiv_class;
6205 int new_equiv_class_num;
6206 int odd_iteration_flag;
6207 int finish_flag;
6208 vla_ptr_t next_iteration_classes;
6209 state_t *equiv_class_ptr;
6210 state_t *state_ptr;
6211
6212 VLA_PTR_CREATE (all_achieved_states , 1500, "all achieved states");
6213 pass_states (automaton, add_achieved_state );
6214 new_equiv_class = init_equiv_class (VLA_PTR_BEGIN (all_achieved_states ),
6215 VLA_PTR_LENGTH (all_achieved_states ));
6216 odd_iteration_flag = 0;
6217 new_equiv_class_num = 1;
6218 VLA_PTR_CREATE (next_iteration_classes, 150, "next iteration classes");
6219 VLA_PTR_ADD (next_iteration_classes, new_equiv_class);
6220 do
6221 {
6222 odd_iteration_flag = !odd_iteration_flag;
6223 finish_flag = 1;
6224 copy_equiv_class (equiv_classes, &next_iteration_classes);
6225 /* Transfer equiv numbers for the next iteration. */
6226 for (state_ptr = VLA_PTR_BEGIN (all_achieved_states );
6227 state_ptr <= (state_t *) VLA_PTR_LAST (all_achieved_states );
6228 state_ptr++)
6229 if (odd_iteration_flag)
6230 (*state_ptr)->equiv_class_num_2 = (*state_ptr)->equiv_class_num_1;
6231 else
6232 (*state_ptr)->equiv_class_num_1 = (*state_ptr)->equiv_class_num_2;
6233 for (equiv_class_ptr = VLA_PTR_BEGIN (*equiv_classes);
6234 equiv_class_ptr <= (state_t *) VLA_PTR_LAST (*equiv_classes);
6235 equiv_class_ptr++)
6236 if (partition_equiv_class (equiv_class_ptr, odd_iteration_flag,
6237 &next_iteration_classes,
6238 &new_equiv_class_num))
6239 finish_flag = 0;
6240 }
6241 while (!finish_flag);
6242 VLA_PTR_DELETE (next_iteration_classes);
6243 VLA_PTR_DELETE (all_achieved_states );
6244 }
在 6213 行的 pass_states 将遍历这个自动机的状态,并通过 add_achieved_state 把状态加入 all_achieved_states 。
5998 static void
5999 add_achieved_state (state_t state) in genautomata.c
6000 {
6001 VLA_PTR_ADD (all_achieved_states , state);
6002 }
然后在 6214 行, init_equiv_class 通过 next_equiv_class_state 域,以反序,链接起这些状态。它只是假定所有的状态都是相等的,因为 next_equiv_class_state 总是应该链接相同的状态。在 6131 行, result_equiv_class 指向 pass_states 所访问的最后的状态。这个值在 6214 行被保存在 next_iteration_classes 里。
6118 static state_t
6119 init_equiv_class (state_t *states, int states_num) in genautomata.c
6120 {
6121 state_t *state_ptr;
6122 state_t result_equiv_class;
6123
6124 result_equiv_class = NULL;
6125 for (state_ptr = states; state_ptr < states + states_num; state_ptr++)
6126 {
6127 (*state_ptr)->equiv_class_num_1 = 1;
6128 (*state_ptr)->next_equiv_class_state = result_equiv_class;
6129 result_equiv_class = *state_ptr;
6130 }
6131 return result_equiv_class;
6132 }
在 6224 行, copy_equiv_class 从 next_iteration_classes 拷贝相等状态的集合。
6049 static void
6050 copy_equiv_class (vla_ptr_t *to, const vla_ptr_t *from) in genautomata.c
6051 {
6052 state_t *class_ptr;
6053
6054 VLA_PTR_NULLIFY (*to);
6055 for (class_ptr = VLA_PTR_BEGIN (*from);
6056 class_ptr <= (state_t *) VLA_PTR_LAST (*from);
6057 class_ptr++)
6058 VLA_PTR_ADD (*to, *class_ptr);
6059 }
在 evaluate_equiv_classes 的 6216 行,变量 odd_iteration_flag 显示这是否是奇数次的迭代。这个标记控制 equiv_class_num_1 与 equiv_class_num_2 的填充及切换。 equiv_class_num_1 及 equiv_class_num_2 用于记录相同状态的数目。这个数目,对于每个相同状态集来说,是唯一的,并且被用作区分相同状态的集合。我们将在后面看到为什么需要这两个变量。
在 evaluate_equiv_classes 的 6236 行, partition_equiv_class 把初始的假定相同状态的集合分裂为正真相同状态的(多个)集合。
6141 static int
6142 partition_equiv_class (state_t *equiv_class_ptr, int odd_iteration_flag, in genautomata.c
6143 vla_ptr_t *next_iteration_classes,
6144 int *new_equiv_class_num_ptr)
6145 {
6146 state_t new_equiv_class;
6147 int partition_p;
6148 state_t first_state;
6149 state_t curr_state;
6150 state_t prev_state;
6151 state_t next_state;
6152 int out_arcs_num;
6153
6154 partition_p = 0;
6155 if (*equiv_class_ptr == NULL)
6156 abort ();
6157 for (first_state = *equiv_class_ptr;
6158 first_state != NULL;
6159 first_state = new_equiv_class)
6160 {
6161 new_equiv_class = NULL;
6162 if (first_state->next_equiv_class_state != NULL)
6163 {
6164 /* There are more one states in the class equivalence. */
6165 out_arcs_num = set_out_arc_insns_equiv_num (first_state,
6166 odd_iteration_flag);
6167 for (prev_state = first_state,
6168 curr_state = first_state->next_equiv_class_state;
6169 curr_state != NULL;
6170 curr_state = next_state)
6171 {
6172 next_state = curr_state->next_equiv_class_state;
6173 if (state_is_differed (curr_state, first_state, out_arcs_num,
6174 odd_iteration_flag))
6175 {
6176 /* Remove curr state from the class equivalence. */
6177 prev_state->next_equiv_class_state = next_state;
6178 /* Add curr state to the new class equivalence. */
6179 curr_state->next_equiv_class_state = new_equiv_class;
6180 if (new_equiv_class == NULL)
6181 (*new_equiv_class_num_ptr)++;
6182 if (odd_iteration_flag)
6183 curr_state->equiv_class_num_2 = *new_equiv_class_num_ptr;
6184 else
6185 curr_state->equiv_class_num_1 = *new_equiv_class_num_ptr;
6186 new_equiv_class = curr_state;
6187 partition_p = 1;
6188 }
6189 else
6190 prev_state = curr_state;
6191 }
6192 clear_arc_insns_equiv_num (first_state);
6193 }
6194 if (new_equiv_class != NULL)
6195 VLA_PTR_ADD (*next_iteration_classes, new_equiv_class);
6196 }
6197 return partition_p;
6198 }
在前面提及的算法中,在假定相同状态的集合中,其它状态将与第一个状态比较,以找出不同的状态。作为一个快速检测,如果两个状态的出口数目不相同,这两个状态可以被认为不相同。在 partition_equiv_class 的 6165 行, set_out_arc_insns_equiv_num 找出集合中第一个状态的出口的数目。同时这个函数还会设置 arc->insn->insn_reserv_decl->equiv_class_num ,及 arc->insn->insn_reserv_decl->state_alts ,以显示已经得到处理 arc 。注意,这里 arc->insn ,已经不代表特定指令类别,它的意义仅在这里的处理中得到体现。
6008 static int
6009 set_out_arc_insns_equiv_num (state_t state, int odd_iteration_flag) in genautomata.c
6010 {
6011 int state_out_arcs_num;
6012 arc_t arc;
6013
6014 state_out_arcs_num = 0;
6015 for (arc = first_out_arc (state); arc != NULL; arc = next_out_arc (arc))
6016 {
6017 if (arc->insn->insn_reserv_decl->equiv_class_num != 0
6018 || arc->insn->insn_reserv_decl->state_alts != 0)
6019 abort ();
6020 state_out_arcs_num++;
6021 arc->insn->insn_reserv_decl->equiv_class_num
6022 = (odd_iteration_flag
6023 ? arc->to_state->equiv_class_num_1
6024 : arc->to_state->equiv_class_num_2);
6025 arc->insn->insn_reserv_decl->state_alts = arc->state_alts;
6026 if (arc->insn->insn_reserv_decl->equiv_class_num == 0
6027 || arc->insn->insn_reserv_decl->state_alts <= 0)
6028 abort ();
6029 }
6030 return state_out_arcs_num;
6031 }
自动机现在是 DFA ,因此在 6015 行的 FOR 循环中,每个 arc 必须关联不同的状态。在 6021 行,我们设置与这个状态关联的,所有 arc 的 equiv_class_num ,这将被用作判断状态相等的证据之一。
6082 static int
6083 state_is_differed (state_t state, state_t another_state, in genautomata.c
6084 int another_state_out_arcs_num, int odd_iteration_flag)
6085 {
6086 arc_t arc;
6087 int state_out_arcs_num;
6088 int i, presence1_p, presence2_p;
6089
6090 state_out_arcs_num = 0;
6091 for (arc = first_out_arc (state); arc != NULL; arc = next_out_arc (arc))
6092 {
6093 state_out_arcs_num++;
6094 if ((odd_iteration_flag
6095 ? arc->to_state->equiv_class_num_1
6096 : arc->to_state->equiv_class_num_2)
6097 != arc->insn->insn_reserv_decl->equiv_class_num
6098 || (arc->insn->insn_reserv_decl->state_alts != arc->state_alts))
6099 return 1;
6100 }
6101 if (state_out_arcs_num != another_state_out_arcs_num)
6102 return 1;
6103 /* Now we are looking at the states with the point of view of query
6104 units. */
6105 for (i = 0; i < description ->units_num; i++)
6106 if (units_array [i]->query_p)
6107 {
6108 presence1_p = first_cycle_unit_presence (state, i);
6109 presence2_p = first_cycle_unit_presence (another_state, i);
6110 if ((presence1_p && !presence2_p) || (!presence1_p && presence2_p))
6111 return 1;
6112 }
6113 return 0;
6114 }
若 state 与 another_state 等效,这意味着:
1) 它们到达相同的目标状态的集合
2) 它们使用相同的功能单元的集合
3) 它们有等效的源状态的集合
state_is_differed 确保前两点,而 evaluate_equiv_classes 中对分解集合的反复迭代,使第三点得到保证。这也是为什么在 init_equiv_class 中要以倒序排序状态,这样就从最后的目标状态开始,向源状态回溯。
如果状态被判定为不相同,返回到 partition_equiv_class 之后,所有与 first_state 不同的状态都放入同一个集合,同时共享同一个 equiv_class_num 。注意 6182 到 6185 行的赋值,在 odd_iteration_flag 为 true 时,赋值的是 equiv_class_num_2 (初始值为 2 ),与上面正好相反(此时,当前使用的是 equiv_class_num_1 )。
这个集合则又在 partition_equiv_class 的 6175 行的 FOR 循环处理(它在 6159 行赋予 first_state )。这个集合继续依照第一个状态进行分解(注意对 set_out_arc_insns_equiv_num 的调用),因为在这个过程中 odd_iteration_flag 维持不变,因此每个不同的集合有递增的 equiv_class_num_2 (假设使用的是 equiv_class_num_1 ,它也在这个过程中维持不变)。
返回 evaluate_equiv_classes ,对新增的集合更新 equiv_class_num_1 或 equiv_class_num_2 (取决于 odd_iteration_flag ,用于区分不同的集合)。然后对每个新增的集合再次调用 partition_equiv_class 进行分解。
这个过程一直重复,直到没有再划分出新的集合为止。
在 partition_equiv_class 的 6192 行,当某个等效集被处理之后,应该恢复对应 arc 的域。使得这个集合与其它部分分离开来( state_is_differed 总是在 6009 行返回 1 )。
6035 static void
6036 clear_arc_insns_equiv_num (state_t state) in genautomata.c
6037 {
6038 arc_t arc;
6039
6040 for (arc = first_out_arc (state); arc != NULL; arc = next_out_arc (arc))
6041 {
6042 arc->insn->insn_reserv_decl->equiv_class_num = 0;
6043 arc->insn->insn_reserv_decl->state_alts = 0;
6044 }
6045 }
在找出等效类别集合后,在 minimize_DFA 的 6364 行,我们可以通过 merge_states 把等效状态合并起来形成最小化的自动机。
6247 static void
6248 merge_states (automaton_t automaton, vla_ptr_t *equiv_classes) in genautomata.
6249 {
6250 state_t *equiv_class_ptr;
6251 state_t curr_state;
6252 state_t new_state;
6253 state_t first_class_state;
6254 alt_state_t alt_states;
6255 alt_state_t alt_state, new_alt_state;
6256 arc_t curr_arc;
6257 arc_t next_arc;
6258
6259 /* Create states corresponding to equivalence classes containing two
6260 or more states. */
6261 for (equiv_class_ptr = VLA_PTR_BEGIN (*equiv_classes);
6262 equiv_class_ptr <= (state_t *) VLA_PTR_LAST (*equiv_classes);
6263 equiv_class_ptr++)
6264 if ((*equiv_class_ptr)->next_equiv_class_state != NULL)
6265 {
6266 /* There are more one states in the class equivalence. */
6267 /* Create new compound state. */
6268 new_state = get_free_state (0, automaton);
6269 alt_states = NULL;
6270 first_class_state = *equiv_class_ptr;
6271 for (curr_state = first_class_state;
6272 curr_state != NULL;
6273 curr_state = curr_state->next_equiv_class_state)
6274 {
6275 curr_state->equiv_class_state = new_state;
6276 if (curr_state->component_states == NULL)
6277 {
6278 new_alt_state = get_free_alt_state ();
6279 new_alt_state->state = curr_state;
6280 new_alt_state->next_alt_state = alt_states;
6281 alt_states = new_alt_state;
6282 }
6283 else
6284 for (alt_state = curr_state->component_states;
6285 alt_state != NULL;
6286 alt_state = alt_state->next_sorted_alt_state)
6287 {
6288 new_alt_state = get_free_alt_state ();
6289 new_alt_state->state = alt_state->state;
6290 new_alt_state->next_alt_state = alt_states;
6291 alt_states = new_alt_state;
6292 }
6293 }
6294 /* Its is important that alt states were sorted before and
6295 after merging to have the same querying results. */
6296 new_state->component_states = uniq_sort_alt_states (alt_states);
6297 }
6298 else
6299 (*equiv_class_ptr)->equiv_class_state = *equiv_class_ptr;
6300 for (equiv_class_ptr = VLA_PTR_BEGIN (*equiv_classes);
6301 equiv_class_ptr <= (state_t *) VLA_PTR_LAST (*equiv_classes);
6302 equiv_class_ptr++)
6303 if ((*equiv_class_ptr)->next_equiv_class_state != NULL)
6304 {
6305 first_class_state = *equiv_class_ptr;
6306 /* Create new arcs output from the state corresponding to
6307 equiv class. */
6308 for (curr_arc = first_out_arc (first_class_state);
6309 curr_arc != NULL;
6310 curr_arc = next_out_arc (curr_arc))
6311 add_arc (first_class_state->equiv_class_state,
6312 curr_arc->to_state->equiv_class_state,
6313 curr_arc->insn, curr_arc->state_alts);
6314 /* Delete output arcs from states of given class equivalence. */
6315 for (curr_state = first_class_state;
6316 curr_state != NULL;
6317 curr_state = curr_state->next_equiv_class_state)
6318 {
6319 if (automaton->start_state == curr_state)
6320 automaton->start_state = curr_state->equiv_class_state;
6321 /* Delete the state and its output arcs. */
6322 for (curr_arc = first_out_arc (curr_state);
6323 curr_arc != NULL;
6324 curr_arc = next_arc)
6325 {
6326 next_arc = next_out_arc (curr_arc);
6327 free_arc (curr_arc);
6328 }
6329 }
6330 }
6331 else
6332 {
6333 /* Change `to_state' of arcs output from the state of given
6334 equivalence class. */
6335 for (curr_arc = first_out_arc (*equiv_class_ptr);
6336 curr_arc != NULL;
6337 curr_arc = next_out_arc (curr_arc))
6338 curr_arc->to_state = curr_arc->to_state->equiv_class_state;
6339 }
6340 }
merge_states 与 create_composed_state 十分类似。在上面 6261 行的 FOR 循环中,对于每个等效类别的集合,一个新状态被建立,并且所有属于这个类别的状态被排序并链接入 component_states 。注意 6275 到 6299 行,对于每个状态,其 equiv_class_state 域被设置为新状态。
接着在 6300 行的 FOR 循环里,首先构建用于来 / 往新状态的 arc (由 6308 行的 FOR 循环处理,注意到 add_arc 将不会构建 arc 如果它已经被构建了),然后把来 / 往过时状态的 arc 移除。
在合并后, build_automaton 返回到 create_automata , enumerate_states 找出状态的总数。
6480 static void
6481 enumerate_states (automaton_t automaton) in genautomata.c
6482 {
6483 curr_state_order_num = 0;
6484 pass_states (automaton, set_order_state_num );
6485 automaton->achieved_states_num = curr_state_order_num ;
6486 }
6472 static void
6473 set_order_state_num ( state_t state) in genautomata.c
6474 {
6475 state->order_state_num = curr_state_order_num ;
6476 curr_state_order_num ++;
6477 }
我们已经构建了最小化的 DFA ,所有的等效状态都已经被合并。现在我们需要按等效状态类别来分组指令类别,因为属于同一个等效类别状态的指令类别可以以相同的方式处理。这在 create_automata 的 6893 行的 set_insn_equiv_classes 来完成。
6584 static void
6585 set_insn_equiv_classes (automaton_t automaton) in genautomata.c
6586 {
6587 ainsn_t ainsn;
6588 ainsn_t first_insn;
6589 ainsn_t curr_insn;
6590 ainsn_t cyclic_insn_list;
6591 ainsn_t insn_with_same_reservs;
6592 int equiv_classes_num;
6593
6594 /* All insns are included in one equivalence class. */
6595 cyclic_insn_list = NULL;
6596 for (ainsn = automaton->ainsn_list; ainsn != NULL; ainsn = ainsn->next_ainsn)
6597 if (ainsn->first_insn_with_same_reservs)
6598 cyclic_insn_list = insert_ainsn_into_equiv_class (ainsn,
6599 cyclic_insn_list);
6600 /* Process insns in order to make equivalence partition. */
6601 pass_states (automaton, process_state_for_insn_equiv_partition );
6602 /* Enumerate equiv classes. */
6603 for (ainsn = automaton->ainsn_list; ainsn != NULL; ainsn = ainsn->next_ainsn)
6604 /* Set undefined value. */
6605 ainsn->insn_equiv_class_num = -1;
6606 equiv_classes_num = 0;
6607 for (ainsn = automaton->ainsn_list; ainsn != NULL; ainsn = ainsn->next_ainsn)
6608 if (ainsn->insn_equiv_class_num < 0)
6609 {
6610 first_insn = ainsn;
6611 if (!first_insn->first_insn_with_same_reservs)
6612 abort ();
6613 first_insn->first_ainsn_with_given_equialence_num = 1;
6614 curr_insn = first_insn;
6615 do
6616 {
6617 for (insn_with_same_reservs = curr_insn;
6618 insn_with_same_reservs != NULL;
6619 insn_with_same_reservs
6620 = insn_with_same_reservs->next_same_reservs_insn)
6621 insn_with_same_reservs->insn_equiv_class_num = equiv_classes_num;
6622 curr_insn = curr_insn->next_equiv_class_insn;
6623 }
6624 while (curr_insn != first_insn);
6625 equiv_classes_num++;
6626 }
6627 automaton->insn_equiv_classes_num = equiv_classes_num;
6628 }
看到在 form_ainsn_with_same_reservs 中,如果这个 ainsn 是所有具有相同单元预订对象的第一个,或者这个指令是单独构成一个集合的 advance_cycle_insn_decl ,设置 first_insn_with_same_reservs 域。在 6598 行, insert_ainsn_into_equiv_class 把这些指令类别通过 next_equiv_class_insn 链接在一起。
6495 static ainsn_t
6496 insert_ainsn_into_equiv_class (ainsn_t ainsn, in genautomata.c
6497 ainsn_t cyclic_equiv_class_insn_list)
6498 {
6499 if (cyclic_equiv_class_insn_list == NULL)
6500 ainsn->next_equiv_class_insn = ainsn;
6501 else
6502 {
6503 ainsn->next_equiv_class_insn
6504 = cyclic_equiv_class_insn_list->next_equiv_class_insn;
6505 cyclic_equiv_class_insn_list->next_equiv_class_insn = ainsn;
6506 }
6507 return ainsn;
6508 }
在这一步,我们得到如下数据结构。图中红色循环链表中的节点代表不同的等效指令类别列表,而蓝色的链表则由是等效的指令类别构成的。
图 80 :等效类别指令的分组,阶段 2
由于 next_same_reservs_insn 所形成的链表是由 form_ainsn_with_same_reservs 构建的,此后经由 NDFA_to_DFA 及 minimize_DFA 的处理,这个链表已经过时。因此,接下来需要 process_state_for_insn_equiv_partition 来重新划分等效的指令集。
6562 static void
6563 pr ocess_state_for_insn_equiv_partition ( state_t state) in genautomata.c
6564 {
6565 arc_t arc;
6566 arc_t *insn_arcs_array;
6567 int i;
6568 vla_ptr_t insn_arcs_vect;
6569
6570 VLA_PTR_CREATE (insn_arcs_vect, 500, "insn arcs vector");
6571 VLA_PTR_EXPAND (insn_arcs_vect, description ->insns_num);
6572 insn_arcs_array = VLA_PTR_BEGIN (insn_arcs_vect);
6573 /* Process insns of the arcs. */
6574 for (i = 0; i < description ->insns_num; i++)
6575 insn_arcs_array [i] = NULL;
6576 for (arc = first_out_arc (state); arc != NULL; arc = next_out_arc (arc))
6577 insn_arcs_array [arc->insn->insn_reserv_decl->insn_num] = arc;
6578 for (arc = first_out_arc (state); arc != NULL; arc = next_out_arc (arc))
6579 process_insn_equiv_class (arc->insn, insn_arcs_array);
6580 VLA_PTR_DELETE (insn_arcs_vect);
6581 }
记得 first_out_arc 及 next_out_arc 都返回从 state 出发的迁移(由 arc 代表),并且 arc 与( source state , target state , insn )对一一对应,检查由上面 6597 行的 process_insn_equiv_class 以 arc 为单位进行。
6531 static void
6532 process_insn_equiv_class (ainsn_t ainsn, arc_t *insn_arcs_array)
6533 {
6534 ainsn_t next_insn;
6535 ainsn_t curr_insn;
6536 ainsn_t cyclic_insn_list;
6537 arc_t arc;
6538
6539 if (insn_arcs_array [ainsn->insn_reserv_decl->insn_num] == NULL)
6540 abort ();
6541 curr_insn = ainsn;
6542 /* New class of ainsns which are not equivalent to given ainsn. */
6543 cyclic_insn_list = NULL;
6544 do
6545 {
6546 next_insn = curr_insn->next_equiv_class_insn;
6547 arc = insn_arcs_array [curr_insn->insn_reserv_decl->insn_num];
6548 if (arc == NULL
6549 || (insn_arcs_array [ainsn->insn_reserv_decl->insn_num]->to_state
6550 != arc->to_state))
6551 {
6552 delete_ainsn_from_equiv_class (curr_insn);
6553 cyclic_insn_list = insert_ainsn_into_equiv_class (curr_insn,
6554 cyclic_insn_list);
6555 }
6556 curr_insn = next_insn;
6557 }
6558 while (curr_insn != ainsn);
6559 }
我们知道使用相同的单元预订是作为等效指令类别的必要条件,因此由 minimize_DFA 得到的等效指令类别集合,必然不会跨越一条以上在 form_ainsn_with_same_reservs 中构建的 next_same_reservs_insn 链表,相反这些链表可能会分解成多个链表。那么在 set_insn_equiv_classes 中,余下的代码重新设置 equiv_classes_num 。
图 81 :等效类别指令的分组,阶段 3