在 cgraph_varpool_assemble_pending_decls 为所有待定变量(注意它们是编译单元外可见)发布汇编后,回到 cgraph_finalize_compilation_unit 开始对该编译单元进行分析。
cgraph_finalize_compilation_unit (continue)
383 timevar_push (TV_CGRAPH);
384 if (cgraph_dump_file )
385 {
386 fprintf (cgraph_dump_file , "Initial entry points:");
387 for (node = cgraph_nodes ; node; node = node->next)
388 if (node->needed && DECL_SAVED_TREE (node->decl))
389 fprintf (cgraph_dump_file , " %s", cgraph_node_name (node));
390 fprintf (cgraph_dump_file , "/n");
391 }
392
393 /* Propagate reachability flag and lower representation of all reachable
394 functions. In the future, lowering will introduce new functions and
395 new entry points on the way (by template instantiation and virtual
396 method table generation for instance). */
397 while (cgraph_nodes_queue )
398 {
399 struct cgraph_edge *edge;
400 tree decl = cgraph_nodes_queue ->decl;
401
402 node = cgraph_nodes_queue;
403 cgraph_nodes_queue = cgraph_nodes_queue ->next_needed;
404
405 /* ??? It is possible to create extern inline function and later using
406 weak alas attribute to kill it's body. See
407 gcc.c-torture/compile/20011119-1.c */
408 if (!DECL_SAVED_TREE (decl))
409 continue ;
410
411 if (node->analyzed || !node->reachable || !DECL_SAVED_TREE (decl))
412 abort ();
413
414 cgraph_analyze_function (node);
415
416 for (edge = node->callees; edge; edge = edge->next_callee)
417 if (!edge->callee->reachable)
418 cgraph_mark_reachable_node (edge->callee);
419
420 cgraph_varpool_assemble_pending_decls ();
421 }
队列 cgraph_nodes_queue 记录了与函数关联的 cgraph_node 节点。
DECL_SAVED_TREE 是代表函数体的中间树。在某些情况下,它可能为空,比如在函数模板中,因此上面 408 行的条件挑出了所有的函数体尚不确定的函数定义。 411 行是一个合法性检查,满足此条件说明编译器出问题了。
319 static void
320 cgraph_analyze_function (struct cgraph_node *node) in cgraphunit.c
321 {
322 tree decl = node->decl;
323 struct cgraph_edge *e;
324
325 current_function_decl = decl;
326
327 /* First kill forward declaration so reverse inlining works properly. */
328 cgraph_create_edges (decl, DECL_SAVED_TREE (decl));
注意,在下面的过程中,将可能向 cgraph_nodes_queue 中加入节点,这样所有在程序中被访问到的函数都会得到处理。
下面的 cgraph_create_edges 遍历指定的函数体,为尚未构建 cgraph_* 节点的合适对象构建 cgraph_* 节点,并加入相应的队列中。
306 void
307 cgraph_create_edges (tree decl, tree body) in cgraphunit.c
308 {
309 /* The nodes we're interested in are never shared, so walk
310 the tree ignoring duplicates. */
311 visited_nodes = htab_create (37, htab_hash_pointer ,
312 htab_eq_pointer , NULL);
313 walk_tree (&body, record_call_1 , decl, visited_nodes );
314 htab_delete (visited_nodes );
315 visited_nodes = NULL;
316 }
前面我们已经看到,在 walk_tree 中,一旦作为其参数的函数返回非 0 值,就会退出遍历。这里, record_call_1 总是返回 NULL ,强制 walk_tree 尽可能完成完整的遍历。
240 static tree
241 record_call_1 (tree *tp, int *walk_subtrees, void *data) in cgraphunit.c
242 {
243 tree t = *tp;
244
245 switch (TREE_CODE (t))
246 {
247 case VAR_DECL:
248 /* ??? Really, we should mark this decl as *potentially* referenced
249 by this function and re-examine whether the decl is actually used
250 after rtl has been generated. */
251 if (TREE_STATIC (t))
252 cgraph_varpool_mark_needed_node (cgraph_varpool_node (t));
253 break ;
254
255 case ADDR_EXPR:
256 if (flag_unit_at_a_time )
257 {
258 /* Record dereferences to the functions. This makes the
259 functions reachable unconditionally. */
260 tree decl = TREE_OPERAND (*tp, 0);
261 if (TREE_CODE (decl) == FUNCTION_DECL)
262 cgraph_mark_needed_node (cgraph_node (decl));
263 }
264 break ;
全局变量因为在编译单元外可见( TREE_PUBLIC 成立),不管怎样都要输出它(消除未使用的全局变量只能由链接器完成,这是 GCC 将要构想的一个特性)。而静态变量的可访问性仅局限于声明它的文件,及包含其声明文件的文件中,一旦发现它没有被使用,编译器即可从产生的汇编代码将其删除。
(记住 cgraph_* 节点仅用于函数,全局及静态变量的分析,局部变量因为局限在某个函数,用不着这么“重型的武器”)。在 252 行,把变量设置为是需要的,并且如果其前端节点已完成,就把它加入 cgraph_varpool_nodes_queue 队列。注意 252 行所调用的函数能确保每个变量只会被加入一次(每个加入的变量其 cgraph_varpool_node 节点的 needed 及 finalized 域都将是 1 ,防止它被再次加入)。
对于 255 行的 ADD_EXPR ,如果是对变量的地址引用,这里不用处理,等 record_call_1 进入其操作数后,自然会见到相应地 VAR_DECL 节点并处理之。需要处理的是函数,注意这里只是表示了对函数地址的引用,并不是调用。同样把这个函数节点为需要的,并且如果其前端节点已完成,就把它加入 cgraph_nodes_queue 队列。同样,这里也能确保每个需要的函数只加入一次。
record_call_1 (continue)
266 case CALL_EXPR:
267 {
268 tree decl = get_callee_fndecl (*tp);
269 if (decl && TREE_CODE (decl) == FUNCTION_DECL)
270 {
271 cgraph_record_call (data, decl);
272
273 /* When we see a function call, we don't want to look at the
274 function reference in the ADDR_EXPR that is hanging from
275 the CALL_EXPR we're examining here, because we would
276 conclude incorrectly that the function's address could be
277 taken by something that is not a function call. So only
278 walk the function parameter list, skip the other subtrees. */
279
280 walk_tree (&TREE_OPERAND (*tp, 1), record_call_1 , data,
281 visited_nodes );
282 *walk_subtrees = 0;
283 }
284 break ;
285 }
接着就是对函数调用的处理。在 268 行得到代表被调用函数声明的节点,在使用 cgraph 分析时,需要构建函数间的调用关系图(参考 函数调用关系图 一节)。
295 struct cgraph_edge *
296 cgraph_record_call (tree caller, tree callee) in cgraph.c
297 {
298 return create_edge (cgraph_node (caller), cgraph_node (callee));
299 }
这里注意, 271 行的实参 data (即参数 caller )是被分析函数的 FUNCTION_DECL 节点(它是 cgraph_create_edges 的参数 decl )。
154 static struct cgraph_edge *
155 create_edge (struct cgraph_node *caller, struct cgraph_node *callee) in cgraph.c
156 {
157 struct cgraph_edge *edge = ggc_alloc (sizeof (struct cgraph_edge));
158 struct cgraph_edge *edge2;
159
160 if (!DECL_SAVED_TREE (callee->decl))
161 edge->inline_failed = N_("function body not available");
162 else if (callee->local.redefined_extern_inline)
163 edge->inline_failed = N_("redefined extern inline functions are not "
164 "considered for inlining");
165 else if (callee->local.inlinable)
166 edge->inline_failed = N_("function not considered for inlining");
167 else
168 edge->inline_failed = N_("function not inlinable");
169
170 /* At the moment we don't associate calls with specific CALL_EXPRs
171 as we probably ought to, so we must preserve inline_call flags to
172 be the same in all copies of the same edge. */
173 if (cgraph_global_info_ready )
174 for (edge2 = caller->callees; edge2; edge2 = edge2->next_callee)
175 if (edge2->callee == callee)
176 {
177 edge->inline_failed = edge2->inline_failed;
178 break ;
179 }
180
181 edge->caller = caller;
182 edge->callee = callee;
183 edge->next_caller = callee->callers;
184 edge->next_callee = caller->callees;
185 caller->callees = edge;
186 callee->callers = edge;
187 return edge;
188 }
前面已经知道( 函数调用关系图 一节), cgraph_edge 用于绑定调用者及被调用者的 cgraph_node 节点。被调用者可能已经被分析过了,那么相应设置 cgraph_edge 。另外,在完成了编译单元的分析后, cgraph_global_info_ready 将被置为 1 ,这时就要遍历该函数所调用的函数( next_callee 把所有被该函数调用的函数链接起来),检查是否已经有该 cgraph_edge ( 175 行的条件)。
在 280 行, CALL_EXPR 的第二个操作数是其实参列表, walk_tree 进而遍历之。而在 281 行, walk_subtrees 被设置为 0 ,表示该 CALL_EXPR 子树以下部分已无关重要。
record_call_1 (continue)
287 default :
288 /* Save some cycles by not walking types and declaration as we
289 won't find anything useful there anyway. */
290 if (DECL_P (*tp) || TYPE_P (*tp))
291 {
292 *walk_subtrees = 0;
293 break ;
294 }
295
296 if ((unsigned int) TREE_CODE (t) >= LAST_AND_UNUSED_TREE_CODE)
297 return (*lang_hooks .callgraph.analyze_expr ) (tp, walk_subtrees, data);
298 break ;
299 }
300
301 return NULL;
302 }
同样,对于函数分析,我们不关心类型及变量声明,因此在 292 行跳过之。前端公用的中间树节点的编码由 LAST_AND_UNUSED_TREE_CODE 结尾(不包括),语言自用的部分跟在其后。显然,这部分节点只能由语言钩子来处理。
2497 tree
2498 cxx_callgraph_analyze_expr (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED, in decl2.c
2499 tree from ATTRIBUTE_UNUSED)
2500 {
2501 tree t = *tp;
2502
2503 if (flag_unit_at_a_time )
2504 switch (TREE_CODE (t))
2505 {
2506 case PTRMEM_CST:
2507 if (TYPE_PTRMEMFUNC_P (TREE_TYPE (t)))
2508 cgraph_mark_needed_node (cgraph_node (PTRMEM_CST_MEMBER (t)));
2509 break ;
2510 case BASELINK:
2511 if (TREE_CODE (BASELINK_FUNCTIONS (t)) == FUNCTION_DECL)
2512 cgraph_mark_needed_node (cgraph_node (BASELINK_FUNCTIONS (t)));
2513 break ;
2514
2515 default :
2516 break ;
2517 }
2518
2519 return NULL;
2520 }
对于 C++ 来说,需要考虑就是对成员函数地址的引用,及派生类对基类成员函数的调用,这两种情况。