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

5.13.4.4.              迭代 为延迟函数发布代码

由编译器产生的函数(经由 implicitly_declare_fn mark_decl_instantiated build_clone )或内联函数缓存在 deferred_fns 中。因为到这里,我们已经完成了整个源文件的代码解析,处理这些延迟函数的信息都应该齐备了。

首先,对于由编译器自己产生的“人造”函数,其设置了标记 DECL_ARTIFICIAL ;并且 FUNCTION_DECL 节点,其 DECL_INITIAL 域,在完成前端的处理后,应该是绑定在该函数域中的对象子树(即,各种 BLOCK 节点),否则将是空的。对于 DECL_INITIAL 域是空的“人造”函数,编译器到目前为止仅是做了声明,现在需要合成其定义。

 

742  void

743  synthesize_method (tree fndecl)                                                                  in method.c

744  {

745    bool nested = (current_function_decl != NULL_TREE);

746    tree context = decl_function_context (fndecl);

747    bool need_body = true;

748    tree stmt;

749 

750    if (at_eof )

751      import_export_decl (fndecl);

752 

753    /* If we've been asked to synthesize a clone, just synthesize the

754      cloned function instead. Doing so will automatically fill in the

755      body for the clone.  */

756    if (DECL_CLONED_FUNCTION_P (fndecl))

757    {

758      synthesize_method (DECL_CLONED_FUNCTION (fndecl));

759      return ;

760    }

761 

762    /* We may be in the middle of deferred access check. Disable

763      it now.  */

764    push_deferring_access_checks (dk_no_deferred);

765 

766    if (! context)

767      push_to_top_level ();

768    else if (nested)

769      push_function_context_to (context);

770 

771    /* Put the function definition at the position where it is needed,

772      rather than within the body of the class. That way, an error

773      during the generation of the implicit body points at the place

774      where the attempt to generate the function occurs, giving the

775      user a hint as to why we are attempting to generate the

776      function.  */

777    DECL_SOURCE_LOCATION (fndecl) = input_location ;

778 

779    interface_unknown = 1;

780    start_function (NULL_TREE, fndecl, NULL_TREE, SF_DEFAULT | SF_PRE_PARSED);

781    clear_last_expr ();

782    stmt = begin_function_body ();

783 

784    if (DECL_OVERLOADED_OPERATOR_P (fndecl) == NOP_EXPR)

785    {

786      do_build_assign_ref (fndecl);

787      need_body = false;

788    }

789    else if (DECL_CONSTRUCTOR_P (fndecl))

790    {

791      tree arg_chain = FUNCTION_FIRST_USER_PARMTYPE (fndecl);

792      if (arg_chain != void_list_node)

793        do_build_copy_constructor (fndecl);

794      else if (TYPE_NEEDS_CONSTRUCTING (current_class_type ))

795        finish_mem_initializers (NULL_TREE);

796    }

797 

798    /* If we haven't yet generated the body of the function, just

799      generate an empty compound statement.  */

800    if (need_body)

801    {

802      tree compound_stmt;

803      compound_stmt = begin_compound_stmt (/*has_no_scope=*/ false);

804      finish_compound_stmt (compound_stmt);

805    }

806 

807    finish_function_body (stmt);

808    expand_or_defer_fn (finish_function (0));

809 

810    extract_interface_info ();

811    if (! context)

812      pop_from_top_level ();

813    else if (nested)

814      pop_function_context_from (context);

815 

816    pop_deferring_access_checks ();

817  }

 

看到对 synthesize_method 的定义在全局名字空间上下文中完成。不过这没有关系,因为这些函数的声明节点中已经包括了正确的上下文信息。另外,上下文信息实际上只是用于名字查找中,所有的代码都将使用修饰名被发布在全局名字空间里。

注意 808 行的 expand_or_defer_fn ,在 finish_file 2769 行亦调用了 expand_or_defer_fn 。不过只要在这里调用了这个函数, finish_file 2762 行条件不成立,因而不会再调用该函数。在前面看到 expand_or_defer_fn 的一个主要操作是遍历给定的函数树,调用函数 simplify_aggr_init_exprs_r 来处理 AGGR_INIT_EXPR 类型的节点。【 2 】对此有如下的描述:

AGGR_INIT_EXPR

一个 AGGR_INIT_EXPR 节点代表一个初始化,它源自一个函数调用返回值,或一个构造函数执行结果。一个 AGGR_INIT_EXPR 只能作为一个 TARGET_EXPR 节点第二个操作数出现。 AGGR_INIT_EXPR 的第一个操作数是被调用函数的地址,就像在一个 CALL_EXPR 节点那样。第二个操作数是传递给该函数的实参,它是一个 TREE_LIST ,同样类似于一个 CALL_EXPR 节点的实参的形式。该表达式( AGGR_INIT_EXPR )的值是该函数返回的值。

对于特定的 AGGR_INIT_EXPR ,如果 AGGR_INIT_VIA_CTOR_P 成立,那么这个初始化通过一个构造函数的调用完成。该 AGGR_INIT_EXPR 的第三个操作数,它总是一个 VAR_DECL 节点,其地址替换参数列表的第一个参数。在这种情况下,该表达式的值是 AGGR_INIT_EXPR 第三个操作数所给出的 VAR_DECL ;构造函数不返回任何值。

这个结构用于具名返回值优化( nrv —— named return value optimization ),调用者传入把值所要存入的内存块的地址。这个地址称为“构造体值地址”( structure value address )。 simplify_aggr_init_exprs_r CALL_EXPR 与其实参及这个临时局部变量结合起来(它被作为额外实参加入,因为我们尝试返回聚集类型);并把这些节点插入 DECL_SAVED_TREE (fn) 链表中。

 

2769 static tree

2770 simplify_aggr_init_exprs_r (tree* tp,                                                    in semantics.c

2771                        int* walk_subtrees,

2772                        void* data ATTRIBUTE_UNUSED)

2773 {

2774   /* We don't need to walk into types; there's nothing in a type that

2775     needs simplification. (And, furthermore, there are places we

2776     actively don't want to go. For example, we don't want to wander

2777     into the default arguments for a FUNCTION_DECL that appears in a

2778     CALL_EXPR.)  */

2779   if (TYPE_P (*tp))

2780   {

2781     *walk_subtrees = 0;

2782     return NULL_TREE;

2783   }

2784   /* Only AGGR_INIT_EXPRs are interesting.  */

2785   else if (TREE_CODE (*tp) != AGGR_INIT_EXPR)

2786     return NULL_TREE;

2787

2788   simplify_aggr_init_expr (tp);

2789

2790   /* Keep iterating.  */

2791   return NULL_TREE;

2792 }

 

看到 simplify_aggr_init_exprs_r 总是返回 NULL ,这将使得 walk_tree 遍历整棵树。这是因为在一个函数定义中可能有多于 1 AGGR_INIT_EXPR 节点。例如:

class A {…};

 

class B {

public :

B (const A&);

};

 

A func1 () {…}

 

B func2 () {

B b = func1 ();   // 1st AGGR_INIT_EXPR

return b;     // 2nd AGGR_INIT_EXPR

}

AGGR_INIT_EXPR 由下面的函数来具体地处理。注意 2804 行的 fn 获得的是函数的地址(即 ADDR_EXPR 类型的节点

 

2798 void

2799 simplify_aggr_init_expr (tree *tp)                                                        in semantics.c

2800 {

2801   tree aggr_init_expr = *tp;

2802

2803   /* Form an appropriate CALL_EXPR.  */

2804   tree fn = TREE_OPERAND (aggr_init_expr, 0);

2805   tree args = TREE_OPERAND (aggr_init_expr, 1);

2806   tree slot = TREE_OPERAND (aggr_init_expr, 2);

2807   tree type = TREE_TYPE (aggr_init_expr);

2808

2809   tree call_expr;

2810   enum style_t { ctor, arg, pcc } style;

2811

2812   if (AGGR_INIT_VIA_CTOR_P (aggr_init_expr))

2813     style = ctor;

2814 #ifdef PCC_STATIC_STRUCT_RETURN

2815   else if (1)

2816     style = pcc;

2817 #endif

2818   else if (TREE_ADDRESSABLE (type))

2819     style = arg;

2820   else

2821     /* We shouldn't build an AGGR_INIT_EXPR if we don't need any special

2822       handling. See build_cplus_new.  */

2823     abort ();

2824

2825   if (style == ctor || style == arg)

2826   {

2827     /* Pass the address of the slot. If this is a constructor, we

2828       replace the first argument; otherwise, we tack on a new one.  */

2829        tree addr;

2830

2831     if (style == ctor)

2832        args = TREE_CHAIN (args);

2833

2834     cxx_mark_addressable (slot);

2835     addr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (slot)), slot);

2836     if (style == arg)

2837     {

2838       /* The return type might have different cv-quals from the slot.  */

2839       tree fntype = TREE_TYPE (TREE_TYPE (fn));

2840 #ifdef ENABLE_CHECKING

2841       if (TREE_CODE (fntype) != FUNCTION_TYPE

2842          && TREE_CODE (fntype) != METHOD_TYPE)

2843         abort ();

2844 #endif

2845       addr = convert (build_pointer_type (TREE_TYPE (fntype)), addr);

2846     }

2847

2848     args = tree_cons (NULL_TREE, addr, args);

2849   }

2850

2851   call_expr = build (CALL_EXPR,

2852                  TREE_TYPE (TREE_TYPE (TREE_TYPE (fn))),

2853                  fn, args, NULL_TREE);

2854

2855   if (style == arg)

2856     /* Tell the backend that we've added our return slot to the argument

2857       list.  */

2858     CALL_EXPR_HAS_RETURN_SLOT_ADDR (call_expr) = 1;

2859   else if (style == pcc)

2860   {

2861     /* If we're using the non-reentrant PCC calling convention, then we

2862         need to copy the returned value out of the static buffer into the

2863        SLOT.  */

2864     push_deferring_access_checks (dk_no_check);

2865     call_expr = build_aggr_init (slot, call_expr,

2866                            DIRECT_BIND | LOOKUP_ONLYCONVERTING);

2867     pop_deferring_access_checks ();

2868   }

2869

2870   /* We want to use the value of the initialized location as the

2871     result.  */

2872   call_expr = build (COMPOUND_EXPR, type,

2873                    call_expr, slot);

2874

2875   /* Replace the AGGR_INIT_EXPR with the CALL_EXPR.  */

2876   TREE_CHAIN (call_expr) = TREE_CHAIN (aggr_init_expr);

2877   *tp = call_expr;

2878 }

 

因为具名返回值优化将去掉函数的返回语句,因此在 2872 行原来的调用语句需要用一个复合语句来替代,使得返回值能正确地获得。另外,注意 2834 行,保存函数返回值的人造变量被标记为可寻址,这对其 RTL 代码的生成将有深刻的影响。而在某些目标机器上,结构体或联合体的返回值,由被调用函数通过返回包含结构体或联合体的静态变量地址来返回。在这些机器上, 2814 行的宏 PCC_STATIC_STRUCT_RETURN 将被定义。对于 x86 机器,这个宏没有定义。

 

你可能感兴趣的:(function,tree,null,Build,Constructor,nested)