操持完 PCH 文件,回到 finish_file 中继续为汇编代码的生成而奋斗。接下来编译器将为基本类型准备 tinfo 。
finish_file (continue)
2546 /* Otherwise, GDB can get confused, because in only knows
2547 about source for LINENO-1 lines. */
2548 input_line -= 1;
2549
2550 interface_unknown = 1;
2551 interface_only = 0;
2552
2553 /* We now have to write out all the stuff we put off writing out.
2554 These include:
2555
2556 o Template specializations that we have not yet instantiated,
2557 but which are needed.
2558 o Initialization and destruction for non-local objects with
2559 static storage duration. (Local objects with static storage
2560 duration are initialized when their scope is first entered,
2561 and are cleaned up via atexit.)
2562 o Virtual function tables.
2563
2564 All of these may cause others to be needed. For example,
2565 instantiating one function may cause another to be needed, and
2566 generating the initializer for an object may cause templates to be
2567 instantiated, etc., etc. */
2568
2569 timevar_push (TV_VARCONST);
2570
2571 emit_support_tinfos ();
所有这样的 tinfo 将可以在名字空间 abi_node 中找到。在 get_tinfo_decl 中,这些 tinfo 将被串入 unemitted_tinfo_decls 。注意到数组 fundamentals 的内容是基本类型的节点,它们是在建立编译器环境时构建的。
1353 void
1354 emit_support_tinfos (void) in rtti.c
1355 {
1356 static tree *const fundamentals[] =
1357 {
1358 &void_type_node,
1359 &boolean_type_node,
1360 &wchar_type_node,
1361 &char_type_node, &signed_char_type_node, &unsigned_char_type_node,
1362 &short_integer_type_node, &short_unsigned_type_node,
1363 &integer_type_node, &unsigned_type_node,
1364 &long_integer_type_node, &long_unsigned_type_node,
1365 &long_long_integer_type_node, &long_long_unsigned_type_node,
1366 &float_type_node, &double_type_node, &long_double_type_node,
1367 0
1368 };
1369 int ix;
1370 tree bltn_type, dtor;
1371
1372 push_nested_namespace (abi_node );
1373 bltn_type = xref_tag (class_type,
1374 get_identifier ("__fundamental_type_info"),
1375 true, false);
1376 pop_nested_namespace (abi_node );
1377 if (!COMPLETE_TYPE_P (bltn_type))
1378 return ;
1379 dtor = TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (bltn_type), 1);
1380 if (DECL_EXTERNAL (dtor))
1381 return ;
1382 doing_runtime = 1;
1383 for (ix = 0; fundamentals[ix]; ix++)
1384 {
1385 tree bltn = *fundamentals[ix];
1386 tree bltn_ptr = build_pointer_type (bltn);
1387 tree bltn_const_ptr = build_pointer_type
1388 (build_qualified_type (bltn, TYPE_QUAL_CONST));
1389 tree tinfo;
1390
1391 tinfo = get_tinfo_decl (bltn);
1392 TREE_USED (tinfo) = 1;
1393 TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (tinfo)) = 1;
1394
1395 tinfo = get_tinfo_decl (bltn_ptr);
1396 TREE_USED (tinfo) = 1;
1397 TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (tinfo)) = 1;
1398
1399 tinfo = get_tinfo_decl (bltn_const_ptr);
1400 TREE_USED (tinfo) = 1;
1401 TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (tinfo)) = 1;
1402 }
1403 }
看到对于每个基本类型,需要产生 3 个 tinfo , 一个对应朴素( plain )类型,一个对应指针,还有一个对应常量指针。
下面的庞大的 DO WHILE 循环不断重复,直到没有任何相关的东西发生改变。在 2582 行, instantiate_pending_templates 具现仍未了结的模板。在前端内部,一个全局的 tree_list 链表 pending_tempaltes 保存了所有具现被延迟的模板,它们要么定义还未知,要么我们推迟了这个具现。每个节点的 tree_purose 或者是一个 DECL (对于一个函数或者静态数据成员),或一个 TYPE (对于一个类),它代表我们所期望的具现形式。而 tree_value 不被使用。
finish_file (continue)
2573 do
2574 {
2575 tree t;
2576 size_t n_old, n_new;
2577
2578 reconsider = false;
2579
2580 /* If there are templates that we've put off instantiating, do
2581 them now. */
2582 instantiate_pending_templates ();
2583 ggc_collect ();
2584
2585 /* Write out virtual tables as required. Note that writing out
2586 the virtual table for a template class may cause the
2587 instantiation of members of that class. If we write out
2588 vtables then we remove the class from our list so we don't
2589 have to look at it again. */
2590
2591 while (keyed_classes != NULL_TREE
2592 && maybe_emit_vtables (TREE_VALUE (keyed_classes )))
2593 {
2594 reconsider = true;
2595 keyed_classes = TREE_CHAIN (keyed_classes );
2596 }
2597
2598 t = keyed_classes ;
2599 if (t != NULL_TREE)
2600 {
2601 tree next = TREE_CHAIN (t);
2602
2603 while (next)
2604 {
2605 if (maybe_emit_vtables (TREE_VALUE (next)))
2606 {
2607 reconsider = true;
2608 TREE_CHAIN (t) = TREE_CHAIN (next);
2609 }
2610 else
2611 t = next;
2612
2613 next = TREE_CHAIN (t);
2614 }
2615 }
2616
2617 /* Write out needed type info variables. We have to be careful
2618 looping through unemitted decls, because emit_tinfo_decl may
2619 cause other variables to be needed. We stick new elements
2620 (and old elements that we may need to reconsider) at the end
2621 of the array, then shift them back to the beginning once we're
2622 done. */
2623
2624 n_old = VARRAY_ACTIVE_SIZE (unemitted_tinfo_decls );
2625 for (i = 0; i < n_old; ++i)
2626 {
2627 tree tinfo_decl = VARRAY_TREE (unemitted_tinfo_decls , i);
2628 if (emit_tinfo_decl (tinfo_decl))
2629 reconsider = true;
2630 else
2631 VARRAY_PUSH_TREE (unemitted_tinfo_decls , tinfo_decl);
2632 }
2633
2634 /* The only elements we want to keep are the new ones. Copy
2635 them to the beginning of the array, then get rid of the
2636 leftovers. */
2637 n_new = VARRAY_ACTIVE_SIZE (unemitted_tinfo_decls ) - n_old;
2638 if (n_new)
2639 memmove (&VARRAY_TREE (unemitted_tinfo_decls , 0),
2640 &VARRAY_TREE (unemitted_tinfo_decls , n_old),
2641 n_new * sizeof (tree));
2642 memset (&VARRAY_TREE (unemitted_tinfo_decls , n_new),
2643 0, n_old * sizeof (tree));
2644 VARRAY_ACTIVE_SIZE (unemitted_tinfo_decls ) = n_new;
2645
2646 /* The list of objects with static storage duration is built up
2647 i n reverse order. We clear STATIC_AGGREGATES so that any new
2648 aggregates added during the initialization of these will be
2649 initialized in the correct order when we next come around the
2650 loop. */
2651 vars = prune_vars_needing_no_initialization (&static_aggregates );
2652
2653 if (vars)
2654 {
2655 tree v;
2656
2657 /* We need to start a new initialization function each time
2658 through the loop. That's because we need to know which
2659 vtables have been referenced, and TREE_SYMBOL_REFERENCED
2660 isn't computed until a function is finished, and written
2661 out. That's a deficiency in the back-end. When this is
2662 fixed, these initialization functions could all become
2663 inline, with resulting performance improvements. */
2664 tree ssdf_body;
2665
2666 /* Set the line and file, so that it is obviously not from
2667 the source file. */
2668 input_location = locus;
2669 ssdf_body = start_static_storage_duration_function (ssdf_count);
2670
2671 /* Make sure the back end knows about all the variables. */
2672 write_out_vars (vars);
2673
2674 /* First generate code to do all the initializations. */
2675 for (v = vars; v; v = TREE_CHAIN (v))
2676 do_static_initialization (TREE_VALUE (v),
2677 TREE_PURPOSE (v));
2678
2679 /* Then, generate code to do all the destructions. Do these
2680 i n reverse order so that the most recently constructed
2681 variable is the first destroyed. If we're using
2682 __cxa_atexit, then we don't need to do this; functions
2683 were registered at initialization time to destroy the
2684 local statics. */
2685 if (!flag_use_cxa_atexit )
2686 {
2687 vars = nreverse (vars);
2688 for (v = vars; v; v = TREE_CHAIN (v))
2689 do_static_destruction (TREE_VALUE (v));
2690 }
2691 else
2692 vars = NULL_TREE;
2693
2694 /* Finish up the static storage duration function for this
2695 round. */
2696 input_location = locus;
2697 finish_static_storage_duration_function (ssdf_body);
2698
2699 /* All those initializations and finalizations might cause
2700 us to need more inline functions, more template
2701 instantiations, etc. */
2702 reconsider = true;
2703 ssdf_count++;
2704 locus.line++;
2705 }
2706
2707 for (i = 0; i < deferred_fns_used ; ++i)
2708 {
2709 tree decl = VARRAY_TREE (deferred_fns , i);
2710
2711 /* Does it need synthesizing? */
2712 if (DECL_ARTIFICIAL (decl) && ! DECL_INITIAL (decl)
2713 && TREE_USED (decl)
2714 && (! DECL_REALLY_EXTERN (decl) || DECL_INLINE (decl)))
2715 {
2716 /* Even though we're already at the top-level, we push
2717 there again. That way, when we pop back a few lines
2718 hence, all of our state is restored. Otherwise,
2719 finish_function doesn't clean things up, and we end
2720 up with CURRENT_FUNCTION_DECL set. */
2721 push_to_top_level ();
2722 synthesize_method (decl);
2723 pop_from_top_level ();
2724 reconsider = true;
2725 }
2726
2727 /* If the function has no body, avoid calling
2728 import_export_decl. On a system without weak symbols,
2729 calling import_export_decl will make an inline template
2730 instantiation "static", which will result in errors about
2731 the use of undefined functions if there is no body for
2732 the function. */
2733 if (!DECL_SAVED_TREE (decl))
2734 continue ;
2735
2736 import_export_decl (decl);
2737
2738 /* We lie to the back-end, pretending that some functions
2739 are not defined when they really are. This keeps these
2740 functions from being put out unnecessarily. But, we must
2741 stop lying when the functions are referenced, or if they
2742 are not comdat since they need to be put out now. This
2743 is done in a separate for cycle, because if some deferred
2744 function is contained in another deferred function later
2745 i n deferred_fns varray, rest_of_compilation would skip
2746 this function and we really cannot expand the same
2747 function twice. */
2748 if (DECL_NOT_REALLY_EXTERN (decl)
2749 && DECL_INITIAL (decl)
2750 && DECL_NEEDED_P (decl))
2751 DECL_EXTERNAL (decl) = 0;
2752
2753 /* If we're going to need to write this function out, and
2754 there's already a body for it, create RTL for it now.
2755 (There might be no body if this is a method we haven't
2756 gotten around to synthesizing yet.) */
2757 if (!DECL_EXTERNAL (decl)
2758 && DECL_NEEDED_P (decl)
2759 && DECL_SAVED_TREE (decl)
2760 && !TREE_ASM_WRITTEN (decl)
2761 && (!flag_unit_at_a_time
2762 || !cgraph_node (decl)->local.finalized))
2763 {
2764 /* We will output the function; no longer consider it in this
2765 loop. */
2766 DECL_DEFER_OUTPUT (decl) = 0;
2767 /* Generate RTL for this function now that we know we
2768 need it. */
2769 expand_or_defer_fn (decl);
2770 /* If we're compiling -fsyntax-only pretend that this
2771 function has been written out so that we don't try to
2772 expand it again. */
2773 if (flag_syntax_only )
2774 TREE_ASM_WRITTEN (decl) = 1;
2775 reconsider = true;
2776 }
2777 }
2778
2779 if (walk_namespaces (wrapup_globals_for_namespace , /*data=*/ 0))
2780 reconsider = true;
2781
2782 /* Static data members are just like namespace-scope globals. */
2783 for (i = 0; i < pending_statics_used ; ++i)
2784 {
2785 tree decl = VARRAY_TREE (pending_statics , i);
2786 if (var_finalized_p (decl))
2787 continue ;
2788 import_export_decl (decl);
2789 if (DECL_NOT_REALLY_EXTERN (decl) && ! DECL_IN_AGGR_P (decl))
2790 DECL_EXTERNAL (decl) = 0;
2791 }
2792 if (pending_statics
2793 && wrapup_global_declarations (&VARRAY_TREE (pending_statics , 0),
2794 pending_statics_used ))
2795 reconsider = true;
2796
2797 if (cgraph_assemble_pending_functions ())
2798 reconsider = true;
2799 }
2800 while (reconsider);
首先,使用中的 vtable 应该被标记,以使编译器为之产生代码。在前面的章节中,我们已经看到对于一个类,其 CLASSTYPE_VTABLES 可能包含了一串 vtable 。这依赖于其基类及其定义。而如果一个类包含了 vtable ,它也同样被记录在 keyed_classes 链表中。在 1565 行, primary_vtbl 指向 ctype 的 vtable 列表。
1556 static bool
1557 maybe_emit_vtables (tree ctype) in decl2.c
1558 {
1559 tree vtbl;
1560 tree primary_vtbl;
1561 bool needed = false;
1562
1563 /* If the vtables for this class have already been emitted there is
1564 nothing more to do. */
1565 primary_vtbl = CLASSTYPE_VTABLES (ctype);
1566 if (var_finalized_p (primary_vtbl))
1567 return false;
1568 /* Ignore dummy vtables made by get_vtable_decl. */
1569 if (TREE_TYPE (primary_vtbl) == void_type_node)
1570 return false;
1571
1572 import_export_class (ctype);
1573
1574 /* See if any of the vtables are needed. */
1575 for (vtbl = CLASSTYPE_VTABLES (ctype); vtbl; vtbl = TREE_CHAIN (vtbl))
1576 {
1577 import_export_vtable (vtbl, ctype, 1);
1578 if (!DECL_EXTERNAL (vtbl) && DECL_NEEDED_P (vtbl))
1579 break ;
1580 }
1581 if (!vtbl)
1582 {
1583 /* If the references to this class' vtables are optimized away,
1584 still emit the appropriate debugging information. See
1585 dfs_debug_mark. */
1586 if (DECL_COMDAT (primary_vtbl)
1587 && CLASSTYPE_DEBUG_REQUESTED (ctype))
1588 note_debug_info_needed (ctype);
1589 return false;
1590 }
1591 else if (TREE_PUBLIC (vtbl) && !DECL_COMDAT (vtbl))
1592 needed = true;
所有的 vtable 都被编译器声明为公有的 VAR_DECL ,这样类型的节点通过 cgraph_varpool_node 关联起来,在其中如果标记 finalized 被设置,就意味着已经为该节点输出代码。
1544 static bool
1545 var_finalized_p (tree var) in decl2.c
1546 {
1547 if (flag_unit_at_a_time )
1548 return cgraph_varpool_node (var)->finalized;
1549 else
1550 return TREE_ASM_WRITTEN (var);
1551 }
如果 flag_unit_at_a_time 不是 0 ,则为这个 VAR_DECL 构建一个 cgraph_varpool_node 。 在下面,哈希表 cgraph_varpool_hash 是 cgraph_varpool_node 的一个队列,它由 GC 管理。
441 struct cgraph_varpool_node *
442 cgraph_varpool_node (tree decl) in cgraph.c
443 {
444 struct cgraph_varpool_node *node;
445 struct cgraph_varpool_node **slot;
446
447 if (!DECL_P (decl) || TREE_CODE (decl) == FUNCTION_DECL)
448 abort ();
449
450 if (!cgraph_varpool_hash)
451 cgraph_varpool_hash = htab_create_ggc (10, cgraph_varpool_hash_node,
452 eq_cgraph_varpool_node, NULL);
453 slot = (struct cgraph_varpool_node **)
454 htab_find_slot_with_hash (cgraph_varpool_hash , DECL_ASSEMBLER_NAME (decl),
455 IDENTIFIER_HASH_VALUE (DECL_ASSEMBLER_NAME (decl)),
456 INSERT);
457 if (*slot)
458 return *slot;
459 node = ggc_alloc_cleared (sizeof (*node));
460 node->decl = decl;
461 cgraph_varpool_n_nodes ++;
462 cgraph_varpool_nodes = node;
463 *slot = node;
464 return node;
465 }
而结构体 cgraph_varpool_node 具有以下的定义。
134 struct cgraph_varpool_node GTY(()) in cgraph.h
135 {
136 tree decl;
137 /* Pointer to the next function in cgraph_varpool_nodes_queue. */
138 struct cgraph_varpool_node *next_needed;
139
140 /* Set when function must be output - it is externally visible
141 or it's address is taken. */
142 bool needed;
143 /* Set once it has been finalized so we consider it to be output. */
144 bool finalized;
145 /* Set when function is scheduled to be assembled. */
146 bool output;
147 };
上面的 1572 行, import_export_class 确定是否需要在这个编译单元发布该类的整套内容;或者只是发布一个签名,而在链接时刻解析这些引用。
记得编译选项 -fno-implicit-templates 表示不要为隐式具现的非内联模板发布代码。对于这个情形,我们设置 import_export 为 -1 要限制其代码发布。
接着需要确定指定的 vtable 是否是外部的(即便类本身是外部定义的,一旦其某一基类是定义在这个编译单元中,并在这里发布 vtable ,该类的 vtable 也需要在这里发布)。
1442 void
1443 import_export_vtable (tree decl, tree type, int final) in decl2.c
1444 {
1445 if (DECL_INTERFACE_KNOWN (decl))
1446 return ;
1447
1448 if (TYPE_FOR_JAVA (type))
1449 {
1450 TREE_PUBLIC (decl) = 1;
1451 DECL_EXTERNAL (decl) = 1;
1452 DECL_INTERFACE_KNOWN (decl) = 1;
1453 }
1454 else if (CLASSTYPE_INTERFACE_KNOWN (type))
1455 {
1456 TREE_PUBLIC (decl) = 1;
1457 DECL_EXTERNAL (decl) = CLASSTYPE_INTERFACE_ONLY (type);
1458 DECL_INTERFACE_KNOWN (decl) = 1;
1459 }
1460 else
1461 {
1462 /* We can only wait to decide if we have real non-inline virtual
1463 functions in our class, or if we come from a template. */
1464
1465 int found = (CLASSTYPE_TEMPLATE_INSTANTIATION (type)
1466 || CLASSTYPE_KEY_METHOD (type) != NULL_TREE);
1467
1468 if (final || ! found)
1469 {
1470 comdat_linkage (decl);
1471 DECL_EXTERNAL (decl) = 0;
1472 }
1473 else
1474 {
1475 TREE_PUBLIC (decl) = 1;
1476 DECL_EXTERNAL (decl) = 1;
1477 }
1478 }
1479 }
在函数 import_export_vtable 为代表 vtable 的 VAR_DECL 设置了相应标记后, DECL_NEEDED_P 分辨这个 vtable 是否需要发布。断言 DECL_COMDAT 如果成立,表示就是具有 TREE_PUBLIC 属性,也不需要发布,除非这个编译单元需要。类似这样的实体在编译单元间共享(类似弱实体),但保证为任何需要它们的编译单元所产生, 因此不需要在不需要它们的地方发布。 DECL_COMDAT 只是给后端的一个暗示;由前端自由设置这个标记,它保证除了发布 DECL_COMDAT 的对象使得代码膨胀外,对代码没有别的坏处。
1730 #define DECL_NEEDED_P (DECL) / in cp-tree.h
1731 ((at_eof && TREE_PUBLIC (DECL) && !DECL_COMDAT (DECL)) /
1732 || (DECL_ASSEMBLER_NAME_SET_P (DECL) /
1733 && TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (DECL)))/
1734 || (((flag_syntax_only || flag_unit_at_a_time ) && TREE_USED (DECL))))
如果 vtable 列表中有任一 vtable 需要发布代码,编译器将为所有的 vtable 发布代码。在这里标记它们。
maybe_emit_vtables (continue)
1595 /* The ABI requires that we emit all of the vtables if we emit any
1596 of them. */
1597 for (vtbl = CLASSTYPE_VTABLES (ctype); vtbl; vtbl = TREE_CHAIN (vtbl))
1598 {
1599 /* Write it out. */
1600 import_export_vtable (vtbl, ctype, 1);
1601 mark_vtable_entries (vtbl);
1602
1603 /* If we know that DECL is needed, mark it as such for the varpool. */
1604 if (needed)
1605 cgraph_varpool_mark_needed_node (cgraph_varpool_node (vtbl));
1606
1607 if (TREE_TYPE (DECL_INITIAL (vtbl)) == 0)
1608 {
1609 /* It had better be all done at compile-time. */
1610 if (store_init_value (vtbl, DECL_INITIAL (vtbl)))
1611 abort ();
1612 }
1613
1614 if (write_symbols == DWARF_DEBUG || write_symbols == DWARF2_DEBUG)
1615 {
1616 /* Mark the VAR_DECL node representing the vtable itself as a
1617 "gratuitous" one, thereby forcing dwarfout.c to ignore it.
1618 It is rather important that such things be ignored because
1619 any effort to actually generate DWARF for them will run
1620 into trouble when/if we encounter code like:
1621
1622 #pragma interface
1623 struct S { virtual void member (); };
1624
1625 because the artificial declaration of the vtable itself (as
1626 manufactured by the g++ front end) will say that the vtable
1627 is a static member of `S' but only *after* the debug output
1628 for the definition of `S' has already been output. This causes
1629 grief because the DWARF entry for the definition of the vtable
1630 will try to refer back to an earlier *declaration* of the
1631 vtable as a static member of `S' and there won't be one.
1632 We might be able to arrange to have the "vtable static member"
1633 attached to the member list for `S' before the debug info for
1634 `S' get written (which would solve the problem) but that would
1635 require more intrusive changes to the g++ front end. */
1636
1637 DECL_IGNORED_P (vtbl) = 1;
1638 }
1639
1640 /* Always make vtables weak. */
1641 if (flag_weak )
1642 comdat_linkage (vtbl);
1643
1644 rest_of_decl_compilation (vtbl, NULL, 1, 1);
1645
1646 /* Because we're only doing syntax-checking, we'll never end up
1647 actually marking the variable as written. */
1648 if (flag_syntax_only )
1649 TREE_ASM_WRITTEN (vtbl) = 1;
1650 }
1651
1652 /* Since we're writing out the vtable here, also write the debug
1653 info. */
1654 note_debug_info_needed (ctype);
1655
1656 return true;
1657 }
下面的函数只是把虚函数标记为可寻址,并设置 in-used 标记来表示编译器需要在后面发布代码。记得总是为 vtable 的初始值构建 CONSTRUCTOR 节点,在调试模式中 CONSTRUCTOR_ELTS 可以执行额外的检查来保证这确实是 CONSTRUCTOR 实体,并获取 vtable 的初始值(参考 initialize_vtable 或 build_vtt 中的 initialize_array 。至于初始值的例子,参考 5.12.5.2.2.2.1.3.11.完成派生类 RECORD_TYPE – 完成 vtable 一节)。
1323 static void
1324 mark_vtable_entries (tree decl) in decl2.c
1325 {
1326 tree entries = CONSTRUCTOR_ELTS (DECL_INITIAL (decl));
1327
1328 for (; entries; entries = TREE_CHAIN (entries))
1329 {
1330 tree fnaddr = TREE_VALUE (entries);
1331 tree fn;
1332
1333 STRIP_NOPS (fnaddr);
1334
1335 if (TREE_CODE (fnaddr) != ADDR_EXPR
1336 && TREE_CODE (fnaddr) != FDESC_EXPR)
1337 /* This entry is an offset: a virtual base class offset, a
1338 virtual call offset, an RTTI offset, etc. */
1339 continue ;
1340
1341 fn = TREE_OPERAND (fnaddr, 0);
1342 TREE_ADDRESSABLE (fn) = 1;
1343 /* When we don't have vcall offsets, we output thunks whenever
1344 we output the vtables that contain them. With vcall offsets,
1345 we know all the thunks we'll need when we emit a virtual
1346 function, so we emit the thunks there instead. */
1347 if (DECL_THUNK_P (fn))
1348 use_thunk (fn, /*emit_p=*/ 0);
1349 mark_used (fn);
1350 }
1351 }
把 decl 标记为在用可以帮助编译器在后面的优化中移除不使用的代码。这个标记在下面的 2982 行设置。而对于未发布的内联函数(一般而言,除了主管构造函数及析构函数外,内联函数此时都未发布。考虑内联函数定义在头文件中,而头文件可能在同一个编译单元不同的源文件中多处被引用,把它的处理推迟到前端最后完成编译单元的时刻,很有必要。虽然此处调用 mark_used 的地方其实已经前端所执行的最后阶段,但 mark_used 在前端中很多地方被调用),把它缓存入 deferred_fns 。在 2991 行,对于 x86 机器上的 Linux , assemble_external 是无关重要的。在前面的章节中,我们已经看到编译器会“人工”产生方法(比如,类里的克隆构造函数及析构函数),这样的方法不会自己运行,而是通过用户定义的方法来触发(由 current_function_decl 指向)。在 2998 行,如果 DECL_INITIAL 是空的,这意味着函数还没有被调用序列: start_function , begin_function_body , begin_compound_stmt || finish_compound_stmt ] , finish_function_body , finish_function 所处理——即没有构建相应的 STMT 节点,并把它链入其触发者的 STMT 链表中。而在下面, synthesize_method 调用上面提及的调用序列来为这些函数产生必要的 STMT 节点,仿佛它来自用户的定义。接着,在 2983 行的 skip_evaluation 被解析器设置。在 C++ 中,存在不要求评估( evalutaion )的声明或表达式,例如,在操作符 sizeof 或 typeof 中的表达式。
2967 void
2968 mark_used (tree decl) in decl2.c
2969 {
2970 /* If DECL is a BASELINK for a single function, then treat it just
2971 like the DECL for the function. Otherwise, if the BASELINK is
2972 for an overloaded function, we don't know which function was
2973 actually used until after overload resolution. */
2974 if (TREE_CODE (decl) == BASELINK)
2975 {
2976 decl = BASELINK_FUNCTIONS (decl);
2977 if (really_overloaded_fn (decl))
2978 return ;
2979 decl = OVL_CURRENT (decl);
2980 }
2981
2982 TREE_USED (decl) = 1;
2983 if (processing_template_decl || skip_evaluation )
2984 return ;
2985
2986 if (TREE_CODE (decl) == FUNCTION_DECL && DECL_DECLARED_INLINE_P (decl)
2987 && !TREE_ASM_WRITTEN (decl))
2988 /* Remember it, so we can check it was defined. */
2989 defer_fn (decl);
2990
2991 assemble_external (decl);
2992
2993 /* Is it a synthesized method that needs to be synthesized? */
2994 if (TREE_CODE (decl) == FUNCTION_DECL
2995 && DECL_NONSTATIC_MEMBER_FUNCTION_P (decl)
2996 && DECL_ARTIFICIAL (decl)
2997 && !DECL_THUNK_P (decl)
2998 && ! DECL_INITIAL (decl)
2999 /* Kludge: don't synthesize for default args. */
3000 && current_function_decl )
3001 {
3002 synthesize_method (decl);
3003 /* If we've already synthesized the method we don't need to
3004 instantiate it, so we can return right away. */
3005 return ;
3006 }
3007
3008 /* If this is a function or variable that is an instance of some
3009 template, we now know that we will need to actually do the
3010 instantiation. We check that DECL is not an explicit
3011 instantiation because that is not checked in instantiate_decl. */
3012 if ((DECL_NON_THUNK_FUNCTION_P (decl) || TREE_CODE (decl) == VAR_DECL)
3013 && DECL_LANG_SPECIFIC (decl) && DECL_TEMPLATE_INFO (decl)
3014 && (!DECL_EXPLICIT_INSTANTIATION (decl)
3015 || (TREE_CODE (decl) == FUNCTION_DECL
3016 && DECL_INLINE (DECL_TEMPLATE_RESULT
3017 (template_for_substitution (decl))))))
3018 {
3019 bool defer;
3020
3021 /* Normally, we put off instantiating functions in order to
3022 improve compile times. Maintaining a stack of active
3023 functions is expensive, and the inliner knows to
3024 instantiate any functions it might need.
3025
3026 However, if instantiating this function might help us mark
3027 the current function TREE_NOTHROW, we go ahead and
3028 instantiate it now.
3029
3030 This is not needed for unit-at-a-time since we reorder the functions
3031 i n topological order anyway.
3032 */
3033 defer = (!flag_exceptions
3034 || flag_unit_at_a_time
3035 || !optimize
3036 || TREE_CODE (decl) != FUNCTION_DECL
3037 /* If the called function can't throw, we don't need to
3038 generate its body to find that out. */
3039 || TREE_NOTHROW (decl)
3040 || !cfun
3041 || !current_function_decl
3042 /* If we already know the current function can't throw,
3043 then we don't need to work hard to prove it. */
3044 || TREE_NOTHROW (current_function_decl )
3045 /* If we already know that the current function *can*
3046 throw, there's no point in gathering more
3047 information. */
3048 || cp_function_chain ->can_throw);
3049
3050 instantiate_decl (decl, defer);
3051 }
3052 }
我们已经看过 thunk 是一个编译器产生的函数,也应该为它发布代码。不过,在这次调用中,参数 emit_p 是 false ,结果仅是把 thunk 标记为在用,而没有真正为之发布代码。
336 void
337 use_thunk (tree thunk_fndecl, bool emit_p) in method.c
338 {
339 tree function, alias;
340 tree virtual_offset;
341 HOST_WIDE_INT fixed_offset, virtual_value;
342 bool this_adjusting = DECL_THIS_THUNK_P (thunk_fndecl);
343
344 /* We should have called finish_thunk to give it a name. */
345 my_friendly_assert (DECL_NAME (thunk_fndecl), 20021127);
346
347 /* We should never be using an alias, always refer to the
348 aliased thunk. */
349 my_friendly_assert (!THUNK_ALIAS (thunk_fndecl), 20031023);
350
351 if (TREE_ASM_WRITTEN (thunk_fndecl))
352 return ;
353
354 function = THUNK_TARGET (thunk_fndecl);
355 if (DECL_RESULT (thunk_fndecl))
356 /* We already turned this thunk into an ordinary function.
357 There's no need to process this thunk again. */
358 return ;
359
360 if (DECL_THUNK_P (function))
361 /* The target is itself a thunk, process it now. */
362 use_thunk (function, emit_p);
363
364 /* Thunks are always addressable; they only appear in vtables. */
365 TREE_ADDRESSABLE (thunk_fndecl) = 1;
366
367 /* Figure out what function is being thunked to. It's referenced in
368 this translation unit. */
369 TREE_ADDRESSABLE (function) = 1;
370 mark_used (function);
371 if (!emit_p)
372 return ;
…
524 }
如果该 vtable 是公有的,而且不是编译单元间共享的,在 maybe_emit_vtables 的 1592 行设置 needed ,然后在 1605 行,调用下面的函数来设置相关联的 cgraph_varpool_node 节点的 needed 标记。
568 void
569 cgraph_varpool_mark_needed_node (struct cgraph_varpool_node *node) in cgraph.c
570 {
571 if (!node->needed && node->finalized)
572 {
573 node->next_needed = cgraph_varpool_nodes_queue ;
574 cgraph_varpool_nodes_queue = node;
575 notice_global_symbol (node->decl);
576 }
577 node->needed = 1;
578 }
接着在 maybe_emit_vtables 的 1644 行,调用 rest_of_decl_compilation 根据中间节点来构建对应的 RTL 节点,并如果需要,发布汇编代码(标签定义,存储分配及初始化)。在这里,参数 top_level 及 at_end 都是 1 ,而 asmspec 为 NULL 。注意到在 1957 行,对于 x86/Linux ,没有定义 ASM_FINISH_DECLARE_OBJECT 。
1910 void
1911 rest_of_decl_compilation (tree decl, in toplev.c
1912 const char *asmspec,
1913 int top_level,
1914 int at_end)
1915 {
1916 /* We deferred calling assemble_alias so that we could collect
1917 other attributes such as visibility. Emit the alias now. */
1918 {
1919 tree alias;
1920 alias = lookup_attribute ("alias", DECL_ATTRIBUTES (decl));
1921 if (alias)
1922 {
1923 alias = TREE_VALUE (TREE_VALUE (alias));
1924 alias = get_identifier (TREE_STRING_POINTER (alias));
1925 assemble_alias (decl, alias);
1926 }
1927 }
1928
1929 /* Forward declarations for nested functions are not "external",
1930 but we need to treat them as if they were. */
1931 if (TREE_STATIC (decl) || DECL_EXTERNAL (decl)
1932 || TREE_CODE (decl) == FUNCTION_DECL)
1933 {
1934 timevar_push (TV_VARCONST);
1935
1936 if (asmspec)
1937 make_decl_rtl (decl, asmspec);
1938
1939 /* Don't output anything when a tentative file-scope definition
1940 is seen. But at end of compilation, do output code for them.
1941
1942 We do output all variables when unit-at-a-time is active and rely on
1943 callgraph code to defer them except for forward declarations
1944 (see gcc.c-torture/compile/920624-1.c) */
1945 if ((at_end
1946 || !DECL_DEFER_OUTPUT (decl)
1947 || (flag_unit_at_a_time && DECL_INITIAL (decl)))
1948 && !DECL_EXTERNAL (decl))
1949 {
1950 if (flag_unit_at_a_time && !cgraph_global_info_ready
1951 && TREE_CODE (decl) != FUNCTION_DECL && top_level)
1952 cgraph_varpool_finalize_decl (decl);
1953 else
1954 assemble_variable (decl, top_level, at_end, 0);
1955 }
1956
1957 #ifdef ASM_FINISH_DECLARE_OBJECT
…
1963 #endif
1964
1965 timevar_pop (TV_VARCONST);
1966 }
1967 else if (DECL_REGISTER (decl) && asmspec != 0)
1968 {
1969 if (decode_reg_name (asmspec) >= 0)
1970 {
1971 SET_DECL_RTL (decl, NULL_RTX);
1972 make_decl_rtl (decl, asmspec);
1973 }
1974 else
1975 {
1976 error ("invalid register name `%s' for register variable", asmspec);
1977 DECL_REGISTER (decl) = 0;
1978 if (!top_level)
1979 expand_decl (decl);
1980 }
1981 }
…
2011 }
在 1950 行,当整个单元已经被分析过了, cgraph_global_info_ready 就不是 0 ,因此我们可以访问全局的信息(在当前情形下,它依旧是 false )。而 1946 行的断言 DECL_DEFER_OUTPUT 如果不是 0 ,表示这个 decl 的链接性状况还是未知的,因此它也不应该在现在发布。
因为 vtable 是一个 VAR_DECL ,调用下面的函数来设置相关联的 cgraph_varpool_node 节点的 finialized 标记,来表示将要为之输出代码。
580 void
581 cgraph_varpool_finalize_decl (tree decl) in cgraph.c
582 {
583 struct cgraph_varpool_node *node = cgraph_varpool_node (decl);
584
585 /* The first declaration of a variable that comes through this function
586 decides whether it is global (in C, has external linkage)
587 or local (in C, has internal linkage). So do nothing more
588 if this function has already run. */
589 if (node->finalized)
590 return ;
591 if (node->needed)
592 {
593 node->next_needed = cgraph_varpool_nodes_queue ;
594 cgraph_varpool_nodes_queue = node;
595 notice_global_symbol (decl);
596 }
597 node->finalized = true;
598
599 if (/* Externally visible variables must be output. The exception are
600 COMDAT functions that must be output only when they are needed. */
601 (TREE_PUBLIC (decl) && !DECL_COMDAT (decl))
602 /* Function whose name is output to the assembler file must be produced.
603 It is possible to assemble the name later after finalizing the function
604 and the fact is noticed in assemble_name then. */
605 || (DECL_ASSEMBLER_NAME_SET_P (decl)
606 && TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))))
607 {
608 cgraph_varpool_mark_needed_node (node);
609 }
610 }
因为上面在 maybe_emit_vtables 的 1605 行,已经为需要输出的 vtable 调用了 cgraph_varpool_mark_needed_node ;因此对于这样的 vtable ,其 cgraph 节点中的 needed 标记是 true ,在这里这个节点也会被链入 cgraph_varpool_nodes_queue (看到在 cgraph_varpool_mark_needed_node 的 571 行,链入的条件是“ (!node->needed && node->finalized) ”,如果那时没有链入,那么现在在这里链入);在 608 行,该 vtable 再次被 cgraph_varpool_mark_needed_node 处理,确保 cgraph 节点链入 cgraph_varpool_nodes_queue 。
注意到一旦 maybe_emit_vtables 成功处理了这个类,就把它从 keyed_classes 中移走,同时强制进行下一次的迭代。