这里在我们的目标 Linux/x86 ,输出文件将具有格式 ELF 。 ELF 是支持重定位及 PIC (位置无关代码)的格式。【 10 】的第三章给出了关于 ELF 的一个很好的解释。从【 10 】的总结中,我们得到:
ELF 文件有 3 种稍有不同的风格的形式:可重定位( relocatable ),可执行( executable )及共享对象。可重定位文件由编译器及汇编器创建,但在运行前需要链接器的处理。可执行文件已完成所有的重定位及确定了所有的符号,除了在运行时确定的共享库符号。共享对象是共享库,包含了用于链接器的符号信息及用于运行时的直接可运行代码。
ELF 文件不寻常的二重性。编译器,汇编器,及链接器把这种文件处理作由一个节头表( section header table )所描述的一组逻辑节( logical section ),而系统加载器( system loader )把这种文件处理作由一个程序头表( program header table )所描述的一组段( segment )。一个单段通常由几个节构成。例如,一个“可加载只读”段可以包含对应于可执行代码,只读数据,及用于动态链接器符号的节。可重定位文件具有多个节表( section table ),可执行文件具有多个程序头表( program header table ),而共享对象同时具有两者。节用于链接器进一步处理的目的,而段用于映射入内存。
assemble_variable (continue)
1460 /* Output any data that we will need to use the address of. */
1461 if (DECL_INITIAL (decl) == error_mark_node)
1462 reloc = contains_pointers_p (TREE_TYPE (decl)) ? 3 : 0;
1463 else if (DECL_INITIAL (decl))
1464 {
1465 reloc = compute_reloc_for_constant (DECL_INITIAL (decl));
1466 output_addressed_constants (DECL_INITIAL (decl));
1467 }
1468 resolve_unique_section (decl, reloc, flag_data_sections );
域 DECL_INITIAL 如果是 NULL ,表示 decl 没有初始值;而如果是 error_mark_node ,则表示 decl 具有初始值,但我们现在还没看到它。如果这个初始值还未知,就需要保守些。在 1462 行, reloc 将保存一个值,这个值被将用来与 flag_pic 比较,来选定代码所要放置的节。正如我们在前面的章节中所见, flag_pic 如果是 1 ,表示制作小 PIC ,而如果是 2 ,表示制作大 PIC 。后面我们会看到这些值的作用。
如果 decl 的类型包含指针(或引用)——由下面的函数判断,把 reloc 设置为 3 。这个值表示尽可能使用大 PIC ,不行就尝试小 PIC 。
1561 static int
1562 contains_pointers_p (tree type) in varasm.c
1563 {
1564 switch (TREE_CODE (type))
1565 {
1566 case POINTER_TYPE:
1567 case REFERENCE_TYPE:
1568 /* I'm not sure whether OFFSET_TYPE needs this treatment,
1569 so I'll play safe and return 1. */
1570 case OFFSET_TYPE:
1571 return 1;
1572
1573 case RECORD_TYPE:
1574 case UNION_TYPE:
1575 case QUAL_UNION_TYPE:
1576 {
1577 tree fields;
1578 /* For a type that has fields, see if the fields have pointers. */
1579 for (fields = TYPE_FIELDS (type); fields; fields = TREE_CHAIN (fields))
1580 if (TREE_CODE (fields) == FIELD_DECL
1581 && contains_pointers_p (TREE_TYPE (fields)))
1582 return 1;
1583 return 0;
1584 }
1585
1586 case ARRAY_TYPE:
1587 /* An array type contains pointers if its element type does. */
1588 return contains_pointers_p (TREE_TYPE (type));
1589
1590 default :
1591 return 0;
1592 }
1593 }
如果初始值是已知的,则需要看它可能需要何种重定位。如果其中包含了地址表达式, ELF 的 PIC 编码要求对其中所援引的对象访问需要通过 GOT ,下面的函数在这个初始值中查找对象地址。如果初始值没有使用对象的地址,就会返回 0 。
C++ 前端钩子 expand_constant 被绑定在 cplus_expand_constant 上,这个函数把 PTRMEM_CST (成员指针常量)或者替换为 PLUS_EXPR (引用数据成员的情形),或替换为更合适的 PTRMEM_CST (从派生类引用基类方法的情形)。
3383 int
3384 compute_reloc_for_constant (tree exp) in varasm.c
3385 {
3386 int reloc = 0, reloc2;
3387 tree tem;
3388
3389 /* Give the front-end a chance to convert VALUE to something that
3390 looks more like a constant to the back-end. */
3391 exp = (*lang_hooks .expand_constant) (exp);
3392
3393 switch (TREE_CODE (exp))
3394 {
3395 case ADDR_EXPR:
3396 case FDESC_EXPR:
3397 /* Go inside any operations that get_inner_reference can handle and see
3398 if what's inside is a constant: no need to do anything here for
3399 addresses of variables or functions. */
3400 for (tem = TREE_OPERAND (exp, 0); handled_component_p (tem);
3401 tem = TREE_OPERAND (tem, 0))
3402 ;
3403
3404 if (TREE_PUBLIC (tem))
3405 reloc |= 2;
3406 else
3407 reloc |= 1;
3408 break ;
3409
3410 case PLUS_EXPR:
3411 reloc = compute_reloc_for_constant (TREE_OPERAND (exp, 0));
3412 reloc |= compute_reloc_for_constant (TREE_OPERAND (exp, 1));
3413 break ;
3414
3415 case MINUS_EXPR:
3416 reloc = compute_reloc_for_constant (TREE_OPERAND (exp, 0));
3417 reloc2 = compute_reloc_for_constant (TREE_OPERAND (exp, 1));
3418 /* The difference of two local labels is computable at link time. */
3419 if (reloc == 1 && reloc2 == 1)
3420 reloc = 0;
3421 else
3422 reloc |= reloc2;
3423 break ;
3424
3425 case NOP_EXPR:
3426 case CONVERT_EXPR:
3427 case NON_LVALUE_EXPR:
3428 reloc = compute_reloc_for_constant (TREE_OPERAND (exp, 0));
3429 break ;
3430
3431 case CONSTRUCTOR:
3432 for (tem = CONSTRUCTOR_ELTS (exp); tem; tem = TREE_CHAIN (tem))
3433 if (TREE_VALUE (tem) != 0)
3434 reloc |= compute_reloc_for_constant (TREE_VALUE (tem));
3435
3436 break ;
3437
3438 default :
3439 break ;
3440 }
3441 return reloc;
3442 }
对于类的非静态数据成员或者数组的元素,它们的地址以基址加上偏移的形式给出(即 PLUS_EXPR 或 MINUS_EXPR )。另外, TREE_PUBLIC ,在一个 FUNCTION_DECL 或 VAR_DECL 中,如果非 0 ,表示该名字从这个模块外访问;在一个 IDENTIFIER_NODE 中,如果非 0 ,表示从这个模块外访问的一个外部声明,该声明的名字在一个内部域中先前已见。因此 3404 到 3407 行相应地设置 reloc 。
下面的函数在上面的 3400 行调用,它找出被援引的对象。
5620 int
5621 handled_component_p (tree t) in expr.c
5622 {
5623 switch (TREE_CODE (t))
5624 {
5625 case BIT_FIELD_REF:
5626 case COMPONENT_REF:
5627 case ARRAY_REF:
5628 case ARRAY_RANGE_REF:
5629 case NON_LVALUE_EXPR:
5630 case VIEW_CONVERT_EXPR:
5631 return 1;
5632
5633 /* ??? Sure they are handled, but get_inner_reference may return
5634 a different PBITSIZE, depending upon whether the expression is
5635 wrapped up in a NOP_EXPR or not, e.g. for bitfields. */
5636 case NOP_EXPR:
5637 case CONVERT_EXPR:
5638 return (TYPE_MODE (TREE_TYPE (t))
5639 == TYPE_MODE (TREE_TYPE (TREE_OPERAND (t, 0))));
5640
5641 default :
5642 return 0;
5643 }
5644 }
5.13.5.2.2.1.1. 输出引用的常量
那么在上面 assemble_variable 的 1466 行,在对 output_addressed_constants 的调用中,其参数就是这个初始值。同样,这个函数查找 exp 中引用的地址。
3448 static void
3449 output_addressed_constants (tree exp) in varasm.c
3450 {
3451 tree tem;
3452
3453 /* Give the front-end a chance to convert VALUE to something that
3454 looks more like a constant to the back-end. */
3455 exp = (*lang_hooks .expand_constant) (exp);
3456
3457 switch (TREE_CODE (exp))
3458 {
3459 case ADDR_EXPR:
3460 case FDESC_EXPR:
3461 /* Go inside any operations that get_inner_reference can handle and see
3462 if what's inside is a constant: no need to do anything here for
3463 addresses of variables or functions. */
3464 for (tem = TREE_OPERAND (exp, 0); handled_component_p (tem);
3465 tem = TREE_OPERAND (tem, 0))
3466 ;
3467
3468 if (TREE_CODE_CLASS (TREE_CODE (tem)) == 'c'
3469 || TREE_CODE (tem) == CONSTRUCTOR)
3470 output_constant_def (tem, 0);
3471 break ;
3472
3473 case PLUS_EXPR:
3474 case MINUS_EXPR:
3475 output_addressed_constants (TREE_OPERAND (exp, 1));
3476 /* Fall through. */
3477
3478 case NOP_EXPR:
3479 case CONVERT_EXPR:
3480 case NON_LVALUE_EXPR:
3481 output_addressed_constants (TREE_OPERAND (exp, 0));
3482 break ;
3483
3484 case CONSTRUCTOR:
3485 for (tem = CONSTRUCTOR_ELTS (exp); tem; tem = TREE_CHAIN (tem))
3486 if (TREE_VALUE (tem) != 0)
3487 output_addressed_constants (TREE_VALUE (tem));
3488
3489 break ;
3490
3491 default :
3492 break ;
3493 }
3494 }
3468 行的条件若满足,表示即找出该地址上的对象是一个常量。而 3469 行的条件若满足,则表明是一个由大括号对包括的初始值,它可以是类或数组的初始值,这个初始值有可能是只读的。对于这些(可能是)常量的节点,前端使用一个数据结构 constant_descriptor_tree 来描述之。
2062 struct constant_descriptor_tree GTY(()) in varasm.c
2063 {
2064 /* A MEM for the constant. */
2065 rtx rtl;
2066
2067 /* The value of the constant. */
2068 tree value;
2069 };
每个常量至少在当前编译单元中是一个单件。我们已经看到在前端中,用于常量的树节点被缓存在哈希表中。现在对于常量描述符节点( constant descriptor node ),也是一样的。
2484 rtx
2485 output_constant_def (tree exp, int defer) in varasm.c
2486 {
2487 struct constant_descriptor_tree *desc;
2488 struct constant_descriptor_tree key;
2489 void **loc;
2490
2491 /* Look up EXP in the table of constant descriptors. If we didn't find
2492 it, create a new one. */
2493 key.value = exp;
2494 loc = htab_find_slot (const_desc_htab , &key, INSERT);
2495
2496 desc = *loc;
2497 if (desc == 0)
2498 {
2499 desc = build_constant_desc (exp);
2500 *loc = desc;
2501 }
2502
2503 maybe_output_constant_def_contents (desc, defer);
2504 return desc->rtl;
2505 }
如果在 const_desc_htab 里找不到这个常量描述符(注意使用该常量的树节点作为键值,通过 const_desc_hash 来比较节点的相同内容),就需要调用 build_constant_desc 来构建之。在下面 2441 行, copy_constant 拷贝 exp 中常量的树节点,因为下面可能改变这个节点,我们需要一份单独的拷贝。
2431 static struct constant_descriptor_tree *
2432 build_constant_desc (tree exp) in varasm.c
2433 {
2434 rtx symbol;
2435 rtx rtl;
2436 char label[256];
2437 int labelno;
2438 struct constant_descriptor_tree *desc;
2439
2440 desc = ggc_alloc (sizeof (*desc));
2441 desc->value = copy_constant (exp);
2442
2443 /* Create a string containing the label name, in LABEL. */
2444 labelno = const_labelno ++;
2445 ASM_GENERATE_INTERNAL_LABEL (label, "LC", labelno);
2446
2447 /* We have a symbol name; construct the SYMBOL_REF and the MEM. */
2448 symbol = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (label));
2449 SYMBOL_REF_FLAGS (symbol) = SYMBOL_FLAG_LOCAL;
2450 SYMBOL_REF_DECL (symbol) = desc->value;
2451 TREE_CONSTANT_POOL_ADDRESS_P (symbol) = 1;
2452
2453 rtl = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)), symbol);
2454 set_mem_attributes (rtl, exp, 1);
2455 set_mem_alias_set (rtl, 0);
2456 set_mem_alias_set (rtl, const_alias_set );
2457
2458 /* Set flags or add text to the name to record information, such as
2459 that it is a local symbol. If the name is changed, the macro
2460 ASM_OUTPUT_LABELREF will have to know how to strip this
2461 information. This call might invalidate our local variable
2462 SYMBOL; we can't use it afterward. */
2463
2464 (*targetm .encode_section_info) (exp, rtl, true);
2465
2466 desc->rtl = rtl;
2467
2468 return desc;
2469 }
为了在生成汇编代码时,以统一的方式引用这些常量,前端给这些常量做标记;因而对其访问可以通过引用关联的标记( label )来实现访问。这是所谓的内部标记。在我们假定的平台及机器下, ASM_GENERATE_INTERNAL_LABEL 具有下面的定义。这个标记将包含字符串:“ *LC1 ”,“ *LC2 ”,并根据所产生的常量标记的数目以此类推。
722 #undef ASM_GENERATE_INTERNAL_LABEL in darwin.h
723 #define ASM_GENERATE_INTERNAL_LABEL(LABEL,PREFIX,NUM) /
724 sprintf (LABEL, "*%s%ld", PREFIX, (long)(NUM))
接着我们把这个标记构建成符号引用( SYMBOL_REF ),并为之构建 MEM 节点。然后,根据(可能)常量的属性如下地设置内存属性。
5.13.5.2.2.1.1.1. 设置内存属性
在下面的函数中,参数 objectp 如果不是 0 ,表示我们正在构建一个新的 MEM 节点。而如果参数 bitpos 不是 0 ,则代表一个要应用在参数 t 上的未实现的偏移。
1787 void
1788 set_mem_attributes (rtx ref, tree t, int objectp) in emit-rtl.c
1789 {
1790 set_mem_attributes_minus_bitpos (ref, t, objectp, 0);
1791 }
下面 1566 至 1569 行的宏提取 MEM 节点中相应域的内容。这个函数也会用于修改已存在的 MEM 节点。注意在 1593 行对 t 别名集的计算,无论如何它都将覆盖 1566 行的结果。
1562 void
1563 set_mem_attributes_minus_bitpos (rtx ref, tree t, int objectp, in emit-rtl.c
1564 HOST_WIDE_INT bitpos)
1565 {
1566 HOST_WIDE_INT alias = MEM_ALIAS_SET (ref);
1567 tree expr = MEM_EXPR (ref);
1568 rtx offset = MEM_OFFSET (ref);
1569 rtx size = MEM_SIZE (ref);
1570 unsigned int align = MEM_ALIGN (ref);
1571 HOST_WIDE_INT apply_bitpos = 0;
1572 tree type;
1573
1574 /* It can happen that type_for_mode was given a mode for which there
1575 is no language-level type. In which case it returns NULL, which
1576 we can see here. */
1577 if (t == NULL_TREE)
1578 return ;
1579
1580 type = TYPE_P (t) ? t : TREE_TYPE (t);
1581 if (type == error_mark_node)
1582 return ;
1583
1584 /* If we have already set DECL_RTL = ref, get_alias_set will get the
1585 wrong answer, as it assumes that DECL_RTL already has the right alias
1586 info. Callers should not set DECL_RTL until after the call to
1587 set_mem_attributes. */
1588 if (DECL_P (t) && ref == DECL_RTL_IF_SET (t))
1589 abort ();
1590
1591 /* Get the alias set from the expression or type (perhaps using a
1592 front-end routine) and use it. */
1593 alias = get_alias_set (t);
记住 ref 是 MEM 节点, type 是参数 t (通常它是一个 DECL 节点,不过它仍然可以是一个类型节点)的类型节点。下面在 1598 行,钩子中的 honor_readonly 在当前 GCC 版本中的所有前端里都是 false 。
set_mem_attributes_minus_bitpos (continue)
1595 MEM_VOLATILE_P (ref) = TYPE_VOLATILE (type);
1596 MEM_IN_STRUCT_P (ref) = AGGREGATE_TYPE_P (type);
1597 RTX_UNCHANGING_P (ref)
1598 |= ((lang_hooks .honor_readonly
1599 && (TYPE_READONLY (type) || TREE_READONLY (t)))
1600 || (! TYPE_P (t) && TREE_CONSTANT (t)));
1601
1602 /* If we are making an object of this type, or if this is a DECL, we know
1603 that it is a scalar if the type is not an aggregate. */
1604 if ((objectp || DECL_P (t)) && ! AGGREGATE_TYPE_P (type))
1605 MEM_SCALAR_P (ref) = 1;
1606
1607 /* We can set the alignment from the type if we are making an object,
1608 this is an INDIRECT_REF, or if TYPE_ALIGN_OK. */
1609 if (objectp || TREE_CODE (t) == INDIRECT_REF || TYPE_ALIGN_OK (type))
1610 align = MAX (align, TYPE_ALIGN (type));
1611
1612 /* If the size is known, we can set that. */
1613 if (TYPE_SIZE_UNIT (type) && host_integerp (TYPE_SIZE_UNIT (type), 1))
1614 size = GEN_INT (tree_low_cst (TYPE_SIZE_UNIT (type), 1));
1615
1616 /* If T is not a type, we may be able to deduce some more information about
1617 the expression. */
1618 if (! TYPE_P (t))
1619 {
1620 maybe_set_unchanging (ref, t);
1621 if (TREE_THIS_VOLATILE (t))
1622 MEM_VOLATILE_P (ref) = 1;
1623
1624 /* Now remove any conversions: they don't change what the underlying
1625 object is. Likewise for SAVE_EXPR. */
1626 while (TREE_CODE (t) == NOP_EXPR || TREE_CODE (t) == CONVERT_EXPR
1627 || TREE_CODE (t) == NON_LVALUE_EXPR
1628 || TREE_CODE (t) == VIEW_CONVERT_EXPR
1629 || TREE_CODE (t) == SAVE_EXPR)
1630 t = TREE_OPERAND (t, 0);
1631
1632 /* If this expression can't be addressed (e.g., it contains a reference
1633 to a non-addressable field), show we don't change its alias set. */
1634 if (! can_address_p (t))
1635 MEM_KEEP_ALIAS_SET_P (ref) = 1;
1636
1637 /* If this is a decl, set the attributes of the MEM from it. */
1638 if (DECL_P (t))
1639 {
1640 expr = t;
1641 offset = const0_rtx;
1642 apply_bitpos = bitpos;
1643 size = (DECL_SIZE_UNIT (t)
1644 && host_integerp (DECL_SIZE_UNIT (t), 1)
1645 ? GEN_INT (tree_low_cst (DECL_SIZE_UNIT (t), 1)) : 0);
1646 align = DECL_ALIGN (t);
1647 }
1648
1649 /* If this is a constant, we know the alignment. */
1650 else if (TREE_CODE_CLASS (TREE_CODE (t)) == 'c')
1651 {
1652 align = TYPE_ALIGN (type);
1653 #ifdef CONSTANT_ALIGNMENT
1654 align = CONSTANT_ALIGNMENT (t, align);
1655 #endif
1656 }
1657
1658 /* If this is a field reference and not a bit-field, record it. */
1659 /* ??? There is some information that can be gleened from bit-fields,
1660 such as the word offset in the structure that might be modified.
1661 But skip it for now. */
1662 else if (TREE_CODE (t) == COMPONENT_REF
1663 && ! DECL_BIT_FIELD (TREE_OPERAND (t, 1)))
1664 {
1665 expr = component_ref_for_mem_expr (t);
1666 offset = const0_rtx;
1667 apply_bitpos = bitpos;
1668 /* ??? Any reason the field size would be different than
1669 the size we got from the type? */
1670 }
RTL 节点也需要标识其中的对象是否是不变的(常量), RTX_UNCHANGING_P 就是这个标记。如果上面钩子中的 honor_readonly 是 true ,表示如果 TREE_READONLY 或 TYPE_READONLY 成立,设置 RTX_UNCHANGING_P 。注意 1600 行的语句,如果 t 的值是常量,设置 RTX_UNCHANGING_P 。另外如果 t 满足以下 602 行的条件,也要设置 RTX_UNCHANGING_P 。注意这两个条件不重复。
594 void
595 maybe_set_unchanging (rtx ref, tree t) in explow.c
596 {
597 /* We can set RTX_UNCHANGING_P from TREE_READONLY for decls whose
598 initialization is only executed once, or whose initializer always
599 has the same value. Currently we simplify this to PARM_DECLs in the
600 first case, and decls with TREE_CONSTANT initializers in the second. */
601
602 if ((TREE_READONLY (t) && DECL_P (t)
603 && (DECL_EXTERNAL (t)
604 || TREE_CODE (t) == PARM_DECL
605 || (DECL_INITIAL (t) && TREE_CONSTANT (DECL_INITIAL (t)))))
606 || TREE_CODE_CLASS (TREE_CODE (t)) == 'c')
607 RTX_UNCHANGING_P (ref) = 1;
608 }
前面我们看过了前端对转换的处理,在 C/C++ 中,转换只改变对象的表达形式,而不改变其所占内存的大小、位置等属性。而且对象的表达形式,只对前端执行语法、语义检查具有意义,后端不在乎对象看起来像什么,而在乎对象在内存中是什么。因此, 1626 行的 WHILE 循环剥除这些对后端不起作用的封装。
另外,对于特定类型的常量,目标平台会有特定的对齐要求, 1653 行的宏 CONSTANT_ALIGNMENT ,对于 x86/Linux 平台,定义为 ix86_constant_alignment 。该函数对对齐量进行必要的调整。
同样,对于 COMPONENT_REF 节点,前端出于维护语法、语义一致性的需要,在所引用的对象节点上可能进行了多次转换的封装。下面的函数剥除了这些对后端无意义的封装。
1489 static tree
1490 component_ref_for_mem_expr (tree ref) in emit-rtl.c
1491 {
1492 tree inner = TREE_OPERAND (ref, 0);
1493
1494 if (TREE_CODE (inner) == COMPONENT_REF)
1495 inner = component_ref_for_mem_expr (inner);
1496 else
1497 {
1498 tree placeholder_ptr = 0;
1499
1500 /* Now remove any conversions: they don't change what the underlying
1501 object is. Likewise for SAVE_EXPR. Also handle PLACEHOLDER_EXPR. */
1502 while (TREE_CODE (inner) == NOP_EXPR || TREE_CODE (inner) == CONVERT_EXPR
1503 || TREE_CODE (inner) == NON_LVALUE_EXPR
1504 || TREE_CODE (inner) == VIEW_CONVERT_EXPR
1505 || TREE_CODE (inner) == SAVE_EXPR
1506 || TREE_CODE (inner) == PLACEHOLDER_EXPR)
1507 if (TREE_CODE (inner) == PLACEHOLDER_EXPR)
1508 inner = find_placeholder (inner, &placeholder_ptr);
1509 else
1510 inner = TREE_OPERAND (inner, 0);
1511
1512 if (! DECL_P (inner))
1513 inner = NULL_TREE;
1514 }
1515
1516 if (inner == TREE_OPERAND (ref, 0))
1517 return ref;
1518 else
1519 return build (COMPONENT_REF, TREE_TYPE (ref), inner,
1520 TREE_OPERAND (ref, 1));
1521 }
下面的 ARRAY_REF 节点代表对数组的访问,其第一个操作数是这个数组,第二个操作数是访问索引。那么 1680 行的 DO WHILE 循环计算出所访问元素到数组开头的偏移。这个偏移保存在下面的 off_tree 中。注意在 1722 行,因为使用 2 进制补码,得到的是 ioff 中为 1 的最低位,也就是所访问元素地址所对齐的边界。
set_mem_attributes_minus_bitpos (continue)
1672 /* If this is an array reference, look for an outer field reference. */
1673 else if (TREE_CODE (t) == ARRAY_REF)
1674 {
1675 tree off_tree = size_zero_node;
1676 /* We can't modify t, because we use it at the end of the
1677 function. */
1678 tree t2 = t;
1679
1680 do
1681 {
1682 tree index = TREE_OPERAND (t2, 1);
1683 tree array = TREE_OPERAND (t2, 0);
1684 tree domain = TYPE_DOMAIN (TREE_TYPE (array));
1685 tree low_bound = (domain ? TYPE_MIN_VALUE (domain) : 0);
1686 tree unit_size = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (array)));
1687
1688 /* We assume all arrays have sizes that are a multiple of a byte.
1689 First subtract the lower bound, if any, in the type of the
1690 index, then convert to sizetype and multiply by the size of the
1691 array element. */
1692 if (low_bound != 0 && ! integer_zerop (low_bound))
1693 index = fold (build (MINUS_EXPR, TREE_TYPE (index),
1694 index, low_bound));
1695
1696 /* If the index has a self-referential type, pass it to a
1697 WITH_RECORD_EXPR; if the component size is, pass our
1698 component to one. */
1699 if (CONTAINS_PLACEHOLDER_P (index))
1700 index = build (WITH_RECORD_EXPR, TREE_TYPE (index), index, t2);
1701 if (CONTAINS_PLACEHOLDER_P (unit_size))
1702 unit_size = build (WITH_RECORD_EXPR, sizetype,
1703 unit_size, array);
1704
1705 off_tree
1706 = fold (build (PLUS_EXPR, sizetype,
1707 fold (build (MULT_EXPR, sizetype,
1708 index,
1709 unit_size)),
1710 off_tree));
1711 t2 = TREE_OPERAND (t2, 0);
1712 }
1713 while (TREE_CODE (t2) == ARRAY_REF);
1714
1715 if (DECL_P (t2))
1716 {
1717 expr = t2;
1718 offset = NULL;
1719 if (host_integerp (off_tree, 1))
1720 {
1721 HOST_WIDE_INT ioff = tree_low_cst (off_tree, 1);
1722 HOST_WIDE_INT aoff = (ioff & -ioff) * BITS_PER_UNIT;
1723 align = DECL_ALIGN (t2);
1724 if (aoff && (unsigned HOST_WIDE_INT) aoff < align)
1725 align = aoff;
1726 offset = GEN_INT (ioff);
1727 apply_bitpos = bitpos;
1728 }
1729 }
1730 else if (TREE_CODE (t2) == COMPONENT_REF)
1731 {
1732 expr = component_ref_for_mem_expr (t2);
1733 if (host_integerp (off_tree, 1))
1734 {
1735 offset = GEN_INT (tree_low_cst (off_tree, 1));
1736 apply_bitpos = bitpos;
1737 }
1738 /* ??? Any reason the field size would be different than
1739 the size we got from the type? */
1740 }
1741 else if (flag_argument_noalias > 1
1742 && TREE_CODE (t2) == INDIRECT_REF
1743 && TREE_CODE (TREE_OPERAND (t2, 0)) == PARM_DECL)
1744 {
1745 expr = t2;
1746 offset = NULL;
1747 }
1748 }
1749
1750 /* If this is a Fortran indirect argument reference, record the
1751 parameter decl. */
1752 else if (flag_argument_noalias > 1
1753 && TREE_CODE (t) == INDIRECT_REF
1754 && TREE_CODE (TREE_OPERAND (t, 0)) == PARM_DECL)
1755 {
1756 expr = t;
1757 offset = NULL;
1758 }
1759 }
1760
1761 /* If we modified OFFSET based on T, then subtract the outstanding
1762 bit position offset. Similarly, increase the size of the accessed
1763 object to contain the negative offset. */
1764 if (apply_bitpos)
1765 {
1766 offset = plus_constant (offset, -(apply_bitpos / BITS_PER_UNIT));
1767 if (size)
1768 size = plus_constant (size, apply_bitpos / BITS_PER_UNIT);
1769 }
1770
1771 /* Now set the attributes we computed above. */
1772 MEM_ATTRS (ref)
1773 = get_mem_attrs (alias, expr, offset, size, align, GET_MODE (ref));
1774
1775 /* If this is already known to be a scalar or aggregate, we are done. */
1776 if (MEM_IN_STRUCT_P (ref) || MEM_SCALAR_P (ref))
1777 return ;
1778
1779 /* If it is a reference into an aggregate, this is part of an aggregate.
1780 Otherwise we don't know. */
1781 else if (TREE_CODE (t) == COMPONENT_REF || TREE_CODE (t) == ARRAY_REF
1782 || TREE_CODE (t) == ARRAY_RANGE_REF
1783 || TREE_CODE (t) == BIT_FIELD_REF)
1784 MEM_IN_STRUCT_P (ref) = 1;
1785 }
上面的标记 flag_argument_noalias ,如果是 0 ,指针实参可能互为别名(这是 C 的情形);如果是 1 ,指针实参不可能互为别名,但可能是全局变量的别名;如果是 2 ,指针实参不可能互为别名,也不可能是全局变量的别名( Fortran 的情况)。
回到 build_constant_desc ,对于常量不应该赋予其类型关联的别名集,因为它不能改变。前端专门为常量准备了别名集 const_alias_set 。在 2455 行对 set_mem_alias_set 的第一次调用,把常量 RTL 节点的别名集设置为 0 ,是为了绕过该函数中对别名集冲突的检查。
1806 void
1807 set_mem_alias_set (rtx mem, HOST_WIDE_INT set) in emit-rtl.c
1808 {
1809 #ifdef ENABLE_CHECKING
1810 /* If the new and old alias sets don't conflict, something is wrong. */
1811 if (!alias_sets_conflict_p (set, MEM_ALIAS_SET (mem)))
1812 abort ();
1813 #endif
1814
1815 MEM_ATTRS (mem) = get_mem_attrs (set, MEM_EXPR (mem), MEM_OFFSET (mem),
1816 MEM_SIZE (mem), MEM_ALIGN (mem),
1817 GET_MODE (mem));
1818 }
而在 2464 行,目标平台钩子 encode_section_info 的细节参考 为内建函数创建 RTX对象 一节,这个函数设置了 RTL 节点的 SYMBOL_REF_FLAGS 域。