Studying note of GCC-3.4.6 source (162 - continue)

At line 5350 current_class_ptr is the PARM_DECL for the `this' pointer, when we're processing a member function. See that in below only the type of instance can be known for sure it returns non-null. Compared with condition at line 5281, which indicates a pointer, and in C++, it can be variant types in the class hierarchy to support polymorphism. So it has to return NULL_TREE as result.

 

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   }

 

Arriving here, fixed_type_p is 0 if type of expr is unknown for certainty (polymorphism may be in used), or is -1 if expr is constructor or destructor, otherwise is 1 as type is known for certainty. And v_binfo is not NULL if there is virtual base involved.

 

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);

 

If no vritual base found, offset gotten at line 296 is all wanted. Code in below then generates the expression (taking example 4 as instance in pseudo code):

*(&b + offset (A));

And if b originally is a pointer, it will generate code to guard NULL pointer too as below (taking example 5 as instance):

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    }

 

However, if the conversion involves virtual base and expr ’s dynamic type isn’t the same as static type, offset gotten at line 296 is just the beginning.

 

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      }

 

If there is virtual base between and it must use vtable to do the conversion between the derived and the base, above at line 305 in _base_initializer is nonzero if it is handling a base initializer, for which case vtable is not created. So it needs use the placeholder current_vtt_parm to indicate to involvement of the vtable.

Function build_indirect_ref builds INDIRECT_REF for pointer type or reference type. For example, if p is a pointer, then the INDIRECT_REF node stands for the expression “*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   }

 

As we have seen TYPE_VFIELD is the artificial field generated by the compiler for class containing vtable. Now it also acts as a placeholder, it will be replaced with the real address of vtable later.

 

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    }

 

Remember BINFO_VPTR_FIELD at line 324 is set for virtual base, in which is an INTEGER_CST indicating the index of the entry in the vtable recording the offset of the virtual base from the base of the derived (why not BINFO_OFFSET (vbase)? because C++ standard requires accessing virtual base by help of BINFO_VPTR_FIELD).

Note that if binfo is derived by v_binfo , then offset at line 330 gets the relative offset between these two bases as indicated by below figure (the red arrow is the offset gotten at line 330).

Then line 334 introduces a minor optimizaton, note that code of MINUS_EXPR with v_binfo not NULL is not allowed and error message is given at line 280. So at line 335, v_offset at right hand side refers to the binfo of the virtual base via vtable, and plus offset gotten at line 330, v_offset at left hand side then points to the binfo in interesting. Following offset is updated accordingly to record the offset into the most derived class.

For our example 5, expr generated finally would be:

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

Back into get_member_function_from_ptrfunc , instance_ptr is now the above statement returned by build_base_path . Consider following example, it gives output “C::f” (note that in class C, vtable entry of f of B1, B2, and A are all overrided as C::f with appropriate thunk, which give us promise here):

class A { public :                                                                                   example - 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;

}

For this example, instance_ptr holds the statement to adjust pa from B2* to A* (“B2 *pc = new C;” carries a pointer converted from C* to B2* already); and delta below gotten at line 2357 records the offset determined by statement “A* (A::*pfn) () = &B2::f;”  (it is 0 because the statement indicate a meaningless adjustment from A to A). Then by expression: “instance_ptr + delta”, it locates the base that defining the function specified by the pointer to method; then idx is the index into the vtable for the virtual function (or function address converting in type of vtable for non-virtual function).

 

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   }

 

In section of class layout, we have seen that vptr is always placed at the beginning of the class. So the code snippet generated would be like below for example 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;

At this point, for a pointer-to-method, we have known which function is expected, and expression to locate the location of the function is ready and returned to build_addr_func , then immediately to build_function_call . Note that function below now holds the expression for locating the function and its type is the pointer of the 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   }

 

So fntype at line 2489 refers to the type of the function in used. Then it needs do conversion for the arguments according to the parameters declaration.

你可能感兴趣的:(Studying note of GCC-3.4.6 source (162 - continue))