GCC-3.4.6源代码学习笔记(173)

5.13.5.3.              分析编译单元

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 节点。

5.13.5.3.1.        分析函数
5.13.5.3.1.1.  构建函数调用图

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++ 来说,需要考虑就是对成员函数地址的引用,及派生类对基类成员函数的调用,这两种情况。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值