现在的位置: 首页 > 综合 > 正文

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

2013年06月11日 ⁄ 综合 ⁄ 共 9192字 ⁄ 字号 评论关闭

5.13.4.7.1.       



为函数调用产生代码
5.13.4.7.1.1. 



构建获取函数地址的表达式

当我们写出类似:
f (a, b);
的代码来调用
f

,编译器需要努力使得这个调用以期望的方式可行。下面的函数泄露了编译器的所作所为。

 

2420  


tree

2421  


build_function_call

(tree function, tree
params)                                         

in typeck.c

2422  


{

2423  


 
tree fntype, fndecl;

2424  


 
tree coerced_params;

2425  


 
tree result;

2426  


 
tree name = NULL_TREE, assembler_name =
NULL_TREE;

2427  


 
int is_method;

2428  


 
tree original = function;

2429  

2430  


 
/* build_c_cast
puts on a NOP_EXPR to make the result not an lvalue.

2431  


   
Strip such NOP_EXPRs, since
FUNCTION is used in non-lvalue context. 

*/

2432  


 
if (TREE_CODE (function) == NOP_EXPR

2433  


     
&& TREE_TYPE (function) ==
TREE_TYPE (TREE_OPERAND (function, 0)))

2434  


   
function = TREE_OPERAND (function, 0);

2435  

2436  


 
if (TREE_CODE (function) == FUNCTION_DECL)

2437  


 
{

2438  


   
name = DECL_NAME (function);

2439  


   
assembler_name = DECL_ASSEMBLER_NAME
(function);

2440  

2441  


   
mark_used
(function);

2442  


   
fndecl = function;

2443  

2444  


   
/* Convert
anything with function type to a pointer-to-function. 
*/

2445  


  
 
if (pedantic

&& DECL_MAIN_P (function))

2446  


     
pedwarn ("ISO C++ forbids calling
`::main' from within program");

2447  

2448  


   
/* Differs from
default_conversion by not setting TREE_ADDRESSABLE

2449  


     
(because calling an inline
function does not mean the function

2450  


     
needs to be separately
compiled). 
*/

2451  


     

2452  


   
if (DECL_INLINE (function))

2453  


     
function = inline_conversion
(function);

2454  


   
else

2455  


     
function = build_addr_func
(function);

2456  


 
}

2457  


 
else

2458  


 
{

2459  


   
fndecl = NULL_TREE;

2460  

2461  


   
function = build_addr_func
(function);

2462  


 
}

 

在调用点内联函数被展开。因此它不能在该编译单元之外分开编译,也不能把它标记为
TREE_ADDRESSABLE
。在这里
ADDR_EXPR
作为这个调用语句的结果被产生出来。

 

1464  


tree

1465  


inline_conversion

(tree exp)                                                                            

in typeck.c

1466  


{

1467  


 
if (TREE_CODE (exp) == FUNCTION_DECL)

1468  


   
exp = build1
(ADDR_EXPR, build_pointer_type
(TREE_TYPE
(exp)), exp);

1469  

1470  


 
return
exp;

1471  


}

 

当通过指针来访问内联函数(以形式
x.*p
,其中
x

是可取址的,因而
build_address


(它要求可取址的对象作为参数)可以工作),访问性检查是要求的;而对于调用普通函数,需要
decay_conversion


来进行函数到指针的转换。

 

178   


tree

179   


build_addr_func

(tree function)                                                                             
in
tree.c

180   


{

181   


 
tree type = TREE_TYPE (function);

182   

183   


 
/* We have to do
these by hand to avoid real pointer to member

184   


   
functions. 
*/

185   


 
if (TREE_CODE (type) == METHOD_TYPE)

186   


 
{

187   


   
if (TREE_CODE (function) == OFFSET_REF)

188   


   
{

189   


     
tree object = build_address
(TREE_OPERAND (function, 0));

190   


     
return
get_member_function_from_ptrfunc
(&object,

191   


                    
                              
   
TREE_OPERAND (function, 1));

192   


   
}

193   


   
function = build_address
(function);

194   


 
}

195   


 
else

196   


   
function = decay_conversion (function);

197   

198   


 
return
function;

199 


}

 

上面的
OFFSET_REF
被用在两种情形中:

1.
一个具有形式‘
A::m
’的表达式,其中‘
A
’是一个类,而‘
m
’是一个非静态成员。在这个情况下,操作数
0
将是一个
TYPE
(对应‘
A
’),而操作数
1
将是一个
FIELD_DECL

BASELINK
,或
TEMPLATE_ID_EXPR
(对应‘
m
’)。

如果其地址被获取了,这个表达式就是一个指向成员的指针,但如果其地址没有被获取,则仅表示该对象的一个成员。

这个形式仅被用在解析阶段;一旦语法分析完成,该形式就要被消除。

2.
一个具有形式‘
x.*p
’的表达式。在这个情况下,操作数
0
将是一个对应‘
x
’的表达式,而操作数
1
将是一个具有指向成员的指针类型的表达式。

显然,类方法的指针遵守第二个规则。对于非静态方法的调用,记得还有一个隐含的“
const this*
”,上面的
189
行,
object

就是这个
this

指针。另外在
C++
中,有一个奇特的类型

方法类型【
14
】,例如:“
typedef void (C::*Cf) () =
&C::f;


Cf


C
(假定
C
是一个类)的一个方法类型,它没有参数,并返回
void

2324
行的谓词
TYPE_PTRMEMFUNC_P
对这样的类型返回
true
。在
2327
行的
TYPE_PTRMEMFUNC_FN_TYPE
获取关联的方法类型。

 

2318  


tree

2319  


get_member_function_from_ptrfunc

(tree
*instance_ptrptr, tree function)   
in typeck.c

2320  


{

2321  


 
if (TREE_CODE (function) == OFFSET_REF)

2322  


   
function = TREE_OPERAND (function, 1);

2323  

2324  


 
if (TYPE_PTRMEMFUNC_P (TREE_TYPE (function)))

2325  


 
{

2326  


   

tree idx, delta, e1, e2, e3, vtbl, basetype;

2327  


   
tree fntype = TYPE_PTRMEMFUNC_FN_TYPE
(TREE_TYPE (function));

2328  

2329  


   
tree instance_ptr = *instance_ptrptr;

2330  


   
tree instance_save_expr = 0;

2331  


   
if (instance_ptr == error_mark_node)

2332  


   
{

2333  


     
if (TREE_CODE (function) == PTRMEM_CST)

2334  


    
   
{

2335  


    
     

/* Extracting the function address from a pmf
is only

2336  


 

        
allowed with -Wno-pmf-conversions. It
only works for

2337  


          
pmf constants. 
*/

2338  


    
     

e1 = build_addr_func
(PTRMEM_CST_MEMBER
(function));

2339  


    
     
e1 = convert (fntype, e1);

2340  


    
     
return
e1;

2341  


     
}

2342  


     
else

2343  


     
{

2344  


       
error ("object missing in use of
`%E'", function);

2345  


       
return
error_mark_node;

2346  


     
}

2347  


   
}

2348  

2349  


   
if (TREE_SIDE_EFFECTS (instance_ptr))

2350  


     

instance_ptr = instance_save_expr =

save_expr


(instance_ptr);

2351  

2352  


   
if (TREE_SIDE_EFFECTS (function))

2353  


     
function = save_expr (function);

2354  

2355  


   
/* Start by
extracting all the information from the PMF itself. 
*/

2356  


   
e3 = PFN_FROM_PTRMEMFUNC
(function);

 

如果
instance_ptr


error_mark_node

,在这里
function

应该是一个
PTRMEM_CST
(成员指针常量的节点),其中的
PTRMEM_CST_CLASS

C


RECORD_TYPE
,而
PTRMEM_CST_MEMBER
是对应
f


*_DECL
(不过我想不出一个会产生这样节点的正常情形,因为上面的
build_address



189
行),对于
instance_ptr

,总是成功的,除非传入的节点是
error_mark_node

,这意味着在这个过程中某些东西出错了。即便对于把方法指针转换到函数指针的情形,似乎也不应该到这里来,其细节可以在【
6
】中看到)。


C++
中,成员方法的指针(
PMF
)使用一个差不多可以处理所有可能调用机制的宽指针来实现;
PMF
需要保存关于如何调整‘
this
’指针的信息,并且如果被指向的函数是虚函数,在何处查找
vtable
,及在该
vtable
的何处查找这个成员方法。如果你在一个内部循环中使用
PMF
,你应该三思。如果别无选择,你可以从该指针提取将为给定对象
/PMF
对所调用的函数,并在这个内部循环中直接调用这个函数,以节约一些时间。

注意到你仍将为通过一个函数指针进行的函数调用付出代价;在绝大多数架构上,这样的一个调用会挫败
CPU
的分支预测特性。这对于普通的虚函数调用也是成立的。

这个扩展的语法是

extern
A a;

extern
int (A::*fp)();

typedef
int (*fptr)(A *);

fptr p =
(fptr)(a.*fp);

对于
PMF
常量(


具有形式

&Klasse::Member

的表达式
),
不需要对象来获取该函数的地址。它们可以被直接转换到函数指针:

fptr p1 =
(fptr)(&A::foo);

你必须指定‘
-Wno-pmf-conversions
’来使用这个扩展。

而如果
instance_ptr

不是
error_mark_node

,在
2356
行的
function

是具有成员指针类型的表达式,而
PFN_FROM_PTRMEMFUNC
具有如下定义。

 

2563  


#define
PFN_FROM_PTRMEMFUNC
(NODE)
pfn_from_ptrmemfunc
((NODE))

 

5637  


tree

5638  


pfn_from_ptrmemfunc

(tree t)                       
                                         
in typeck.c

5639  


{

5640  


 
if (TREE_CODE (t) == PTRMEM_CST)

5641  


 
{

5642  


   
tree delta;

5643  


   
tree pfn;

5644  


     

5645  


   
expand_ptrmemfunc_cst
(t, &delta, &pfn);

5646  


   
if (pfn)

5647  


     
return
pfn;

5648  


 
}

5649  

5650  


 
return
build_ptrmemfunc_access_expr
(t, pfn_identifier

);

5651  


}

 

上面的函数获取了由这个成员指针类型的表达式或
PTRMEM_CST
结构所引用的方法,并且它需要调整
this
指针,或适当地操控
vtable

首先,如果上面的实参
function

具有形如:“
&C::f
”的形式,它是应该
PTRMEM_CST
节点。不过在构建函数调用的上下文中,
function

不会是
PTRMEM_CST
;而
function


PTRMEM_CST
的一个场景是形如:“
void (B::*pfn)() = &B::f;
”的语句,然后
expand_ptrmemfunc_cst


将被
build_ptrmemfunc


调用来从“
&B::f
”的
PTRMEM_CST
中,得到用于方法指针类型“
void (B::*)()
”的结构(见下面)的
__pfn


__delta

不管怎么说,这值得我们花一些时间看一下怎样为方法指针确定
__pfn


__delta

域。下面在
5587
行的
TYPE_PTRMEMFUNC_OBJECT_TYPE
为形如“
int (A::*)(double)
”的类型返回‘
A
’。

 

5574  


void

5575  


expand_ptrmemfunc_cst

(tree cst, tree
*delta, tree *pfn)                                  
in
typeck.c

5576  


{

5577  


 
tree type = TREE_TYPE (cst);

5578  


 
tree fn = PTRMEM_CST_MEMBER (cst);

5579  


 
tree ptr_class, fn_class;

5580  

5581  


 
my_friendly_assert (TREE_CODE (fn) ==
FUNCTION_DECL, 0);

5582  

5583  


 
/* The class that
the function belongs to. 
*/

5584  


 
fn_class = DECL_CONTEXT (fn);

5585  

5586  


 
/* The class that
we're creating a pointer to member of. 

*/

5587  


 
ptr_class = TYPE_PTRMEMFUNC_OBJECT_TYPE
(type);

5588  

5589  


 
/* First, calculate
the adjustment to the function's class. 

*/

5590  


 
*delta = get_delta_difference
(fn_class, ptr_class, /*force=*/
0);

 

考虑下面的例子:

class
A { public
:
void f (); };

class
B: public
A {};

void (B::*pfn)
() = &B::f;

对于这个例子,
fn_class

将是
A

(来自“
&B::f
”部分),
ptr_class

将是
B

(来自“
vod (B::*pfn) ()
”部分),而
fn

将是
f

。因此,首先需要知道派生类与定义该函数的基类间的调整量。在下面的函数中,参数
force

如果是
false
,表示不允许反向的转换(从
to

from
)。

 

5392  


static
tree

5393  


get_delta_difference

(tree from, tree
to, int force)                                      
in
typeck.c

5394  


{

5395  


 
tree binfo;

5396  


 
tree virt_binfo;

5397  


 
base_kind kind;

5398  


 

5399  


 
binfo = lookup_base
(to, from, ba_check, &kind);

5400  


 
if (kind == bk_inaccessible || kind ==
bk_ambig)

5401  


 
{

5402  


   
error ("  
in pointer to member function
conversion");

5403  


   
goto
error;

5404  


 
}

5405  


 
if (!binfo)

5406  


 
{

5407  


   
if (!force)

5408  


   
{

5409  


     
error_not_base_type (from, to);

5410  


   
  
error ("  
in pointer to member conversion");

5411  


     
goto
error;

5412  


   
}

5413  


   
binfo = lookup_base
(from, to, ba_check, &kind);

5414  


   
if (!binfo)

5415  


     
goto
error;

5416  


   
virt_binfo = binfo_from_vbase
(binfo);

5417  


   
if (virt_binfo)

5418  


   
{

5419  


     
/* This is a
reinterpret cast, we choose to do nothing. 

*/

5420  


     
warning ("pointer to member cast via
virtual base `%T'",

5421  


     
BINFO_TYPE (virt_binfo));

5422  


     
goto
error;

5423  


   
}

5424  


   
return
fold
(convert_to_integer (ptrdiff_type_node

,

5425  


                             
size_diffop
(size_zero_node,

5426  


                                       

BINFO_OFFSET (binfo))));

5427  


 
}

5428  

5429  


 

virt_binfo =

binfo_from_vbase


(binfo);

5430  


 
if (!virt_binfo)

5431  


   
return
fold
(convert_to_integer (ptrdiff_type_node

, BINFO_OFFSET (binfo)));

5432  

5433  


 
/* This is a
reinterpret cast, we choose to do nothing. 

*/

5434  


 
if (force)

5435  


   
warning ("pointer to member cast via
virtual base `%T'",

5436  


            
BINFO_TYPE (virt_binfo));

5437  


 
else

5438  


   
error ("pointer to member conversion
via virtual base `%T'",

5439  


    
  
BINFO_TYPE (virt_binfo));

5440  

5441  


error:

5442  


 
return
fold
(convert_to_integer(ptrdiff_type_node

, integer_zero_node));

5443  


}

 

注意到参数
from


to

必须具有继承关系。另外,如果转换涉及虚拟基类,这是仅可以被
reinterpret_cast
处理的语法,在这里应该被忽略。并且内部对象
ptrdiff_type_node

对应于我们经常在
C/C++
程序中看到的
ptrdiff_t
。考虑这个例子:

class
base { public
: void fb() {} virtual
~base() {} };

class
A : public
base {};

class
B1 : virtual
public
A {};

class
B2 : virtual
public
A {};

class
C: public
B1, public
B2 {};

int main() {

   
C c;

   
void (C::*pfn) () =
&base::fb;

   
(c.*pfn)();

   
return
0;

}

语句

PFN pfn = &base::fb;

导致错误

error: pointer to member
conversion via virtual base ‘A’



。但是如果
PFN

抱歉!评论已关闭.