3.3.2. 重组识别树
回到 main ,下来调用 process_tree 来识别这棵树。它将尝试简化这棵树,并把节点赋予用于输出的函数。
2582 static void in genreocg.c
2583 process_tree (struct decision_head *head, enum routine_type subroutine_type)
2584 {
2585 if (head->first == NULL)
2586 {
2587 /* We can elide peephole2_insns, but not recog or split_insns. */
2588 if (subroutine_type == PEEPHOLE2)
2589 return ;
2590 }
2591 else
2592 {
2593 factor_tests (head);
2594
2595 next_subroutine_number = 0;
2596 break_out_subroutines (head, 1);
2597 find_afterward (head, NULL);
2598
2599 /* We run this after find_afterward, because find_afterward needs
2600 the redundant DT_mode tests on predicates to determine whether
2601 two tests can both be true or not. */
2602 simplify_tests (head);
2603
2604 write_subroutines (head, subroutine_type);
2605 }
2606
2607 write_subroutine (head, subroutine_type);
2608 }
注意到对于我们这里的例子,参数 head 指向 recog_trees ,而参数 subroutine_type 是 RECOG 。显然,我们的 recog_trees 不是空的。那么调用 factor_tests 。它将比较相邻的兄弟节点,提取出它们中公共的测试部分,并这部分构建新的 decision 节点。
1481 static void
1482 factor_tests (struct decision_head *head) in genreocg.c
1483 {
1484 struct decision *first, *next;
1485
1486 for (first = head->first; first && first->next; first = next)
1487 {
1488 enum decision_type type;
1489 struct decision *new, *old_last;
1490
1491 type = first->tests->type;
1492 next = first->next;
1493
1494 /* Want at least two compatible sequential nodes. */
1495 if (next->tests->type != type)
1496 continue ;
1497
1498 /* Don't want all node types, just those we can turn into
1499 switch statements. */
1500 if (type != DT_mode
1501 && type != DT_code
1502 && type != DT_veclen
1503 && type != DT_elt_zero_int
1504 && type != DT_elt_one_int
1505 && type != DT_elt_zero_wide_safe)
1506 continue ;
1507
1508 /* If we'd been performing more than one test, create a new node
1509 below our first test. */
1510 if (first->tests->next != NULL)
1511 {
1512 new = new_decision (first->position, &first->success);
1513 new->tests = first->tests->next;
1514 first->tests->next = NULL;
1515 }
1516
1517 /* Crop the node tree off after our first test. */
1518 first->next = NULL;
1519 old_last = head->last;
1520 head->last = first;
1521
1522 /* For each compatible test, adjust to perform only one test in
1523 the top level node, then merge the node back into the tree. */
1524 do
1525 {
1526 struct decision_head h;
1527
1528 if (next->tests->next != NULL)
1529 {
1530 new = new_decision (next->position, &next->success);
1531 new->tests = next->tests->next;
1532 next->tests->next = NULL;
1533 }
1534 new = next;
1535 next = next->next;
1536 new->next = NULL;
1537 h.first = h.last = new;
1538
1539 merge_trees (head, &h);
1540 }
1541 while (next && next->tests->type == type);
1542
1543 /* After we run out of compatible tests, graft the remaining nodes
1544 back onto the tree. */
1545 if (next)
1546 {
1547 next->prev = head->last;
1548 head->last->next = next;
1549 head->last = old_last;
1550 }
1551 }
1552
1553 /* Recurse. */
1554 for (first = head->first; first; first = first->next)
1555 factor_tests (&first->success);
1556 }
对于没有兄弟的 decision 节点,这个函数没有作用,直到我们来到下面的节点。
图 18 : factor_tree 函数,图 1
上面在 1486 行, head->first 指向感兴趣节点的 first 链中的节点,其第一个子节点是 DT_mode 类型。而在其 next 链中的节点也具有 DT_mode 类型的第一个子节点。 1495 及 1500 行的测试通过。因此类型 DT_mode 的 tests 的子节点被提取入一个新的 decision 节点,在调用 merge_trees 之前,我们可以得到以下我们例子的树。注意到绿色的节点,从 recog_trees 出发,是不可到达的。不过,这些节点现在还是可以访问的,因为我们持有指向它们的指针。
图 19 : factor_tree 函数,图 2
注意到感兴趣的节点是 merge_trees 的第一个参数,而 new 节点是第二个参数。显然,在 1438 行,对所检查的节点 nodes_identical 返回 1 。而 merge_accept_insn ,对于这些节点则不做任何事。那么 merge_trees 再次如下图所示递归。
图 20 : merge_trees 函数,图 1
现在我们具有如下的调用栈:
merge_trees (from factor_tests )
|-- à merge_trees (recurse)
|-- à merge_trees (recurse)
对于感兴趣的节点, nodes_identical 返回 0 ,然后 maybe_both_true 也返回 0 ,因为两者的第一个 decision_test 节点都是 DT_code 类型, insert_before 保持为 null ,两个感兴趣的 decision 节点将被链接成兄弟,如下图所示。
图 21 : merge_trees 函数,图 2
随着最里层的 merge_trees 返回,在栈上方的 merge_trees 依次返回,最后回到 factor_test 。我们仍然在 factor_test 的 1486 到 1551 行的 FOR 循环中。看到在 1486 行, next 指向下图中所示的节点。
图 22 : merge_trees 函数,图 3
因为节点 next 的 next 域是 null ,我们立即从这个 FOR 循环中跳出。然后我们在 factor_test 的 1554 , 1555 行继续处理余下的节点,如 图 21 所示。重复上面的步骤,最后我们可以得到下面的图。
图 23 : factor_tree 函数,图 3
3.3.2. 把节点输出为子例程
回到 process_tree , break_out_subroutines 被调用来计算 HEAD 的子节点的数目。如果 initial 是 0 ,并且数目足够大,使得 HEAD 中的第一个节点,在所要产生 C 代码中,开始另一个函数。
1603 static int
1604 break_out_subroutines (struct decision_head *head, int initial) in genreocg.c
1605 {
1606 int size = 0;
1607 struct decision *sub;
1608
1609 for (sub = head->first; sub; sub = sub->next)
1610 size += 1 + break_out_subroutines (&sub->success, 0);
1611
1612 if (size > SUBROUTINE_THRESHOLD && ! initial)
1613 {
1614 head->first->subroutine_number = ++next_subroutine_number ;
1615 size = 1;
1616 }
1617 return size;
1618 }
出于演示的目的,对于我们的例子,假定上面的 SUBROUTINE_THRESHOLD 是 4 。那么我们可以得到如下的图形。注意图中 size 及 subroutine_number 的值。
图 24 : process_tree 函数,图 1
接着是 find_afterward 。我们已经看到 factor_tests 将提取相邻兄弟节点的公共测试部分,而 find_afterward 将遍历所有的兄弟节点,并把匹配的节点链接在一起。对于来自 process_tree 的调用,其第二个参数 real_afterward 是 null 。
1623 static void
1624 find_afterward (struct decision_head *head, struct decision *real_afterward) in genreocg.c
1625 {
1626 struct decision *p, *q, *afterward;
1627
1628 /* We can't propagate alternatives across subroutine boundaries.
1629 This is not incorrect, merely a minor optimization loss. */
1630
1631 p = head->first;
1632 afterward = (p->subroutine_number > 0 ? NULL : real_afterward);
1633
1634 for ( ; p ; p = p->next)
1635 {
1636 /* Find the next node that might be true if this one fails. */
1637 for (q = p->next; q ; q = q->next)
1638 if (maybe_both_true (p, q, 1))
1639 break ;
1640
1641 /* If we reached the end of the list without finding one,
1642 use the incoming afterward position. */
1643 if (!q)
1644 q = afterward;
1645 p->afterward = q;
1646 if (q)
1647 q->need_label = 1;
1648 }
1649
1650 /* Recurse. */
1651 for (p = head->first; p ; p = p->next)
1652 if (p->success.first)
1653 find_afterward (&p->success, p->afterward);
1654
1655 /* When we are generating a subroutine, record the real afterward
1656 position in the first node where write_tree can find it, and we
1657 can do the right thing at the subroutine call site. */
1658 p = head->first;
1659 if (p->subroutine_number > 0)
1660 p->afterward = real_afterward;
1661 }
对于没有兄弟的 decision 节点, find_afterward 只是把其 afterward 填成 null 。我们使用上面产生的树作为例子,当来到下面的节点时,我们看到以下的图。
图 25 : process_tree 函数,图 2
对于这两个节点, maybe_both_true 在 1638 行返回 0 。该函数的目的是找出可能同时为 true 的兄弟节点。作为结果,所有我们例子中的节点都具有为 null 的 afterward 。在提取公共部分之后, simplify_tests 被调用来简化任一节点上的测试。
1566 static void
1567 simplify_tests (struct decision_head *head) in genreocg.c
1568 {
1569 struct decision *tree;
1570
1571 for (tree = head->first; tree; tree = tree->next)
1572 {
1573 struct decision_test *a, *b;
1574
1575 a = tree->tests;
1576 b = a->next;
1577 if (b == NULL)
1578 continue ;
1579
1580 /* Find a predicate node. */
1581 while (b && b->type != DT_pred)
1582 b = b->next;
1583 if (b)
1584 {
1585 /* Due to how these tests are constructed, we don't even need
1586 to check that the mode and code are compatible -- they were
1587 generated from the predicate in the first place. */
1588 while (a->type == DT_mode || a->type == DT_code)
1589 a = a->next;
1590 tree->tests = a;
1591 }
1592 }
1593
1594 /* Recurse. */
1595 for (tree = head->first; tree; tree = tree->next)
1596 simplify_tests (&tree->success);
1597 }
这个函数的代码相当简单。它移除断言中重复的 mode 测试或 code 测试。之后,我们可以得到以下的树。