Studying note of GCC-3.4.6 source (179)

5.13.5.3.2.4.  Emit assemble basing on cgraph

Coming to the last function in cgraph_optimize , it optimizes all functions, and emits assembler code for them.

 

1579 static void

1580 cgraph_expand_all_functions (void)                                                      in cgraphunit.c

1581 {

1582   struct cgraph_node *node;

1583   struct cgraph_node **order =

1584     xcalloc (cgraph_n_nodes, sizeof (struct cgraph_node *));

1585   int order_pos = 0;

1586   int i;

1587

1588   cgraph_mark_functions_to_output ();

1589

1590   order_pos = cgraph_postorder (order);

1591

1592   for (i = order_pos - 1; i >= 0; i--)

1593   {

1594     node = order[i];

1595     if (node->output)

1596     {

1597        if (!node->reachable)

1598          abort ();

1599        node->output = 0;

1600        cgraph_expand_function (node);

1601     }

1602   }

1603   free (order);

1604 }

5.13.5.3.2.4.1.          Mark functions need assemble emitted

First, it needs find out functions needing assembler code emiited. Recall that inlined function would be expanded at site of calling, it would not emitted as function.

 

462  static void

463  cgraph_mark_functions_to_output (void)                                              in cgraphunit.c

464  {

465    struct cgraph_node *node;

466 

467    for (node = cgraph_nodes ; node; node = node->next)

468    {

469      tree decl = node->decl;

470      struct cgraph_edge *e;

471 

472      if (node->output)

473        abort ();

474 

475      for (e = node->callers; e; e = e->next_caller)

476        if (e->inline_failed)

477           break ;

478 

479      /* We need to output all local functions that are used and not

480         always inlined, as well as those that are reachable from

481         outside the current compilation unit.  */

482      if (DECL_SAVED_TREE (decl)

483          && (node->needed

484                  || (e && node->reachable))

485          && !TREE_ASM_WRITTEN (decl) && !node->origin

486         && !DECL_EXTERNAL (decl))

487        node->output = 1;

488      else

489        DECL_SAVED_INSNS (decl) = NULL;

490    }

491  }

 

DECL_SAVED_INSNS at line 489, in fact records the context at time of parsing the function. It is useless for function being inlined.

5.13.5.3.2.4.2.          Expand inline function basing on cgraph

5.13.5.3.2.4.2.1.    Find out inlinable calling

In cgraph_expand_all_functions at line 1590, calls cgraph_postorder once again to sort functions in invocation order. We begin with the function at bottom of the call stack (most possible).

 

526  static void

527  cgraph_expand_function (struct cgraph_node *node)                              in cgraphunit.c

528  {

529    tree decl = node->decl;

530 

531    if (flag_unit_at_a_time )

532      announce_function (decl);

533 

534    cgraph_optimize_function (node);

535 

536    /* Generate RTL for the body of DECL. Nested functions are expanded

537      via lang_expand_decl_stmt.  */

538    (*lang_hooks .callgraph.expand_function) (decl);

539    if (DECL_DEFER_OUTPUT (decl))

540      abort ();

541 

542    current_function_decl = NULL;

543  }

 

At line 532, announce_function will output the function name on therminal. Then before using language hook at line 538 to generate RTL code for the function, invoke below function to do optimization basing on cgraph.

 

495  static void

496  cgraph_optimize_function (struct cgraph_node *node)                                   in cgraphunit.c

497  {

498    tree decl = node->decl;

499 

500    timevar_push (TV_INTEGRATION);

501    /* optimize_inline_calls avoids inlining of current_function_decl.  */

502    current_function_decl = decl;

503    if (flag_inline_trees )

504    {

505      struct cgraph_edge *e;

506 

507      for (e = node->callees; e; e = e->next_callee)

508        if (!e->inline_failed || warn_inline

509           || (DECL_DECLARED_INLINE_P (e->callee->decl)

510             && lookup_attribute ("always_inline",

511                               DECL_ATTRIBUTES (e->callee->decl))))

512          break ;

513      if (e)

514        optimize_inline_calls (decl);

515    }

516    if (node->nested)

517    {

518      for (node = node->nested; node; node = node->next_nested)

519        cgraph_optimize_function (node);

520    }

521    timevar_pop (TV_INTEGRATION);

522  }

 

Above loop at line 507 selects out functions regarded as inlinable in previous sections, note at line 508, once warn_inline is nonzero, the function will undergo below processing. But setting this just means, if the function inlined is too large, gives out a waring.

 

1617 void

1618 optimize_inline_calls (tree fn)                                                             in tree-inline.c

1619 {

1620   inline_data id;

1621   tree prev_fn;

1622

1623   /* There is no point in performing inlining if errors have already

1624     occurred -- and we might crash if we try to inline invalid

1625     code.  */

1626   if (errorcount || sorrycount)

1627     return ;

1628

1629   /* Clear out ID.  */

1630   memset (&id, 0, sizeof (id));

1631

1632   id.decl = fn;

1633   id.current_decl = fn;

1634   /* Don't allow recursion into FN.  */

1635   VARRAY_TREE_INIT (id.fns, 32, "fns");

1636   VARRAY_PUSH_TREE (id.fns, fn);

1637   /* Or any functions that aren't finished yet.  */

1638   prev_fn = NULL_TREE;

1639   if (current_function_decl )

1640   {

1641     VARRAY_PUSH_TREE (id.fns, current_function_decl );

1642     prev_fn = current_function_decl ;

1643   }

1644

1645   prev_fn = ((*lang_hooks .tree_inlining.add_pending_fn_decls )

1646             (&id.fns, prev_fn));

1647

1648   /* Create the list of functions this call will inline.  */

1649   VARRAY_TREE_INIT (id.inlined_fns, 32, "inlined_fns");

1650

1651   /* Keep track of the low-water mark, i.e., the point where the first

1652     real inlining is represented in ID.FNS.  */

1653   id.first_inlined_fn = VARRAY_ACTIVE_SIZE (id.fns);

1654

1655   /* Replace all calls to inline functions with the bodies of those

1656     functions.  */

1657   id.tree_pruner = htab_create (37, htab_hash_pointer ,

1658                           htab_eq_pointer , NULL);

1659   expand_calls_inline (&DECL_SAVED_TREE (fn), &id);

1660

1661   /* Clean up.  */

1662   htab_delete (id.tree_pruner);

1663   if (DECL_LANG_SPECIFIC (fn))

1664   {

1665     tree ifn = make_tree_vec (VARRAY_ACTIVE_SIZE (id.inlined_fns));

1666

1667     if (VARRAY_ACTIVE_SIZE (id.inlined_fns))

1668       memcpy (&TREE_VEC_ELT (ifn, 0), &VARRAY_TREE (id.inlined_fns, 0),

1669               VARRAY_ACTIVE_SIZE (id.inlined_fns) * sizeof (tree));

1670     DECL_INLINED_FNS (fn) = ifn;

1671   }

1672 }

 

At line 1620, inline_data is the data structure aids to this inlining process, its definition is given in below:

 

74    typedef struct inline_data                                                                      in tree-inline.c

75    {

76      /* A stack of the functions we are inlining. For example, if we are

77        compiling `f', which calls `g', which calls `h', and we are

78        inlining the body of `h', the stack will contain, `h', followed

79        by `g', followed by `f'. The first few elements of the stack may

80         contain other functions that we know we should not recurse into,

81         even though they are not directly being inlined.  */

82      varray_type fns;

83      /* The index of the first element of FNS that really represents an

84        inlined function.  */

85      unsigned first_inlined_fn;

86      /* The label to jump to when a return statement is encountered. If

87        this value is NULL, then return statements will simply be

88        remapped as return statements, rather than as jumps.  */

89      tree ret_label;

90      /* The map from local declarations in the inlined function to

91        equivalents in the function into which it is being inlined.  */

92      splay_tree decl_map;

93      /* Nonzero if we are currently within the cleanup for a

94        TARGET_EXPR.  */

95      int in_target_cleanup_p;

96      /* A list of the functions current function has inlined.  */

97      varray_type inlined_fns;

98      /* We use the same mechanism to build clones that we do to perform

99        inlining. However, there are a few places where we need to

100      distinguish between those two situations. This flag is true if

101      we are cloning, rather than inlining.  */

102    bool cloning_p;

103    /* Hash table used to prevent walk_tree from visiting the same node

104      umpteen million times.  */

105    htab_t tree_pruner;

106    /* Decl of function we are inlining into.  */

107     tree decl;

108    tree current_decl;

109  } inline_data;

 

Pay attention to above comment of member fns , it is why here first needs sort by cgraph_postorder first, besides as optimize_inline_calls may be called during parsing the function body (in case optimization is off, flag_unit_at_a_time is 0), above at line 1645, hook add_pending_fn_decls adds these under parsing functions into fns , and returns the one one the top of the call stack.

And in case of optimization is on, this function should not do anything.

 

2107 tree

2108 cp_add_pending_fn_decls (void* fns_p, tree prev_fn)                                          in tree.c

2109 {

2110   varray_type *fnsp = (varray_type *)fns_p;

2111   struct saved_scope *s;

2112

2113   for (s = scope_chain ; s; s = s->prev)

2114     if (s->function_decl && s->function_decl != prev_fn)

2115     {

2116       VARRAY_PUSH_TREE (*fnsp, s->function_decl);

2117       prev_fn = s->function_decl;

2118     }

2119

2120   return prev_fn;

2121 }

 

In below invocation, parameter tp is the body of the function, id->pruner is the hashtable to gaurantee single visit of every node.

 

1603 static void

1604 expand_calls_inline (tree *tp, inline_data *id)                                       in tree-inline.c

1605 {

1606   /* Search through *TP, replacing all calls to inline functions by

1607     appropriate equivalents. Use walk_tree in no-duplicates mode

1608     to avoid exponential time complexity. (We can't just use

1609     walk_tree_without_duplicates, because of the special TARGET_EXPR

1610     handling in expand_calls. The hash table is set up in

1611     optimize_function.  */

1612   walk_tree (tp, expand_call_inline , id, id->tree_pruner);

1613 }

 

Remember that walk_tree takes pre-order traversal, and calls function passed as parameter on every encountered node, here the function is expand_call_inline .

 

1233 static tree

1234 expand_call_inline (tree *tp, int *walk_subtrees, void *data)                   in tree-inline.c

1235 {

1236   inline_data *id;

1237   tree t;

1238   tree expr;

1239   tree stmt;

1240 #ifndef INLINER_FOR_JAVA

1241   tree chain;

1242   tree scope_stmt;

1243   tree use_stmt;

1244 #else /* INLINER_FOR_JAVA */

1245   tree retvar;

1246 #endif /* INLINER_FOR_JAVA */

1247   tree fn;

1248   tree arg_inits;

1249   tree *inlined_body;

1250   splay_tree st;

1251   tree args;

1252   tree return_slot_addr;

1253   const char *reason;

1254

1255   /* See what we've got.  */

1256   id = (inline_data *) data;

1257   t = *tp;

1258

1259   /* Recurse, but letting recursive invocations know that we are

1260     inside the body of a TARGET_EXPR.  */

1261   if (TREE_CODE (*tp) == TARGET_EXPR)

1262   {

1263 #ifndef INLINER_FOR_JAVA

1264     int i, len = first_rtl_op (TARGET_EXPR);

1265

1266     /* We're walking our own subtrees.  */

1267     *walk_subtrees = 0;

1268

1269     /* Actually walk over them. This loop is the body of

1270        walk_trees, omitting the case where the TARGET_EXPR

1271        itself is handled.  */

1272     for (i = 0; i < len; ++i)

1273     {

1274       if (i == 2)

1275         ++id->in_target_cleanup_p;

1276       walk_tree (&TREE_OPERAND (*tp, i), expand_call_inline, data,

1277                id->tree_pruner);

1278       if (i == 2)

1279         --id->in_target_cleanup_p;

1280     }

1281

1282     return NULL_TREE;

1283 #else /* INLINER_FOR_JAVA */

1284       abort ();

1285 #endif /* INLINER_FOR_JAVA */

1286   }

1287   else if (TREE_CODE (t) == EXPR_WITH_FILE_LOCATION)

1288   {

1289      /* We're walking the subtree directly.  */

1290     *walk_subtrees = 0;

1291      /* Update the source position.  */

1292     push_srcloc (EXPR_WFL_FILENAME (t), EXPR_WFL_LINENO (t));

1293     walk_tree (&EXPR_WFL_NODE (t), expand_call_inline, data,

1294              id->tree_pruner);

1295     /* Restore the original source position.  */

1296     pop_srcloc ();

1297

1298     return NULL_TREE;

1299   }

1300

1301   if (TYPE_P (t))

1302     /* Because types were not copied in copy_body, CALL_EXPRs beneath

1303       them should not be expanded. This can happen if the type is a

1304       dynamic array type, for example.  */

1305     *walk_subtrees = 0;

1306

1307   /* From here on, we're only interested in CALL_EXPRs.  */

1308   if (TREE_CODE (t) != CALL_EXPR)

1309     return NULL_TREE;

 

In theroy, expand_call_inline only interests in CALL_EXPR node. Further, as TARGET_EXPR and EXPR_WITH_FILE_LOCATION (may) enclose CALL_EXPR, so need special treatement. EXPR_WITH_FILE_LOCATION encloses information of source location, EXPR_WFL_NODE gives the contained expression.

 

expand_call_inline (continue)

 

1311   /* First, see if we can figure out what function is being called.

1312     If we cannot, then there is no hope of inlining the function.  */

1313   fn = get_callee_fndecl (t);

1314   if (!fn)

1315     return NULL_TREE;

1316

1317   /* Turn forward declarations into real ones.  */

1318   fn = cgraph_node (fn)->decl;

1319

1320   /* If fn is a declaration of a function in a nested scope that was

1321     globally declared inline, we don't set its DECL_INITIAL.

1322     However, we can't blindly follow DECL_ABSTRACT_ORIGIN because the

1323     C++ front-end uses it for cdtors to refer to their internal

1324     declarations, that are not real functions. Fortunately those

1325     don't have trees to be saved, so we can tell by checking their

1326     DECL_SAVED_TREE.  */

1327   if (! DECL_INITIAL (fn)

1328       && DECL_ABSTRACT_ORIGIN (fn)

1329       && DECL_SAVED_TREE (DECL_ABSTRACT_ORIGIN (fn)))

1330     fn = DECL_ABSTRACT_ORIGIN (fn);

1331

1332   /* Don't try to inline functions that are not well-suited to

1333     inlining.  */

1334   if (!cgraph_inline_p (id->current_decl, fn, &reason))

1335   {

1336     if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)))

1337     {

1338        sorry ("%Jinlining failed in call to '%F': %s", fn, fn, reason);

1339        sorry ("called from here");

1340     }

1341     else if (warn_inline && DECL_DECLARED_INLINE_P (fn)

1342            && !DECL_IN_SYSTEM_HEADER (fn)

1343            && strlen (reason)

1344            && !lookup_attribute ("noinline", DECL_ATTRIBUTES (fn)))

1345     {

1346        warning ("%Jinlining failed in call to '%F': %s", fn, fn, reason);

1347        warning ("called from here");

1348     }

1349     return NULL_TREE;

1350   }

1351

1352   if (! (*lang_hooks .tree_inlining.start_inlining) (fn))

1353     return NULL_TREE;

1354

1355   /* Set the current filename and line number to the function we are

1356     inlining so that when we create new _STMT nodes here they get

1357     line numbers corresponding to the function we are calling. We

1358     wrap the whole inlined body in an EXPR_WITH_FILE_AND_LINE as well

1359     because individual statements don't record the filename.  */

1360   push_srcloc (DECL_SOURCE_FILE (fn), DECL_SOURCE_LINE (fn));

1361

1362 #ifndef INLINER_FOR_JAVA

1363   /* Build a statement-expression containing code to initialize the

1364     arguments, the actual inline expansion of the body, and a label

1365     for the return statements within the function to jump to. The

1366     type of the statement expression is the return type of the

1367     function call.  */

1368   expr = build1 (STMT_EXPR, TREE_TYPE (TREE_TYPE (fn)), make_node (COMPOUND_STMT));

1369   /* There is no scope associated with the statement-expression.  */

1370   STMT_EXPR_NO_SCOPE (expr) = 1;

1371   if (lookup_attribute ("warn_unused_result",

1372                    TYPE_ATTRIBUTES (TREE_TYPE (fn))))

1373     STMT_EXPR_WARN_UNUSED_RESULT (expr) = 1;

1374   stmt = STMT_EXPR_STMT (expr);

 

Coming here, with previous processing, inline_failed field of cgraph_edge of inlinable invocation is NULL, otherwise it records the reason for the inlining failure. Thus, determine if certain call is inlinable is quite simple.

 

1490 bool

1491 cgraph_inline_p (tree caller_decl, tree callee_decl, const char **reason)   in cgraphunit.c

1492 {

1493   struct cgraph_node *caller = cgraph_node (caller_decl);

1494   struct cgraph_node *callee = cgraph_node (callee_decl);

1495   struct cgraph_edge *e;

1496

1497   for (e = caller->callees; e; e = e->next_callee)

1498     if (e->callee == callee)

1499     {

1500       if (e->inline_failed && reason)

1501          *reason = e->inline_failed;

1502         return !e->inline_failed;

1503     }

1504   /* We do not record builtins in the callgraph. Perhaps it would make more

1505     sense to do so and then prune out those not overwritten by explicit

1506     function body.  */

1507   if (reason)

1508     *reason = "originally indirect function calls never inlined";

1509   return false;

1510 }

 

At calling builtin function, it is not built into cgraph, it is not inlined too. Above at line 1352, current version GCC only defines the default hook returning true. At line 1368, builds a compund statement to accomendate the expanded code, see that its type is the returned type of the function.

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值