您的位置:首页 > 编程语言

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

2011-02-19 11:36 330 查看

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

)。



然后
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

指向所使用的函数的类型。然后需要根据参数声明,为实参执行转换。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: