构建别名集的入口是下面的 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 类型的指针,编译器可以断定这个指针是这个域及其它声明的可能别名,避免过度优化。