9.5.8.5. Minimize finite state automaton
The algorithm used to transform NDFA to DFA does not promise to produce the minimized DFA. So, it needs try to minimize the automaton created. The minimization algorithm here is, first assuming all states are same, and then visits these states one by one, and selects out those are not the same as the first state. Puts those states in another set and again assumes them are same. Then visits these states repeatly by above steps, until no different state can be found out in the set assuming equivalent set. Next merges the equivalent states to produce the minimized automaton.
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 }
evaluate_equiv_class at line 6363, groups the states into equivalence set as above describing.
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 }
In pass_states at line 6213, it will traverse the automaton by states, and add states into all_achieved_states by add_achieved_state .
5998 static void
5999 add_achieved_state (state_t state) in genautomata.c
6000 {
6001 VLA_PTR_ADD (all_achieved_states , state);
6002 }
Then init_equiv_class at line 6214 links these states by next_equiv_class_state in reverse order. It just assumes all the states are equivalent, as next_equiv_class_state should links equivalent state. At line 6131, result_equiv_class points to the last state visited by pass_states . This value is saved in next_iteration_classes at line 6214.
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 }
At line 6214, copy_equiv_class copies equivalence sets from 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 }
At line 6216 in evaluate_equiv_classes , variable odd_iteration_flag is used to indicate if it is the odd-th time of iteration. This flag controls filling and switching of equiv_class_num_1 and equiv_class_num_2 . equiv_class_num_1 and equiv_class_num_2 are used to record the No. of equivalent states. This No. is unique for every equivalent states set, and is used as differentiation for equivalent states sets. We will see later why needs the two variables.
partition_equiv_class at line 6236 in evaluate_equiv_classes , splits the original assuming equivalent states set into real equivalent states sets.
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 }
In algorithm mentioned brefore, rest states of the assuming equivlant states set will be compared with first state to find out the different ones. As a short cut, if the outlet numbers are different between two states, the states are considered as different. set_out_arc_insns_equiv_num finds out the outlet number for the first state at line 6165 in partition_equiv_class . At the same time arc->insn->insn_reserv_decl->equiv_class_num, arc->insn->insn_reserv_decl->state_alts are set to indicate arc been processed. Note, at here, arc->insn doesn’t represent any specified insn, its meaning is only demonstrated in here treatment.
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 }
The automaton now is DFA, so in the FOR loop at line 6015, every arc must associate with different state. We set equiv_class_num of all arcs associating the state at line 6021, which will be one of proofs for state equivalence.
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 }
If state and another_state is equivalent, that means:
1) They reaches same sets of equivalent target states
2) They use the same units’ reservation
3) They have same sets of equivalent source states
state_is_differed guarantees the first two conditions, while the iteration of the split sets in evaluate_equiv_classes makes the second condition satisfied. That is why in init_equiv_class rerrange states in reverse order, which can make the latest target state at beginning, and backtracks to its source states.
If states are judged as not equivalent, after back partition_equiv_class , all states not equivalent with first_state are put in the same set, and share a same equiv_class_num. Note assignment from line 6182 to 6185, when odd_iteration_flag is true, equiv_class_num_2 is assigned (the initial value is 2), just opposite to above (at that time, it is equiv_class_num_1 that in used.
That set is then processed in FOR loop at line 6175 in partition_equiv_class (it is assigned to first_state at line 6159. The set is further split by the first state (note the invocation of set_out_arc_insns_equiv_num ), as during which odd_iteration_flag is unchanged, so every separate sets has the increasing equiv_class_num_2 (assuming equiv_class_num_1 is in used, which is unchanged here too).
Back evaluate_equiv_classes , it updates equiv_class_num_1 or equiv_class_num_2 (depends on odd_iteration_flag , which is used to distinguish sets) for all new sets. Then call partition_equiv_class for every new set again for the partitiion.
The procedure is repeated unitl no new set is generated.
At line 6192 in partition_equiv_class , when certain equivalent set has been handled, related fields of the arc should be restored to separate the set from the rest (state_is_differed always returns at line 6099).
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 }
After finding out the equivalent classes, at line 6364 in minimize_DFA , we can merge the equivalent states tegother to produce the minimized automaton by 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 works very similar as create_composed_state . Above in FOR loop at line 6261, for every equivalent class, a new state is created, and all states belonging to the class are sorted and linked into component_states . Pay attention to line 6275 and 6299, for every state, its equiv_class_state field is set with new state.
Following in the FOR loop at line 6300, first creating arc for transition to/from new states (FOR loop at line 6308 handles it, notice that add_arc will not create arc if it has been created), then removes arcs for transition to/from obsolete states.
After merging, build_automaton returns back to create_automata , enumerate_states finds out the total number of 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 }
Now we have built the minimized DFA, and all equivalent states have been merged. We need to group instruction classes according to the equivalent state classes, as instruction class belonging to the same equivalent class can be handled in the same way. It is done at line 6893 in create_automata by 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 }
See in form_ainsn_with_same_reservs , first_insn_with_same_reservs field is set if the ainsn is the first of those with same unit reservation or if the instruction is advance_cycle_insn_decl which forms a set only containing itself. At line 6598, insert_ainsn_into_equiv_class links these instruction classes tegother via 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 }
At this step, we get following data structure. In the figure, in the list in red, its nodes represents different classes of equivalent instruction set, while the lists in blue are formed by equivalent instruction set individually.
figure 80 : grouping instruction into equivalent class, stage 2
As the list formed by next_same_reservs_insn is built by form_ainsn_with_same_reservs , after that by processing in NDFA_to_DFA and minimize_DFA , this list is obselete. So, next it needs process_state_for_insn_equiv_partition to repartite the equivalent instruction sets.
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 }
Remember that first_out_arc and next_out_arc all return the transition out from the state and arc has one-to-one correspondence with (source state, target state, insn) pair, the checking is done by process_insn_equiv_class at line 6597 above per 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 }
We know that using same unit reservation is the necessary condtion for being equivalent instruction class, so no class of equivalent instruction set gotten in minimize_DFA can span lists of next_same_reservs_insn constructed in form_ainsn_with_same_reservs , on the contractly, one of the lists may be split into several lists. Then in rest part of set_insn_equiv_classes , it reassigns new equiv_classes_num.
figure 81 : grouping instruction into equivalent class, stage 3