5.13.5.3.2.4.2.3.3. 构建返回变量
加入为形参构建的局部变量后,紧接着这些变量,(可能)声明用于返回的变量。
expand_call_inline (continue)
1467 /* Declare the return variable for the function. */
1468 COMPOUND_BODY (stmt)
1469 = chainon (COMPOUND_BODY (stmt),
1470 declare_return_variable (id, return_slot_addr, &use_stmt));
1471 #else /* INLINER_FOR_JAVA */
….
1483 #endif /* INLINER_FOR_JAVA */
上面的 return_slot_addr ,如果使用具名返回值优化( NRVO )通过形参返回聚集对象时,用于指向这个对象;否则就是 NULL (在前面拷贝形参列表时,我们专门去掉了对这个对象的处理)。
913 #ifndef INLINER_FOR_JAVA
914 static tree
915 declare_return_variable (struct inline_data *id, tree return_slot_addr, in tree-inline.c
916 tree *use_stmt)
917 #else /* INLINER_FOR_JAVA */
…
921 #endif /* INLINER_FOR_JAVA */
922 {
923 tree fn = VARRAY_TOP_TREE (id->fns);
924 tree result = DECL_RESULT (fn);
925 #if ndef INLINER_FOR_JAVA
926 tree var;
927 #endif /* not INLINER_FOR_JAVA */
928 int need_return_decl = 1;
929
930 /* We don't need to do anything for functions that don't return
931 anything. */
932 if (!result || VOID_TYPE_P (TREE_TYPE (result)))
933 {
934 #ifndef INLINER_FOR_JAVA
935 *use_stmt = NULL_TREE;
936 #else /* INLINER_FOR_JAVA */
…
938 #endif /* INLINER_FOR_JAVA */
939 return NULL_TREE;
940 }
941
942 #ifndef INLINER_FOR_JAVA
943 var = ((*lang_hooks .tree_inlining.copy_res_decl_for_inlining )
944 (result, fn, VARRAY_TREE (id->fns, 0), id->decl_map,
945 &need_return_decl, return_slot_addr));
946
947 /* Register the VAR_DECL as the equivalent for the RESULT_DECL; that
948 way, when the RESULT_DECL is encountered, it will be
949 automatically replaced by the VAR_DECL. */
950 splay_tree_insert (id->decl_map,
951 (splay_tree_key) result,
952 (splay_tree_value) var);
953
954 /* Build the USE_STMT. If the return type of the function was
955 promoted, convert it back to the expected type. */
956 if (TREE_TYPE (var) == TREE_TYPE (TREE_TYPE (fn)))
957 *use_stmt = build_stmt (EXPR_STMT, var);
958 else
959 *use_stmt = build_stmt (EXPR_STMT,
960 build1 (NOP_EXPR, TREE_TYPE (TREE_TYPE (fn)),
961 var));
962 TREE_ADDRESSABLE (*use_stmt) = 1;
963
964 /* Build the declaration statement if FN does not return an
965 aggregate. */
966 if (need_return_decl)
967 return build_stmt (DECL_STMT, var);
968 #else /* INLINER_FOR_JAVA */
...
979 #endif /* INLINER_FOR_JAVA */
980 /* If FN does return an aggregate, there's no need to declare the
981 return variable; we're using a variable in our caller's frame. */
982 else
983 return NULL_TREE;
984 }
如果没有返回值或返回值是 NULL ,显然什么都不用做,否则就要拷贝返回值。在对下面函数的调用中, result 是函数的返回值声明, fn 是当前被调用函数, caller 是调用者。因为调用返回聚集对象的函数,调用者需要传入返回地址( NRVO 的做法),这个变量由调用者负责声明,我们不需要管;但其他返回值,在这里我们需要为它们声明对应的变量,因此相应设置 need_decl ( 2171 行)。
2147 tree
2148 cp_copy_res_decl_for_inlining (tree result, in tree.c
2149 tree fn,
2150 tree caller,
2151 void* decl_map_,
2152 int* need_decl,
2153 tree return_slot_addr)
2154 {
2155 splay_tree decl_map = (splay_tree)decl_map_;
2156 tree var;
2157
2158 /* If FN returns an aggregate then the caller will always pass the
2159 address of the return slot explicitly. If we were just to
2160 create a new VAR_DECL here, then the result of this function
2161 would be copied (bitwise) into the variable initialized by the
2162 TARGET_EXPR. That's incorrect, so we must transform any
2163 references to the RESULT into references to the target. */
2164
2165 /* We should have an explicit return slot iff the return type is
2166 TREE_ADDRESSABLE. See simplify_aggr_init_expr. */
2167 if (TREE_ADDRESSABLE (TREE_TYPE (result))
2168 != (return_slot_addr != NULL_TREE))
2169 abort ();
2170
2171 *need_decl = !return_slot_addr;
2172 if (return_slot_addr)
2173 {
2174 var = build_indirect_ref (return_slot_addr, "");
2175 if (! same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (var),
2176 TREE_TYPE (result)))
2177 abort ();
2178 }
2179 /* Otherwise, make an appropriate copy. */
2180 else
2181 var = copy_decl_for_inlining (result, fn, caller);
2182
2183 if (DECL_SAVED_FUNCTION_DATA (fn))
2184 {
2185 tree nrv = DECL_SAVED_FUNCTION_DATA (fn)->x_return_value;
2186 if (nrv)
2187 {
2188 /* We have a named return value; copy the name and source
2189 position so we can get reasonable debugging information, and
2190 register the return variable as its equivalent. */
2191 if (TREE_CODE (var) == VAR_DECL
2192 /* But not if we're initializing a variable from the
2193 enclosing function which already has its own name. */
2194 && DECL_NAME (var) == NULL_TREE)
2195 {
2196 DECL_NAME (var) = DECL_NAME (nrv);
2197 DECL_SOURCE_LOCATION (var) = DECL_SOURCE_LOCATION (nrv);
2198 DECL_ABSTRACT_ORIGIN (var) = DECL_ORIGIN (nrv);
2199 /* Don't lose initialization info. */
2200 DECL_INITIAL (var) = DECL_INITIAL (nrv);
2201 /* Don't forget that it needs to go in the stack. */
2202 TREE_ADDRESSABLE (var) = TREE_ADDRESSABLE (nrv);
2203 }
2204
2205 splay_tree_insert (decl_map,
2206 (splay_tree_key) nrv,
2207 (splay_tree_value) var);
2208 }
2209 }
2210
2211 return var;
2212 }
DECL_SAVED_FUNCTION_DATA 保存了描述该函数的 language_function 对象(它在被 finsh_function 调用的 save_function_data 设置)。而其中的 x_return_value 域专门用于 NRVO 的目的(它在被 check_return_expr 设置为返回值)。因为在 2174 行已经为这个返回值构建了 return_slot_addr 所指向对象的 INDRECT_REF 节点,这是真正用于返回值的对象,因此根据 x_return_value 域来设置它,并把它作为 x_return_value 的替代。
如果返回值不需要声明, declare_return_variable 将返回 NULL ,否则就是相应的 DECL_STMT ,这个 STMT 在 expand_call_inline 的 1470 行插入到为形参声明的变量后。而 use_stmt 则要在展开末尾才能插入(作为 ret_label 的对象)。
5.13.5.3.2.4.2.3.4. 拷贝函数体
在声明了对应于形参及返回值的局部变量后,可以拷贝函数体并使用这些变量替换形参及返回值,这由下面的 copy_body 函数来进行。
expand_call_inline (continue)
1485 /* After we've initialized the parameters, we insert the body of the
1486 function itself. */
1487 #ifndef INLINER_FOR_JAVA
1488 inlined_body = &COMPOUND_BODY (stmt);
1489 while (*inlined_body)
1490 inlined_body = &TREE_CHAIN (*inlined_body);
1491 *inlined_body = copy_body (id);
1492 #else /* INLINER_FOR_JAVA */
…
1503 #endif /* INLINER_FOR_JAVA */
1504
1505 /* After the body of the function comes the RET_LABEL. This must come
1506 before we evaluate the returned value below, because that evaluation
1507 may cause RTL to be generated. */
1508 #ifndef INLINER_FOR_JAVA
1509 COMPOUND_BODY (stmt)
1510 = chainon (COMPOUND_BODY (stmt),
1511 build_stmt (LABEL_STMT, id->ret_label));
1512 #else /* INLINER_FOR_JAVA */
…
1519 #endif /* INLINER_FOR_JAVA */
1520
1521 /* Finally, mention the returned value so that the value of the
1522 statement-expression is the returned value of the function. */
1523 #ifndef INLINER_FOR_JAVA
1524 COMPOUND_BODY (stmt) = chainon (COMPOUND_BODY (stmt), use_stmt);
1525
1526 /* Close the block for the parameters. */
1527 scope_stmt = build_stmt (SCOPE_STMT, DECL_INITIAL (fn));
1528 SCOPE_NO_CLEANUPS_P (scope_stmt) = 1;
1529 remap_block (scope_stmt, NULL_TREE, id);
1530 COMPOUND_BODY (stmt)
1531 = chainon (COMPOUND_BODY (stmt), scope_stmt);
1532 #else /* INLINER_FOR_JAVA */
DECL_SAVED_TREE 保存的就是代表函数体的 COMPOUND_STMT 或对等物。
704 static tree
705 copy_body (inline_data *id) in tree-inline.c
706 {
707 tree body;
708
709 body = DECL_SAVED_TREE (VARRAY_TOP_TREE (id->fns));
710 walk_tree (&body, copy_body_r , id, NULL);
711
712 return body;
713 }
函数 copy_body_r 的大部我们前面已经看过,除了下面对 RETURN_STMT 的处理。这里首先为前面准备的 ret_label 构建一个 GOTO_STMT 。如果有返回值,还要在 GOTO_STMT 之前插入包含了返回值表达式的 EXPR_STMT 。
copy_body_r (continue)
528 #ifndef INLINER_FOR_JAVA
529 if (TREE_CODE (*tp) == RETURN_STMT && id->ret_label)
530 #else /* INLINER_FOR_JAVA */
…
532 #endif /* INLINER_FOR_JAVA */
533 {
534 tree return_stmt = *tp;
535 tree goto_stmt;
536
537 /* Build the GOTO_STMT. */
538 #ifndef INLINER_FOR_JAVA
539 goto_stmt = build_stmt (GOTO_STMT, id->ret_label);
540 TREE_CHAIN (goto_stmt) = TREE_CHAIN (return_stmt);
541 GOTO_FAKE_P (goto_stmt) = 1;
542 #else /* INLINER_FOR_JAVA */
…
546 #endif /* INLINER_FOR_JAVA */
547
548 /* If we're returning something, just turn that into an
549 assignment into the equivalent of the original
550 RESULT_DECL. */
551 #ifndef INLINER_FOR_JAVA
552 if (RETURN_STMT_EXPR (return_stmt))
553 {
554 *tp = build_stmt (EXPR_STMT,
555 RETURN_STMT_EXPR (return_stmt));
556 STMT_IS_FULL_EXPR_P (*tp) = 1;
557 /* And then jump to the end of the function. */
558 TREE_CHAIN (*tp) = goto_stmt;
559 }
560 #else /* INLINER_FOR_JAVA */
…
567 #endif /* INLINER_FOR_JAVA */
568 /* If we're not returning anything just do the jump. */
569 else
570 *tp = goto_stmt;
在这里 RETURN_STMT 被(可能出现) EXPR_STMT 及 GOTO_STMT 所代替,不过其主要内容都在 RETURN_STMT_EXPR 域中,现在这个域转入了 EXPR_STMT 里。注意, copy_body_r 总是返回 0 ,那么 walk_tree 仍会进入这些新的 STMT 的内部( walk_tree 执行前序遍历),把出现在原来 RETURN_STMT_EXPR 域中的 RETURN_DECL (它被视为局部变量)替换掉。
拷贝、替换完函数体后,把先前准备的 use_stmt 附上。那么内联函数的展开实际上是这样一个结果:
double retVal = inline_f1 (int a1, short a2) =>
double retVal = { int a1; short a2; double ` 匿名返回值 `; { ` 替换后函数体 ` }; ret_lable: ` 匿名返回值 `; }
expand_call_inline (continue)
1547 /* Clean up. */
1548 splay_tree_delete (id->decl_map);
1549 id->decl_map = st;
1550
1551 /* Although, from the semantic viewpoint, the new expression has
1552 side-effects only if the old one did, it is not possible, from
1553 the technical viewpoint, to evaluate the body of a function
1554 multiple times without serious havoc. */
1555 TREE_SIDE_EFFECTS (expr) = 1;
1556
1557 /* Replace the call by the inlined body. Wrap it in an
1558 EXPR_WITH_FILE_LOCATION so that we'll get debugging line notes
1559 pointing to the right place. */
1560 #ifndef INLINER_FOR_JAVA
1561 chain = TREE_CHAIN (*tp);
1562 #endif /* INLINER_FOR_JAVA */
1563 *tp = build_expr_wfl (expr, DECL_SOURCE_FILE (fn), DECL_SOURCE_LINE (fn),
1564 /*col=*/ 0);
1565 EXPR_WFL_EMIT_LINE_NOTE (*tp) = 1;
1566 #ifndef INLINER_FOR_JAVA
1567 TREE_CHAIN (*tp) = chain;
1568 #endif /* not INLINER_FOR_JAVA */
1569 pop_srcloc ();
1570
1571 /* If the value of the new expression is ignored, that's OK. We
1572 don't warn about this for CALL_EXPRs, so we shouldn't warn about
1573 the equivalent inlined version either. */
1574 TREE_USED (*tp) = 1;
1575
1576 /* Update callgraph if needed. */
1577 if (id->decl)
1578 {
1579 cgraph_remove_call (id->decl, fn);
1580 cgraph_create_edges (id->decl, *inlined_body);
1581 }
1582
1583 /* Recurse into the body of the just inlined function. */
1584 {
1585 tree old_decl = id->current_decl;
1586 id->current_decl = fn;
1587 expand_calls_inline (inlined_body, id);
1588 id->current_decl = old_decl;
1589 }
1590 VARRAY_POP (id->fns);
1591
1592 /* Don't walk into subtrees. We've already handled them above. */
1593 *walk_subtrees = 0;
1594
1595 (*lang_hooks .tree_inlining.end_inlining) (fn);
1596
1597 /* Keep iterating. */
1598 return NULL_TREE;
1599 }
上面 inlined_body 指向展开的函数体,因为其中可能有 CALL_EXPR ,继续使用 expand_calls_inline 来展开可能存在的内联函数。看到 expand_calls_inline 总是返回 NULL ,它希望 walk_tree 能完整遍历调用者函数。