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

5.13.5.1.3.        准备别名集

构建别名集的入口是下面的 get_alias_set 。类型节点及声明节点可以要求别名分析,不过声明的别名集由其底层类型的别名集确定。在 467 行的条件选出声明的节点。在 C++ 前端, 475 行的钩子 get_alias_set 指向 cxx_get_alias_set 。这个函数主要处理类型定义节点,我们暂时不管这个函数。

 

449  HOST_WIDE_INT

450  get_alias_set (tree t)                                                                                    in alias.c

451  {

452    HOST_WIDE_INT set;

453 

454    /* If we're not doing any alias analysis, just assume everything

455      aliases everything else. Also return 0 if this or its type is

456      an error.  */

457    if (! flag_strict_aliasing || t == error_mark_node

458        || (! TYPE_P (t)

459          && (TREE_TYPE (t) == 0 || TREE_TYPE (t) == error_mark_node)))

460      return 0;

461 

462    /* We can be passed either an expression or a type. This and the

463      language-specific routine may make mutually-recursive calls to each other

464      to figure out what to do. At each juncture, we see if this is a tree

465      that the language may need to handle specially. First handle things that

466      aren't types.  */

467    if (! TYPE_P (t))

468    {

469      tree inner = t;

470      tree placeholder_ptr = 0;

471 

472      /* Remove any nops, then give the language a chance to do

473         something with this tree before we look at it.  */

474      STRIP_NOPS (t);

475      set = (*lang_hooks .get_alias_set ) (t);

476      if (set != -1)

477        return set;

478 

479      /* First see if the actual object referenced is an INDIRECT_REF from a

480         restrict-qualified pointer or a "void *". Replace

481         PLACEHOLDER_EXPRs.  */

482      while (TREE_CODE (inner) == PLACEHOLDER_EXPR

483            || handled_component_p (inner))

484      {

485        if (TREE_CODE (inner) == PLACEHOLDER_EXPR)

486          inner = find_placeholder (inner, &placeholder_ptr);

487        else

488           inner = TREE_OPERAND (inner, 0);

489 

490        STRIP_NOPS (inner);

491      }

492 

493      /* Check for accesses through restrict-qualified pointers.  */

494      if (TREE_CODE (inner) == INDIRECT_REF)

495      {

496        tree decl = find_base_decl (TREE_OPERAND (inner, 0));

497 

498        if (decl && DECL_POINTER_ALIAS_SET_KNOWN_P (decl))

499        {

500          /* If we haven't computed the actual alias set, do it now.  */

501          if (DECL_POINTER_ALIAS_SET (decl) == -2)

502          {

503            tree pointed_to_type = TREE_TYPE (TREE_TYPE (decl));

504 

505            /* No two restricted pointers can point at the same thing.

506               However, a restricted pointer can point at the same thing

507               as an unrestricted pointer, if that unrestricted pointer

508               is based on the restricted pointer. So, we make the

509               alias set for the restricted pointer a subset of the

510               alias set for the type pointed to by the type of the

511               decl.  */

512            HOST_WIDE_INT pointed_to_alias_set

513                = get_alias_set (pointed_to_type);

514 

515            if (pointed_to_alias_set == 0)

516              /* It's not legal to make a subset of alias set zero.  */

517              DECL_POINTER_ALIAS_SET (decl) = 0;

518            else if (AGGREGATE_TYPE_P (pointed_to_type))

519              /* For an aggregate, we must treat the restricted

520                 pointer the same as an ordinary pointer. If we

521                 were to make the type pointed to by the

522                 restricted pointer a subset of the pointed-to

523                 type, then we would believe that other subsets

524                 of the pointed-to type (such as fields of that

525                 type) do not conflict with the type pointed to

526                 by the restricted pointer.   */

527              DECL_POINTER_ALIAS_SET (decl)

528                   = pointed_to_alias_set;

529            else

530            {

531              DECL_POINTER_ALIAS_SET (decl) = new_alias_set ();

532               record_alias_subset (pointed_to_alias_set,

533                               DECL_POINTER_ALIAS_SET (decl));

534            }

535          }

536 

537           /* We use the alias set indicated in the declaration.   */

538          return DECL_POINTER_ALIAS_SET (decl);

539        }

540 

541         /* If we have an INDIRECT_REF via a void pointer, we don't

542           know anything about what that might alias.  */

543        else if (TREE_CODE (TREE_TYPE (inner)) == VOID_TYPE)

544          return 0;

545      }

546 

547      /* Otherwise, pick up the outermost object that we could have a pointer

548         to, processing conversion and PLACEHOLDER_EXPR as above.  */

549      placeholder_ptr = 0;

550      while (TREE_CODE (t) == PLACEHOLDER_EXPR

551            || (handled_component_p (t) && ! can_address_p (t)))

552      {

553        if (TREE_CODE (t) == PLACEHOLDER_EXPR)

554          t = find_placeholder (t, &placeholder_ptr);

555        else

556          t = TREE_OPERAND (t, 0);

557 

558        STRIP_NOPS (t);

559      }

560 

561       /* If we've already determined the alias set for a decl, just return

562         it. This is necessary for C++ anonymous unions, whose component

563         variables don't look like union members (boo!).  */

564      if (TREE_CODE (t) == VAR_DECL

565         && DECL_RTL_SET_P (t) && GET_CODE (DECL_RTL (t)) == MEM)

566        return MEM_ALIAS_SET (DECL_RTL (t));

567 

568      /* Now all we care about is the type.  */

569      t = TREE_TYPE (t);

570    }

 

上面的 INDIRECT_REF 节点,在 C++ 语言中,代表一个使用了指针或引用的表达式(例如, char *p; ),显然其所援引的对象的别名集正是我们需要的。那么 find_base_decl 找出这个表达式所基于的 DECL (例如,在‘ a[i] ’中,这将是‘ a ’)。而如果没有这样的 DECL ,或者不能确定一个唯一的 DECL ,则返回 NULL_TREE

 

361  static tree

362  find_base_decl (tree t)                                                                                in alias.c

363  {

364    tree d0, d1, d2;

365 

366    if (t == 0 || t == error_mark_node || ! POINTER_TYPE_P (TREE_TYPE (t)))

367      return 0;

368 

369    /* If this is a declaration, return it.  */

370    if (TREE_CODE_CLASS (TREE_CODE (t)) == 'd')

371      return t;

372 

373    /* Handle general expressions. It would be nice to deal with

374      COMPONENT_REFs here. If we could tell that `a' and `b' were the

375      same, then `a->f' and `b->f' are also the same.  */

376    switch (TREE_CODE_CLASS (TREE_CODE (t)))

377    {

378      case '1':

379        return find_base_decl (TREE_OPERAND (t, 0));

380 

381      case '2':

382         /* Return 0 if found in neither or both are the same.  */

383        d0 = find_base_decl (TREE_OPERAND (t, 0));

384        d1 = find_base_decl (TREE_OPERAND (t, 1));

385        if (d0 == d1)

386          return d0;

387        else if (d0 == 0)

388          return d1;

389        else if (d1 == 0)

390          return d0;

391        else

392          return 0;

393 

394      case '3':

395        d0 = find_base_decl (TREE_OPERAND (t, 0));

396        d1 = find_base_decl (TREE_OPERAND (t, 1));

397        d2 = find_base_decl (TREE_OPERAND (t, 2));

398 

399        /* Set any nonzero values from the last, then from the first.  */

400        if (d1 == 0) d1 = d2;

401        if (d0 == 0) d0 = d1;

402        if (d1 == 0) d1 = d0;

403        if (d2 == 0) d2 = d1;

404 

405        /* At this point all are nonzero or all are zero. If all three are the

406          same, return it. Otherwise, return zero.  */

407        return (d0 == d1 && d1 == d2) ? d0 : 0;

408 

409      default :

410        return 0;

411    }

412  }

 

如果我们找出基于的 DECL ,回到 498 行的 get_alias_set DECL_POINTER_ALIAS_SET 如果不是 -1 DECL_POINTER_ALIAS_SET_KNOWN_P 则是 true -1 意味着对于该声明还没有计算别名集)。另外如果 DECL_POINTER_ALIAS_SET 返回 -2 ,它表示我们需要为这个指针建造一个唯一的别名集(它仅应用于使用 GNU 扩展关键字 restrict 来约束指针别名的情形,参见 c_apply_type_quals_to_decl ),这需要特别的处理。

restrict 指针限定词可以被应用于一个数据指针来表示,在指针声明的作用域中,所有对这个数据的访问,将仅限于通过这个指针,而不通过其他指针。关键字 __restrict__ 因而使能编译器,在基于给定的一个对象不能通过其它指针来改变的前提下,执行某些优化。例如,声明“ int  f ( int  * restrict  x,  int  * restrict  y) ”意味着由指针 x 指向的地址中的内容,仅能通过这个指针来改变。因此如下计算别名集是安全的。

那么在 get_alias_set 503 行,在这里 decl 是基底 DECL ,它必须具有间接类型(指针或引用),因此 pointed_to_type 保存了间接类型所援引的类型。接着在 512 行, get_alias_set 被递归调用来制定出这个类型的别名集。因此在 518 行,如果 pointed_to_type 指向聚集类型,对于这个类型的别名集应该已经在 512 行的调用中被计算出来,为这个指针使用这个别名集(为聚集类型计算别名集由 record_component_aliases 完成,我们将很快看到)。

而如果该指针不是聚集类型,进入 get_alias_set 529 行的代码块,保守然而正确,把所有被这个类型的指针所指向的声明,组合入以这个指针类型为根节点的别名集树中(不过注意到 decl 的别名集除了本身,不是其它对象的别名,因为它被声明为“ restrict ”)。

那么如果来到 get_alias_set 549 行,要么因为其内部不是 INDIRECT_REF ,或其别名集未知。对于这个情形,我们根据类型节点来计算别名集。

WITH_RECORD_EXPR PLACEHOLDER_EXPR 节点用在那些具有类型,在这些类型的对象中的某些域,包含了一个值用于计算其它域的偏移量或大小,及或该类型的大小,的语言中。这些域的位置及或大小,在相同类型的不同对象中,可以不同;甚至是在其作用域中的同一个对象中也可以这样。在 Ada 中,具有判别( discriminants )的记录类型( record type ),或 Pascal 中的概要类型( schema type )就是这样类型的代表。对于 C++ 前端,我们不需要操心这些节点。

 

get_alias_set (continue)

 

572    /* Variant qualifiers don't affect the alias set, so get the main

573      variant. If this is a type with a known alias set, return it.  */

574    t = TYPE_MAIN_VARIANT (t);

575    if (TYPE_ALIAS_SET_KNOWN_P (t))

576      return TYPE_ALIAS_SET (t);

577 

578    /* See if the language has special handling for this type.  */

579    set = (*lang_hooks .get_alias_set ) (t);

580    if (set != -1)

581      return set;

582 

583    /* There are no objects of FUNCTION_TYPE, so there's no point in

584      using up an alias set for them. (There are, of course, pointers

585      and references to functions, but that's different.)  */

586    else if (TREE_CODE (t) == FUNCTION_TYPE)

587      set = 0;

588 

589    /* Unless the language specifies otherwise, let vector types alias

590      their components. This avoids some nasty type punning issues in

591      normal usage. And indeed lets vectors be treated more like an

592      array slice.  */

593    else if (TREE_CODE (t) == VECTOR_TYPE)

594      set = get_alias_set (TREE_TYPE (t));

595 

596    else

597      /* Otherwise make a new alias set for this type.  */

598      set = new_alias_set ();

599 

600    TYPE_ALIAS_SET (t) = set;

601 

602    /* If this is an aggregate type, we must record any component aliasing

603      information.  */

604    if (AGGREGATE_TYPE_P (t) || TREE_CODE (t) == COMPLEX_TYPE)

605      record_component_aliases (t);

606 

607    return set;

608  }

 

首先,检查是否有语言给出的提示。对于 C++ ,下面的钩子 get_alias_set 指向 cxx_get_alias_set

回忆对于非 POD 类, CLASSTYPE_AS_BASE 是一个去掉虚拟基类的 RECORD_TYPE 节点,并且其 TYPE_CONTEXT 指向该类的 RECORD_TYPE ,因此如果 t 是非 POD 类的 CLASSTYPE_AS_BASE ,断言“ CLASSTYPE_AS_BASE (TYPE_CONTEXT (t)) == t ”是 true (而对于 POD 类, CLASSTYPE_AS_BASE 是该类的 RECORD_TYPE ,因此该断言是 false )。

因此 295 行的条件断定 t 封闭在一个类中,并且它是封闭类的基类部分。

 

292  static HOST_WIDE_INT

293  cxx_get_alias_set (tree t)                                                                            in cp-lang.c

294  {

295    if (TREE_CODE (t) == RECORD_TYPE

296        && TYPE_CONTEXT (t) && CLASS_TYPE_P (TYPE_CONTEXT (t))

297        && CLASSTYPE_AS_BASE (TYPE_CONTEXT (t)) == t)

298      /* The base variant of a type must be in the same alias set as the

299        complete type.  */

300      return get_alias_set (TYPE_CONTEXT (t));

301   

302    if (/* It's not yet safe to use alias sets for some classes in C++.  */

303      ! ok_to_generate_alias_set_for_type (t)

304       /* Nor is it safe to use alias sets for pointers-to-member

305         functions, due to the fact that there may be more than one

306         RECORD_TYPE type corresponding to the same pointer-to-member

307         type.  */

308       || TYPE_PTRMEMFUNC_P (t))

309      return 0;

310 

311    return c_common_get_alias_set (t);

312  }

 

对于来到 302 行的情形, ok_to_generate_alias_set_for_type 检查该类型对于别名是否安全。

首先,具有虚拟基类的类不是别名安全的。例如:

class A { ... };

class B : public virtual A { ... } ;

class C : public virtual A { ... } ;

class D : public B, C { ... } ;

虽然 D 派生自 B ,在 D 中,基类 B 与类 B 的布局不同,即便 B 是空类。记得 250 行的断言 CLASS_TYPE_P ,对于非 POD 类的 CLASSTYPE_AS_BASE ,是 false

类似的,如果一个类含有一个类型是包含虚拟基类的其它类的域,这个类型也不是别名安全的(两者被视为可能别名( may-alias ))。

 

239  static bool

240  ok_to_generate_alias_set_for_type (tree t)                                                    in cp-lang.c

241  {

242    if (TYPE_PTRMEMFUNC_P (t))

243      return true;

244    if (AGGREGATE_TYPE_P (t))

245    {

246      if ((TREE_CODE (t) == RECORD_TYPE) || (TREE_CODE (t) == UNION_TYPE))

247      {

248        tree fields;

249        /* Backend-created structs are safe.  */

250        if (! CLASS_TYPE_P (t))

251          return true;

252         /* PODs are safe.  */

253         if (! CLASSTYPE_NON_POD_P(t))

254          return true;

255        /* Classes with virtual baseclasses are not.  */

256        if (TYPE_USES_VIRTUAL_BASECLASSES (t))

257          return false;

258        /* Recursively check the base classes.  */

259        if (TYPE_BINFO (t) != NULL && TYPE_BINFO_BASETYPES (t) != NULL)

260        {

261          int i;

262          for (i = 0; i < TREE_VEC_LENGTH (TYPE_BINFO_BASETYPES (t)); i++)

263          {

264            tree binfo = TREE_VEC_ELT (TYPE_BINFO_BASETYPES (t), i);

265            if (!ok_to_generate_alias_set_for_type (BINFO_TYPE (binfo)))

266              return false;

267          }

268        }

269        /* Check all the fields.  */

270        for (fields = TYPE_FIELDS (t); fields; fields = TREE_CHAIN (fields))

271        {

272          if (TREE_CODE (fields) != FIELD_DECL)

273            continue ;

274          if (! ok_to_generate_alias_set_for_type (TREE_TYPE (fields)))

275             return false;

276        }

277        return true;

278      }

279      else if (TREE_CODE (t) == ARRAY_TYPE)

280        return ok_to_generate_alias_set_for_type (TREE_TYPE (t));

281      else

282        /* This should never happen, we dealt with all the aggregate

283          types that can appear in C++ above.  */

284        abort ();

285    }

286    else

287      return true;

288  }

 

接着考虑下面的例子:

extern "C" int printf(const char *,...);

class A {

protected  : int i ;

    public :

void f1 () { printf ("%d/n", i); }

        A () : i (0) {}

};

class B : public A {

    public :

void f1 () { printf ("%d/n", i); }

        B () { i = 2 ; }

};

int main () {

    void (A::*pf1) () = &A::f1;

    B b;

    (b.*pf1)();              // output “2”

    A a ;

    (a.*pf1)() ;      // output “0”

    return 0;

}

它输出“ 2, 0 ”,无疑 pf1 同时访问不同的对象。因此要小心处理成员指针别名集。在这里这样的指针被视为可能别名( may-alias )( cxx_get_alias_set 308 行)。

除了类的处理, C++ 前端与 C 前端共享下面的函数来处理 C 中存在的类型。首先对于 union 成员的直接访问,它与该 union 中其它成员互为别名。对该情形的注释解释到,在 C 标准中,通过取其地址来设置这个域是不允许的,其行为是由实现定义的,【 6 】也给出了一个解释。

-fstrict-aliasing

²        允许编译器为正在编译的语言采用最严格的可用的别名规则。对于C (及C++ ),这激活基于表达式类型的优化。特别的,一个类型的一个对象被假定不会作为另一个类型的一个对象,存在相同地址上,除非这些类型几乎相同。例如,一个 unsigned int 可以是一个 int 的别名,但不是一个 void* double 的别名。而一个字符类型是其它类型的可能别名。

特别注意像这样的代码:

union a_union {

int i;

double d;

};

int f() {

a_union t;

t.d = 3.0;

return t.i;

}

从最近写入的成员以外的成员中读出(称为“类型双关语, type-punning ”)是个普通的做法。即便使用‘ -fstrict-aliasing ’,也允许类型双关语,假定该内存通过这个 union 类型访问。因此,上面的代码将以期望的方式工作。不过,下面的代码不一定:

int f() {

a_union t;

int* ip;

t.d = 3.0;

ip = &t.i;

return *ip;

}

类似地,通过获取地址访问,转换其结果指针并提领这个结果具有未定义的行为,即便该转换使用一个 union 类型,即:

int f() {

double d = 3.0;

return ((union a_union *) &d)->i;

}

选项‘ -fstrict-aliasing ’在‘ -O2 ’,‘ -O3 ’,‘ -Os ’水平下使能。

紧接着在下面,我们将看到一个由类型双关语产生错误代码的例子。这个例子可以很好地解释为什么这里可能有未定义行为。不过, GCC 不给出警告,如果我们如上面那样行为不良。除了这个情形,非类型的 t 被踢开。

 

2848 HOST_WIDE_INT

2849 c_common_get_alias_set (tree t)                                                          n c-common.c

2850 {

2851   tree u;

2852

2853   /* Permit type-punning when accessing a union, provided the access

2854     is directly through the union. For example, this code does not

2855     permit taking the address of a union member and then storing

2856     through it. Even the type-punning allowed here is a GCC

2857     extension, albeit a common and useful one; the C standard says

2858     that such accesses have implementation-defined behavior.  */

2859   for (u = t;

2860        TREE_CODE (u) == COMPONENT_REF || TREE_CODE (u) == ARRAY_REF;

2861        u = TREE_OPERAND (u, 0))

2862     if (TREE_CODE (u) == COMPONENT_REF

2863        && TREE_CODE (TREE_TYPE (TREE_OPERAND (u, 0))) == UNION_TYPE)

2864       return 0;

2865

2866   /* That's all the expressions we handle specially.  */

2867   if (! TYPE_P (t))

2868     return -1;

2869

2870   /* The C standard guarantees that any object may be accessed via an

2871     lvalue that has character type.  */

2872   if (t == char_type_node

2873       || t == signed_char_type_node

2874       || t == unsigned_char_type_node)

2875     return 0;

2876

2877   /* If it has the may_alias attribute, it can alias anything.  */

2878   if (lookup_attribute ("may_alias", TYPE_ATTRIBUTES (t)))

2879     return 0;

2880

2881   /* The C standard specifically allows aliasing between signed and

2882     unsigned variants of the same type. We treat the signed

2883     variant as canonical.  */

2884   if (TREE_CODE (t) == INTEGER_TYPE && TREE_UNSIGNED (t))

2885   {

2886     tree t1 = c_common_signed_type (t);

2887

2888     /* t1 == t can happen for boolean nodes which are always unsigned.  */

2889     if (t1 != t)

2890       return get_alias_set (t1);

2891   }

2892   else if (POINTER_TYPE_P (t))

2893   {

2894     tree t1;

2895

2896      /* Unfortunately, there is no canonical form of a pointer type.

2897       I n particular, if we have `typedef int I', then `int *', and

2898       `I *' are different types. So, we have to pick a canonical

2899       representative. We do this below.

2900

2901       Technically, this approach is actually more conservative that

2902       it needs to be. In particular, `const int *' and `int *'

2903       should be in different alias sets, according to the C and C++

2904       standard, since their types are not the same, and so,

2905       technically, an `int **' and `const int **' cannot point at

2906       the same thing.

2907

2908       But, the standard is wrong. In particular, this code is

2909       legal C++:

2910

2911          int *ip;

2912          int **ipp = &ip;

2913          const int* const* cipp = &ipp;

2914

2915       And, it doesn't make sense for that to be legal unless you

2916       can dereference IPP and CIPP. So, we ignore cv-qualifiers on

2917       the pointed-to types. This issue has been reported to the

2918       C++ committee.  */

2919     t1 = build_type_no_quals (t);

2920     if (t1 != t)

2921       return get_alias_set (t1);

2922   }

2923

2924   return -1;

2925 }

 

上面在 2872 行,事实上将覆盖两个情形。即是形如:“ char c; ”及“ char *pc; ”的声明。对于前者,“ c ”不能是别的对象的别名,不需要构建别名集(而考虑 short 类型的情形,它是一个 2 个字节的组合,因而这两个字节可以是 short 类型的别名,而且其别名集必须包含在 short 的别名集中,前端必须为这个类型准备一个别名集,就像我们在代码中所见),而后者可以是任意可以通过一个左值访问对象的别名。不过对于后者,每次它指向特定的对象,将通过 get_alias_set 494 行的代码块得到其别名集,而且这个别名集可能不再是 0 (对于 C 形式的类型转换,它返回 0 )。

在这里,显然对于间接引用( indirect reference ,即指针或引用类型)之外的声明将共享它们基于的类的别名集。例如:“ short j , k, l; ”,在这个语句中,变量 j k l 将具有相同的类型 short 的别名集。不过,这个别名集将被忽略,因为这些变量不能互为别名。现在如果我们把这个语句改为:“ short j, k, l, *p; ”,现在 j k l 及指针 p 将共享 short 的别名集。因此前端可以知道 p 可能是 j k l 的别名。

接着考虑语句“ short j, k; char *p; ”, j k 共享 short 的别名集,不过现在 p 没有别名集,因为 get_alias_set (准确地说,是 c_common_get_alias_set )为之返回 0 。就这个语句而言,前端将不会把 p 视为 j k 的别名。如果我们加入一个语句:“ p = &j; ”,在 C++ 中,这将触发隐式转换错误。那么如果我们加入一个语句:“ p = (char*)&j; ”,前端检查 p j 别名集的交集。检查将通过,因为 p 具有超集 0 (记住真正的超集 0 ,没有被构建出来),并为这个转换产生代码。然而, p 的别名集总是保持为 0 ,这意味着它可以是任意可以通过左值访问的对象的别名。这个假定可以防止编译器采用容易出错的过度优化。不过这也是今后改进的地方,进一步细化别名集。

在上面 2884 行,具有不同符号的整数类型也共享相同的别名集。

2878 行提到了一个有趣的 GNU C++ 属性“ may_alias ”。【 6 】给出细节。

may_alias

²        通过具有这个属性的类型指针的访问不受 基于类型别名分析的支配,而是被假定为可以是任意类型对象的别名。在 6.5/7 的上下文中,一个左值表达式提领这样的一个指针被处理作仿佛其具有一个字符类型。参见‘ -fstrict-aliasing ’有关别名问题的更多信息。这个扩展用于支持某些向量( vector API ,其中一个向量类型的指针被允许作为不同向量类型指针的别名。注意到具有这个属性的一个类型的一个对象不具有特别的语义。使用的例子:

typedef short __attribute__ ((__may_alias__ )) short_a;

int main (void) {

int a = 0x12345678;

short_a *b = (short_a *) &a;

b[1] = 0;

if (a == 0x12345678)

abort();

exit(0);

}

如果在变量声明中使用 short 替换 short_a ,当使用‘ -fstrict-aliasing ’编译时,上面程序将执行 abort ,这个选项,在当前 GCC 版本中,在‘ -O2 ’或以上默认打开。

在上面的例子中,换句话来说,使用 short 替换 short_a 产生了不正确的代码。不过你可以使用 –Wstrict-aliasing 来找出可能破坏别名的指针。例如,在这里使用这个选项,编译器给出一个警告:“ test2.cpp:4: warning: dereferencing type-punned pointer will break strict-aliasing rules ”。

然后,如果 cxx_get_alias_set 返回 -1 以外的值,要么找到了别名集,或者这个对象不能是别名(即, char 类型)。否则,需要前进。看到在 get_alias_set 598 行,这个类型被赋予独一无二的别名集,并将被该类型的声明所共享。而对于聚集类型或包含多于一个成分的类型,其别名集应该包含其域的别名集。下面的函数 record_omponent_aliases 以这个方式构建这个别名集。 709 行的断言 TYPE_NONALIASED_COMPONENT ,如果是 true ,表明不允许获取该类型一个成分的地址。

 

697  void

698  record_component_aliases (tree type)                                                           in alias.c

699  {

700    HOST_WIDE_INT superset = get_alias_set (type);

701    tree field;

702 

703    if (superset == 0)

704      return ;

705 

706    switch (TREE_CODE (type))

707    {

708      case ARRAY_TYPE:

709        if (! TYPE_NONALIASED_COMPONENT (type))

710          record_alias_subset (superset, get_alias_set (TREE_TYPE (type)));

711        break ;

712 

713      case RECORD_TYPE:

714      case UNION_TYPE:

715      case QUAL_UNION_TYPE:

716        /* Recursively record aliases for the base classes, if there are any.  */

717        if (TYPE_BINFO (type) != NULL && TYPE_BINFO_BASETYPES (type) != NULL)

718        {

719          int i;

720          for (i = 0; i < TREE_VEC_LENGTH (TYPE_BINFO_BASETYPES (type)); i++)

721           {

722            tree binfo = TREE_VEC_ELT (TYPE_BINFO_BASETYPES (type), i);

723            record_alias_subset (superset,

724                             get_alias_set (BINFO_TYPE (binfo)));

725          }

726        }

727        for (field = TYPE_FIELDS (type); field != 0; field = TREE_CHAIN (field))

728          if (TREE_CODE (field) == FIELD_DECL && ! DECL_NONADDRESSABLE_P (field))

729            record_alias_subset (superset, get_alias_set (TREE_TYPE (field)));

730        break ;

731 

732      case COMPLEX_TYPE:

733        record_alias_subset (superset, get_alias_set (TREE_TYPE (type)));

734        break ;

735 

736      default :

737        break ;

738    }

739  }

 

注意到在这个聚集类型中,比如,类型为 short 的域将使用类型 short 的别名集,这意味着它将与这个类型的其它声明共享这个别名集。通过这个方式,如果有一个 short 类型的指针,编译器可以断定这个指针是这个域及其它声明的可能别名,避免过度优化。

 

你可能感兴趣的:(GCC-3.4.6源代码学习笔记(166))