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

5350 行,当我们正在处理一个成员函数时, current_class_ptr 是用于‘ this ’指针的 PARM_DECL 。看到在下面,仅当 instance 的类型是可以明确知道的情形,才返回非空值。与 5281 行的条件相比, 5281 行的条件表示一个指针,而在 C++ 里,它可以是该类层次中不同的类型,以提供多态。因此作为结果,它返回 NULL_TREE

 

5275   static tree

5276   fixed_type_or_null (tree instance, int* nonnull, int* cdtorp)                         in class.c

5277   {

5278     switch (TREE_CODE (instance))

5279     {

5280       case INDIRECT_REF:

5281         if (POINTER_TYPE_P (TREE_TYPE (instance)))

5282           return NULL_TREE;

5283         else

5284           return fixed_type_or_null (TREE_OPERAND (instance, 0),

5285                                 nonnull, cdtorp);

5286  

5287       case CALL_EXPR:

5288         /* This is a call to a constructor, hence it's never zero.  */

5289         if (TREE_HAS_CONSTRUCTOR (instance))

5290         {

5291           if (nonnull)

5292            *nonnull = 1;

5293           return TREE_TYPE (instance);

5294         }

5295         return NULL_TREE;

5296  

5297       case SAVE_EXPR:

5298         /* This is a call to a constructor, hence it's never zero.  */

5299         if (TREE_HAS_CONSTRUCTOR (instance))

5300         {

5301           if (nonnull)

5302             *nonnull = 1;

5303           return TREE_TYPE (instance);

5304         }

5305         return fixed_type_or_null (TREE_OPERAND (instance, 0), nonnull, cdtorp);

5306  

5307       case RTL_EXPR:

5308         return NULL_TREE;

5309  

5310       case PLUS_EXPR:

5311       case MINUS_EXPR:

5312         if (TREE_CODE (TREE_OPERAND (instance, 0)) == ADDR_EXPR)

5313           return fixed_type_or_null (TREE_OPERAND (instance, 0), nonnull, cdtorp);

5314         if (TREE_CODE (TREE_OPERAND (instance, 1)) == INTEGER_CST)

5315           /* Propagate nonnull.  */

5316           return fixed_type_or_null (TREE_OPERAND (instance, 0), nonnull, cdtorp);

5317         return NULL_TREE;

5318  

5319       case NOP_EXPR:

5320       case CONVERT_EXPR:

5321         return fixed_type_or_null (TREE_OPERAND (instance, 0), nonnull, cdtorp);

5322  

5323       case ADDR_EXPR:

5324         if (nonnull)

5325           *nonnull = 1;

5326         return fixed_type_or_null (TREE_OPERAND (instance, 0), nonnull, cdtorp);

5327  

5328       case COMPONENT_REF:

5329         return fixed_type_or_null (TREE_OPERAND (instance, 1), nonnull, cdtorp);

5330  

5331       case VAR_DECL:

5332       case FIELD_DECL:

5333         if (TREE_CODE (TREE_TYPE (instance)) == ARRAY_TYPE

5334            && IS_AGGR_TYPE (TREE_TYPE (TREE_TYPE (instance))))

5335         {

5336           if (nonnull)

5337             *nonnull = 1;

5338           return TREE_TYPE (TREE_TYPE (instance));

5339         }

5340          /* fall through...  */

5341       case TARGET_EXPR:

5342       case PARM_DECL:

5343       case RESULT_DECL:

5344         if (IS_AGGR_TYPE (TREE_TYPE (instance)))

5345         {

5346           if (nonnull)

5347             *nonnull = 1;

5348           return TREE_TYPE (instance);

5349         }

5350         else if (instance == current_class_ptr )

5351         {

5352           if (nonnull)

5353             *nonnull = 1;

5354          

5355            /* if we're in a ctor or dtor, we know our type.  */

5356           if (DECL_LANG_SPECIFIC (current_function_decl )

5357              && (DECL_CONSTRUCTOR_P (current_function_decl )

5358                    || DECL_DESTRUCTOR_P (current_function_decl )))

5359           {

5360             if (cdtorp)

5361               *cdtorp = 1;

5362             return TREE_TYPE (TREE_TYPE (instance));

5363           }

5364         }

5365         else if (TREE_CODE (TREE_TYPE (instance)) == REFERENCE_TYPE)

5366         {

5367           /* Reference variables should be references to objects.  */

5368           if (nonnull)

5369             *nonnull = 1;

5370         

5371           /* DECL_VAR_MARKED_P is used to prevent recursion; a

5372             variable's initializer may refer to the variable

5373             itself.  */

5374           if (TREE_CODE (instance) == VAR_DECL

5375              && DECL_INITIAL (instance)

5376              && !DECL_VAR_MARKED_P (instance))

5377           {

5378             tree type;

5379             DECL_VAR_MARKED_P (instance) = 1;

5380             type = fixed_type_or_null (DECL_INITIAL (instance),

5381                                   nonnull, cdtorp);

5382             DECL_VAR_MARKED_P (instance) = 0;

5383             return type;

5384           }

5385         }

5386         return NULL_TREE;

5387  

5388       default :

5389         return NULL_TREE;

5390     }

5391   }

 

来到这里, fixed_type_p 0 ,如果 expr 的类型是不确定的(可能使用了多态);或者是 -1 ,如果 expr 是构造函数或析构函数;否则就是 1 ,因为类型能确定。而 v_binfo 不是 NULL ,如果涉及了虚拟基类。

 

build_base_path (continue)

 

293      if (want_pointer && !nonnull)

294        null_test = build (EQ_EXPR, boolean_type_node, expr, integer_zero_node);

295     

296      offset = BINFO_OFFSET (binfo);

 

如果没有发现虚拟基类, 296 行得到的 offset 就是所期望的。下面的代码产生了表达式(以例 4 为例的伪代码形式):

*(&b + offset (A));

而如果 b 一开始是一个指针,将产生下面的代码来防护 NULL 指针(以例 5 为例):

pa? (pa + offset (A)) : 0;

 

build_base_path (continue)

 

350      target_type = code == PLUS_EXPR ? BINFO_TYPE (binfo) : BINFO_TYPE (d_binfo);

351     

352      target_type = cp_build_qualified_type

353        (target_type, cp_type_quals (TREE_TYPE (TREE_TYPE (expr))));

354      ptr_target_type = build_pointer_type (target_type);

355      if (want_pointer)

356        target_type = ptr_target_type;

357     

358      expr = build1 (NOP_EXPR, ptr_target_type, expr);

359   

360      if (!integer_zerop (offset))

361        expr = build (code, ptr_target_type, expr, offset);

362      else

363        null_test = NULL;

364     

365      if (!want_pointer)

366        expr = build_indirect_ref (expr, NULL);

367   

368      if (null_test)

369        expr = build (COND_EXPR, target_type, null_test,

370                   build1 (NOP_EXPR, target_type, integer_zero_node),

371                    expr);

372   

373      return expr;

374    }

 

不过,如果转换涉及虚拟基类,并且 expr 的动态类型与静态类型不相同,在 296 行得到 offset 只是开始。

 

build_base_path (continue)

 

298      if (v_binfo && fixed_type_p <= 0)

299      {

300        /* Going via virtual base V_BINFO. We need the static offset

301           from V_BINFO to BINFO, and the dynamic offset from D_BINFO to

302          V_BINFO. That offset is an entry in D_BINFO's vtable.  */

303        tree v_offset;

304   

305        if (fixed_type_p < 0 && in_base_initializer )

306        {

307          /* In a base member initializer, we cannot rely on

308            the vtable being set up. We have to use the vtt_parm.  */

309          tree derived = BINFO_INHERITANCE_CHAIN (v_binfo);

310          

311           v_offset = build (PLUS_EXPR, TREE_TYPE (current_vtt_parm ),

312                        current_vtt_parm , BINFO_VPTR_INDEX (derived));

313          

314          v_offset = build1 (INDIRECT_REF,

315                         TREE_TYPE (TYPE_VFIELD (BINFO_TYPE (derived))),

316                         v_offset);

317          

318        }

319        else

320          v_offset = build_vfield_ref (build_indirect_ref (expr, NULL),

321                                 TREE_TYPE (TREE_TYPE (expr)));

322         

323        v_offset = build (PLUS_EXPR, TREE_TYPE (v_offset),

324                      v_offset,  BINFO_VPTR_FIELD (v_binfo));

325        v_offset = build1 (NOP_EXPR,

326                       build_pointer_type (ptrdiff_type_node ),

327                        v_offset);

328        v_offset = build_indirect_ref (v_offset, NULL);

329   

330        offset = convert_to_integer (ptrdiff_type_node ,

331                               size_diffop (offset,

332                                         BINFO_OFFSET (v_binfo)));

333   

334        if (!integer_zerop (offset))

335          v_offset = build (code, ptrdiff_type_node , v_offset, offset);

336   

337        if (fixed_type_p < 0)

338          /* Negative fixed_type_p means this is a constructor or destructor;

339            virtual base layout is fixed in in-charge [cd]tors, but not in

340            base [cd]tors.  */

341          offset = build (COND_EXPR, ptrdiff_type_node ,

342                      build (EQ_EXPR, boolean_type_node,

343                           current_in_charge_parm , integer_zero_node),

344                      v_offset,

345                      BINFO_OFFSET (binfo));

346        else

347          offset = v_offset;

348      }

 

如果中间有虚拟基类 那么必须使用 vtable 来进行派生类与基类间的转换。上面在 305 行的 in_base_initializer 如果正在处理一个基类初始值 是非 0 值。在这个情形下还没有创建派生类的 vtable ,因此需要占位符 current_vtt_parm 来表示涉及 vtable

函数 build_indirect_ref 为指针类型或引用类型构建 INDIRECT_REF 。例如,如果 p 是一个指针,那么这个 INDIRECT_REF 节点代表表达式“ *p ”。

 

2028   tree

2029   build_indirect_ref (tree ptr, const char *errorstring)                                           in typeck.c

2030   {

2031     tree pointer, type;

2032  

2033     if (ptr == error_mark_node)

2034       return error_mark_node;

2035  

2036     if (ptr == current_class_ptr )

2037       return current_class_ref ;

2038  

2039     pointer = (TREE_CODE (TREE_TYPE (ptr)) == REFERENCE_TYPE

2040             ? ptr : decay_conversion (ptr));

2041     type = TREE_TYPE (pointer);

2042  

2043     if (TYPE_PTR_P (type) || TREE_CODE (type) == REFERENCE_TYPE)

2044     {

2045        /* [expr.unary.op]

2046       

2047         If the type of the expression is "pointer to T," the type

2048         of the result is "T."  

2049  

2050         We must use the canonical variant because certain parts of

2051         the back end, like fold, do pointer comparisons between

2052         types.  */

2053       tree t = canonical_type_variant (TREE_TYPE (type));

2054  

2055       if (VOID_TYPE_P (t))

2056       {

2057         /* A pointer to incomplete type (other than cv void) can be

2058           dereferenced [expr.unary.op]/1  */

2059         error ("`%T' is not a pointer-to-object type", type);

2060         return error_mark_node;

2061       }

2062       else if (TREE_CODE (pointer) == ADDR_EXPR

2063               && same_type_p (t, TREE_TYPE (TREE_OPERAND (pointer, 0))))

2064         /* The POINTER was something like `&x'.  We simplify `*&x' to

2065           `x'.  */

2066         return TREE_OPERAND (pointer, 0);

2067       else

2068       {

2069         tree ref = build1 (INDIRECT_REF, t, pointer);

2070  

2071         /* We *must* set TREE_READONLY when dereferencing a pointer to const,

2072           so that we get the proper error message if the result is used

2073           to assign to. Also, &* is supposed to be a no-op.  */

2074         TREE_READONLY (ref) = CP_TYPE_CONST_P (t);

2075         TREE_THIS_VOLATILE (ref) = CP_TYPE_VOLATILE_P (t);

2076         TREE_SIDE_EFFECTS (ref)

2077            = (TREE_THIS_VOLATILE (ref) || TREE_SIDE_EFFECTS (pointer));

2078         return ref;

2079       }

2080     }

2081     /* `pointer' won't be an error_mark_node if we were given a

2082       pointer to member, so it's cool to check for this here.  */

2083     else if (TYPE_PTR_TO_MEMBER_P (type))

2084       error ("invalid use of `%s' on pointer to member", errorstring);

2085     else if (pointer != error_mark_node)

2086     {

2087       if (errorstring)

2088         error ("invalid type argument of `%s'", errorstring);

2089       else

2090         error ("invalid type argument");

2091     }

2092     return error_mark_node;

2093   }

 

我们已经看到 TYPE_VFIELD 是由编译器为包含 vtable 的类产生的 人造 域。现在它也作为一个占位符 后面它将为真正的地址所替代。

 

115     tree

116     build_vfield_ref (tree datum, tree type)                                                             in call.c

117     {

118       if (datum == error_mark_node)

119         return error_mark_node;

120   

121      if (TREE_CODE (TREE_TYPE (datum)) == REFERENCE_TYPE)

122        datum = convert_from_reference (datum);

123   

124      if (TYPE_BASE_CONVS_MAY_REQUIRE_CODE_P (type)

125          && !same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (datum), type))

126         datum = convert_to_base (datum, type, /*check_access=*/ false);

127   

128      return build (COMPONENT_REF, TREE_TYPE (TYPE_VFIELD (type)),

129                 datum, TYPE_VFIELD (type));

130    }

 

记得在 324 行的 BINFO_VPTR_FIELD 为虚拟基类设置,其中是一个 INTEGER_CST ,它是 vtable 中一个项的索引,其中记录了从派生类的基址到该虚拟基类的偏移量(为什么不是 BINFO_OFFSET (vbase) ?因为 C++ 标准要求通过 BINFO_VPTR_FIELD 访问虚拟基类)。

注意到如果 binfo v_binfo 继承,那么在 330 行的 offset 得到由下图所示的 2 个基类间的相对偏移量(红色指针 是在 330 行得到的 offset )。

t

然后 334 行引入了一个细微的优化,注意到作为 MINUS_EXPR code ,连同非空的 v_binfo 是不被允许的,它在 280 行给出错误消息。因此在 335 行,在右手边的 v_offset 通过 vtable 指向该虚拟基类的 binfo ,并加上 330 行得到的 offset ,在左手边的 v_offset 因而指向感兴趣的 binfo 。接下来 offset 被相应地更新,来记录到最后派生类的偏移量。

对于我们的例 5 ,最后产生的 expr 应该是:

(pa + offset (A))? (pa + offset (A)): 0;

回到 get_member_function_from_ptrfunc instance_ptr 现在是上面由 build_base_path 返回的语句。考虑下面的例子,它给出结果“ C::f ”(注意到在类 C 中, B1 B2 A f vtable 项都被 C::f 使用合适的 thunk 覆盖,它们在这里给我们作出多态的保证):

class A { public :                                                                                   - 6

    virtual A* f () { printf ("A::f/n"); return 0; }

    virtual A* f1 () { printf ("A::f1/n"); return 0; }

virtual ~A() {}

};

class B1 : virtual public A { public : virtual B1* f1 () { printf ("B1::f1/n"); return 0; } };

class B2 : virtual public A {};

class C: public B1, public B2 { public : virtual C* f () { printf ("C::f/n"); return 0; } };

 

int main() {

B2 *pc = new C; // no matter declare pc as A*, B1*, B2*, or C*, get same result, as // they all derive from A, and pfn is declared as within A

    A* (A::*pfn) () = &B2::f; // same result, use either B1, B2, A, or C at rhs

    (pc->*pfn)();

    delete pc;

    return 0;

}

对于这个例子, instance_ptr 保存了把 pa B2* 调整到 A* 的语句(“ B2 *pc = new C; ”携带一个已经从 C* 转换到 B2* 的指针);而下面在 2357 行得到的 delta 记录了由语句“ A* (A::*pfn) () = &B2::f; ”确定的偏移量(它是 0 因为该语句表示一个从 A A 的没有意义的调整量)。那么通过表达式:“ instance_ptr + delta ”,找出定义了由该方法指针指定的函数的基类;因此 idx 就是用于该虚函数(或对于非虚函数,转换为 vtable 类型的函数地址)的 vtable 的索引。

 

get_member_function_from_ptrfunc (continue)

 

2383       /* ...and then the delta in the PMF.  */

2384       instance_ptr = build (PLUS_EXPR, TREE_TYPE (instance_ptr),

2385                        instance_ptr, delta);

2386  

2387       /* Hand back the adjusted 'this' argument to our caller.  */

2388       *instance_ptrptr = instance_ptr;

2389  

2390       /* Next extract the vtable pointer from the object.  */

2391       vtbl = build1 (NOP_EXPR, build_pointer_type (vtbl_ptr_type_node),

2392                   instance_ptr);

2393       vtbl = build_indirect_ref (vtbl, NULL);

2394  

2395       /* Finally, extract the function pointer from the vtable.  */

2396       e2 = fold (build (PLUS_EXPR, TREE_TYPE (vtbl), vtbl, idx));

2397       e2 = build_indirect_ref (e2, NULL);

2398       TREE_CONSTANT (e2) = 1;

2399  

2400       /* When using function descriptors, the address of the

2401         vtable entry is treated as a function pointer.  */

2402       if (TARGET_VTABLE_USES_DESCRIPTORS)

2403         e2 = build1 (NOP_EXPR, TREE_TYPE (e2),

2404                   build_unary_op (ADDR_EXPR, e2, /*noconvert=*/ 1));

2405  

2406       TREE_TYPE (e2) = TREE_TYPE (e3);

2407       e1 = build_conditional_expr (e1, e2, e3);

2408        

2409       /* Make sure this doesn't get evaluated first inside one of the

2410         branches of the COND_EXPR.  */

2411       if (instance_save_expr)

2412         e1 = build (COMPOUND_EXPR, TREE_TYPE (e1),

2413                   instance_save_expr, e1);

2414  

2415       function = e1;

2416     }

2417     return function;

2418   }

 

在类布局一节中,我们已经看到 vptr 总是放置在该类的开头。因此对于例 5 ,所产生的代码片段,看起来就像下面:

idx = (vtable_index_type) pfn;           // pfn returned by PFN_FROM_PTRMEMFUNC

instance_ptr = instance_ptr? instance_ptr + offset (A): 0;

(idx & 1)? *(((vtbl_ptr_type_node) instance_ptr) + (idx-1)): pfn;

这时,对于一个方法指针,我们已经知道所期待的是哪个函数,而且指出该函数位置的表达式已经准备好了,并返回给 build_addr_func ,然后立即返回给 build_function_call 。注意到下面的 function 现在保存了定位该函数的这个表达式,并且其类型是该函数的指针。

 

build_function_call (continue)

 

2464     if (function == error_mark_node)

2465       return error_mark_node;

2466  

2467     fntype = TREE_TYPE (function);

2468  

2469     if (TYPE_PTRMEMFUNC_P (fntype))

2470     {

2471       error ("must use .* or ->* to call pointer-to-member function in `%E (...)'",

2472             original);

2473       return error_mark_node;

2474     }

2475  

2476     is_method = (TREE_CODE (fntype) == POINTER_TYPE

2477                && TREE_CODE (TREE_TYPE (fntype)) == METHOD_TYPE);

2478  

2479     if (!((TREE_CODE (fntype) == POINTER_TYPE

2480            && TREE_CODE (TREE_TYPE (fntype)) == FUNCTION_TYPE)

2481         || is_method

2482         || TREE_CODE (function) == TEMPLATE_ID_EXPR))

2483     {

2484       error ("`%E' cannot be used as a function", original);

2485       return error_mark_node;

2486     }

2487  

2488     /* fntype now gets the type of function pointed to.  */

2489     fntype = TREE_TYPE (fntype);

2490  

2491     /* Convert the parameters to the types declared in the

2492       function prototype, or apply default promotions.  */

2493  

2494     coerced_params = convert_arguments (TYPE_ARG_TYPES (fntype),

2495                                     params, fndecl, LOOKUP_NORMAL);

2496     if (coerced_params == error_mark_node)

2497       return error_mark_node;

2498  

2499     /* Check for errors in format strings.  */

2500  

2501     if (warn_format )

2502       check_function_format (NULL, TYPE_ATTRIBUTES (fntype), coerced_params);

2503  

2504     /* Recognize certain built-in functions so we can make tree-codes

2505       other than CALL_EXPR. We do this when it enables fold-const.c

2506       to do something useful.  */

2507  

2508     if (TREE_CODE (function) == ADDR_EXPR

2509         && TREE_CODE (TREE_OPERAND (function, 0)) == FUNCTION_DECL

2510         && DECL_BUILT_IN (TREE_OPERAND (function, 0)))

2511     {

2512       result = expand_tree_builtin (TREE_OPERAND (function, 0),

2513                               params, coerced_params);

2514       if (result)

2515         return result;

2516     }

2517  

2518     return build_cxx_call (function, params, coerced_params);

2519   }

 

因此在 2489 行的 fntype 指向所使用的函数的类型。然后需要根据参数声明,为实参执行转换。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值