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

来源:互联网 发布:中控门禁系统数据库 编辑:程序博客网 时间:2024/05/21 06:39

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 lowerrepresentation of all reachable

394      functions. In the future, lowering willintroduce new functions and

395      new entry points on the way (by templateinstantiation 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 inlinefunction 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 reverseinlining 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 nevershared, 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 thedecl 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. Thismakes 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节点的neededfinalized域都将是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 wesee a function call, we don't want to look at the

274              function reference in the ADDR_EXPR thatis hanging from

275              the CALL_EXPR we're examining here,because we would

276              conclude incorrectly that the function'saddress could be

277              taken by something that is not afunction call. So only

278              walk the function parameter list, skipthe 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 arenot "

164                           "considered forinlining");

165   else if (callee->local.inlinable)

166     edge->inline_failed = N_("function not considered forinlining");

167   else

168     edge->inline_failed = N_("function not inlinable");

169 

170   /* At the moment we don't associate calls withspecific CALL_EXPRs

171      as we probably ought to, so we must preserveinline_call flags to

172      be the same in all copies of the sameedge.  */

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_edge175行的条件)。

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 anddeclaration 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       casePTRMEM_CST:

2507         if (TYPE_PTRMEMFUNC_P (TREE_TYPE (t)))

2508           cgraph_mark_needed_node(cgraph_node (PTRMEM_CST_MEMBER (t)));

2509         break;

2510       caseBASELINK:

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