而在int_const_binop的余下部分,计算结果已经存放在low和hi中,这个结果将被合成为相应的树节点对象并检查溢出情况。下面的notrunc如果非零,表明不做值截取,而overflow则记录了计算过程中是否发生溢出,no_overflow如果非零,表明计算不会导致溢出。
int_const_binop (continue)
1331 /* If this is for a sizetype, can be represented as one (signed)
1332 HOST_WIDE_INT word, and doesn't overflow, use size_int since it caches
1333 constants. */
1334 if (is_sizetype
1335 && ((hi == 0 && (HOST_WIDE_INT) low >= 0)
1336 || (hi == -1 && (HOST_WIDE_INT) low < 0))
1337 && overflow == 0 && ! TREE_OVERFLOW (arg1) && ! TREE_OVERFLOW (arg2))
1338 return size_int_type_wide (low, type);
1339 else
1340 {
1341 t = build_int_2 (low, hi);
1342 TREE_TYPE (t) = TREE_TYPE (arg1);
1343 }
1344
1345 TREE_OVERFLOW (t)
1346 = ((notrunc
1347 ? (!uns || is_sizetype) && overflow
1348 : (force_fit_type (t, (!uns || is_sizetype) && overflow)
1349 && ! no_overflow))
1350 | TREE_OVERFLOW (arg1)
1351 | TREE_OVERFLOW (arg2));
1352
1353 /* If we're doing a size calculation, unsigned arithmetic does overflow.
1354 So check if force_fit_type truncated the value. */
1355 if (is_sizetype
1356 && ! TREE_OVERFLOW (t)
1357 && (TREE_INT_CST_HIGH (t) != hi
1358 || TREE_INT_CST_LOW (t) != low))
1359 TREE_OVERFLOW (t) = 1;
1360
1361 TREE_CONSTANT_OVERFLOW (t) = (TREE_OVERFLOW (t)
1362 | TREE_CONSTANT_OVERFLOW (arg1)
1363 | TREE_CONSTANT_OVERFLOW (arg2));
1364 return t;
1365 }
上面1338行,如果计算的常量为size_t类型,其结果可被保存在一个HOST_WIDE_INT中,而且其过程没有发生溢出,那么利用size_int_type_wide将其保存在哈希表size_htab中(在这个函数中会调用force_fit_type,根据类型的精度进行必要的值截取和溢出标记)。因为size_t的精度,在我们的假想平台下只有32位,因此,其他情况都意味着有溢出,但可能结果节点的溢出标记还未设置。1355行即对这个情况进行检查。
在获取数组的长度后,接下来需要确定元素的大小。在下面1645行,TYPE_PACKED 返回非零值,如果该类型的对象应以尽可能紧凑的方式来布局。而在1657行,integer_one_node 指向代表常量1的全局树节点。
在1669行,数组的大小按比特位来表示,而在1680行,这个大小由单位来计算(通常是字节,取决于目标机器)。
layout_type (continue)
1643 /* Special handling for arrays of bits (for Chill). */
1644 element_size = TYPE_SIZE (element);
1645 if (TYPE_PACKED (type) && INTEGRAL_TYPE_P (element)
1646 && (integer_zerop (TYPE_MAX_VALUE (element))
1647 || integer_onep (TYPE_MAX_VALUE (element)))
1648 && host_integerp (TYPE_MIN_VALUE (element), 1))
1649 {
1650 HOST_WIDE_INT maxvalue
1651 = tree_low_cst (TYPE_MAX_VALUE (element), 1);
1652 HOST_WIDE_INT minvalue
1653 = tree_low_cst (TYPE_MIN_VALUE (element), 1);
1654
1655 if (maxvalue - minvalue == 1
1656 && (maxvalue == 1 || maxvalue == 0))
1657 element_size = integer_one_node;
1658 }
1659
1660 /* If neither bound is a constant and sizetype is signed, make
1661 sure the size is never negative. We should really do this
1662 if *either* bound is non-constant, but this is the best
1663 compromise between C and Ada. */
1664 if (! TREE_UNSIGNED (sizetype)
1665 && TREE_CODE (TYPE_MIN_VALUE (index)) != INTEGER_CST
1666 && TREE_CODE (TYPE_MAX_VALUE (index)) != INTEGER_CST)
1667 length = size_binop (MAX_EXPR, length, size_zero_node);
1668
1669 TYPE_SIZE (type) = size_binop (MULT_EXPR, element_size,
1670 convert (bitsizetype, length));
1671
1672 /* If we know the size of the element, calculate the total
1673 size directly, rather than do some division thing below.
1674 This optimization helps Fortran assumed-size arrays
1675 (where the size of the array is determined at runtime)
1676 substantially.
1677 Note that we can't do this in the case where the size of
1678 the elements is one bit since TYPE_SIZE_UNIT cannot be
1679 set correctly in that case. */
1680 if (TYPE_SIZE_UNIT (element) != 0 && ! integer_onep (element_size))
1681 TYPE_SIZE_UNIT (type)
1682 = size_binop (MULT_EXPR, TYPE_SIZE_UNIT (element), length);
1683 }
1684
1685 /* Now round the alignment and size,
1686 using machine-dependent criteria if any. */
1687
1688 #ifdef ROUND_TYPE_ALIGN
1689 TYPE_ALIGN (type)
1690 = ROUND_TYPE_ALIGN (type, TYPE_ALIGN (element), BITS_PER_UNIT);
1691 #else
1692 TYPE_ALIGN (type) = MAX (TYPE_ALIGN (element), BITS_PER_UNIT);
1693 #endif
1694 TYPE_USER_ALIGN (type) = TYPE_USER_ALIGN (element);
1695 TYPE_MODE (type) = BLKmode;
1696 if (TYPE_SIZE (type) != 0
1697 #ifdef MEMBER_TYPE_FORCES_BLK
1698 && ! MEMBER_TYPE_FORCES_BLK (type, VOIDmode)
1699 #endif
1700 /* BLKmode elements force BLKmode aggregate;
1701 else extract/store fields may lose. */
1702 && (TYPE_MODE (TREE_TYPE (type)) != BLKmode
1703 || TYPE_NO_FORCE_BLK (TREE_TYPE (type))))
1704 {
1705 /* One-element arrays get the component type's mode. */
1706 if (simple_cst_equal (TYPE_SIZE (type),
1707 TYPE_SIZE (TREE_TYPE (type))))
1708 TYPE_MODE (type) = TYPE_MODE (TREE_TYPE (type));
1709 else
1710 TYPE_MODE (type)
1711 = mode_for_size_tree (TYPE_SIZE (type), MODE_INT, 1);
1712
1713 if (TYPE_MODE (type) != BLKmode
1714 && STRICT_ALIGNMENT && TYPE_ALIGN (type) < BIGGEST_ALIGNMENT
1715 && TYPE_ALIGN (type) < GET_MODE_ALIGNMENT (TYPE_MODE (type))
1716 && TYPE_MODE (type) != BLKmode)
1717 {
1718 TYPE_NO_FORCE_BLK (type) = 1;
1719 TYPE_MODE (type) = BLKmode;
1720 }
1721 }
1722 break;
1723 }
宏ROUND_TYPE_ALIGN 在Linx/x86平台下没有定义,在1692行,BITS_PER_UNIT对Linux定义为8(代表字节)。因此数组至少要在字节边界上对齐(对C/C++没有这个问题,元素的大小都是字节倍数的)。在1694行TYPE_USER_ALIGN如果非零表示声明使用了align这个属性(align attribute)。
进而,在1697行,MEMBER_TYPE_FORCES_BLK没有定义。在1703行,数组节点的TREE_TYPE返回元素的类型,而TYPE_NO_FORCE_BLK返回非零值,如果元素的类型是RECORD_TYPE,UNION_TYPE或QUAL_UNION_TYPE,而且仅当类型缺乏对齐量信息时才使用BLKmode模式。在这种情况下或者元素不是BLKmode模式,函数mode_for_size_tree将会寻找合用的非BLKmode的模式。
230 enum machine_mode
231 mode_for_size_tree (tree size, enum mode_class class, int limit) in stor-layout.c
232 {
233 if (TREE_CODE (size) != INTEGER_CST
234 || TREE_OVERFLOW (size)
235 /* What we really want to say here is that the size can fit in a
236 host integer, but we know there's no way we'd find a mode for
237 this many bits, so there's no point in doing the precise test. */
238 || compare_tree_int (size, 1000) > 0)
239 return BLKmode;
240 else
241 return mode_for_size (tree_low_cst (size, 1), class, limit);
242 }
对于不定长的类型,或长度超过1000字节的类型,只能使用BLKmode模式。而且在241行的mode_for_size在找不到合用的其他模式时,也会返回BLKmode。
注意到在1714行,STRICT_ALIGNMENT如果非零,表示对于非对齐数据,移动指令(move instructions)将会失败。而1715行的判断,检查数据是否为某种紧凑形式。
函数save_expr把表达式封装入一个SAVE_EXPR节点中。任何只能被求值一次,但被编译器多次使用的表达式需要被封装入SAVE_EXPR节点(例如:++j += k; 会被编译器展开为++j = ++j + k; 但++j是有副作用的,对它2次求值会导致不同的结果。编译器必须抑制除了原始语义要求外的求值)。
通常,函数expand_expr每次都会对表达式重新求值(reevaluate)。在expand_expr第一次处理这个表达式时,调用save_expr将产生的结果缓存。随后的expand_expr调用将使用这个保存的值。
产生真正计算代码的那次对expand_expr的调用是编译时的第一次调用。编译时后续的调用将产生使用对应的保存值的代码。只要在运行时,第一次调用expand_expr的指令在其他使用这个对应的save_expr的指令前执行,就能得到正确的结果。save_expr的调用者必须保证这一点。
常量和只读的节点不需要封装入SAVE_EXPR。这样做仍然是安全的。对包含占位符(placeholder)的表达式,save_expr也不做处理。
1340 tree
1341 save_expr (tree expr) in tree.c
1342 {
1343 tree t = fold (expr);
1344 tree inner;
1345
1346 /* If the tree evaluates to a constant, then we don't want to hide that
1347 fact (i.e. this allows further folding, and direct checks for constants).
1348 However, a read-only object that has side effects cannot be bypassed.
1349 Since it is no problem to reevaluate literals, we just return the
1350 literal node. */
1351 inner = skip_simple_arithmetic (t);
1352 if (TREE_CONSTANT (inner)
1353 || (TREE_READONLY (inner) && ! TREE_SIDE_EFFECTS (inner))
1354 || TREE_CODE (inner) == SAVE_EXPR
1355 || TREE_CODE (inner) == ERROR_MARK)
1356 return t;
1357
1358 /* If INNER contains a PLACEHOLDER_EXPR, we must evaluate it each time, since
1359 it means that the size or offset of some field of an object depends on
1360 the value within another field.
1361
1362 Note that it must not be the case that T contains both a PLACEHOLDER_EXPR
1363 and some variable since it would then need to be both evaluated once and
1364 evaluated more than once. Front-ends must assure this case cannot
1365 happen by surrounding any such subexpressions in their own SAVE_EXPR
1366 and forcing evaluation at the proper time. */
1367 if (contains_placeholder_p (inner))
1368 return t;
1369
1370 t = build (SAVE_EXPR, TREE_TYPE (expr), t, current_function_decl, NULL_TREE);
1371
1372 /* This expression might be placed ahead of a jump to ensure that the
1373 value was computed on both sides of the jump. So make sure it isn't
1374 eliminated as dead. */
1375 TREE_SIDE_EFFECTS (t) = 1;
1376 TREE_READONLY (t) = 1;
1377 return t;
1378 }
上面的1351行,skip_simple_arithmetic提取出表达式封装入SAVE_EXPR的必要部分。这样可以减少需要封装的表达式的数量,加快编译时间。
1383 tree
1384 skip_simple_arithmetic (tree expr) in tree.c
1385 {
1386 tree inner;
1387
1388 /* We don't care about whether this can be used as an lvalue in this
1389 context. */
1390 while (TREE_CODE (expr) == NON_LVALUE_EXPR)
1391 expr = TREE_OPERAND (expr, 0);
1392
1393 /* If we have simple operations applied to a SAVE_EXPR or to a SAVE_EXPR and
1394 a constant, it will be more efficient to not make another SAVE_EXPR since
1395 it will allow better simplification and GCSE will be able to merge the
1396 computations if they actually occur. */
1397 inner = expr;
1398 while (1)
1399 {
1400 if (TREE_CODE_CLASS (TREE_CODE (inner)) == '1')
1401 inner = TREE_OPERAND (inner, 0);
1402 else if (TREE_CODE_CLASS (TREE_CODE (inner)) == '2')
1403 {
1404 if (TREE_CONSTANT (TREE_OPERAND (inner, 1)))
1405 inner = TREE_OPERAND (inner, 0);
1406 else if (TREE_CONSTANT (TREE_OPERAND (inner, 0)))
1407 inner = TREE_OPERAND (inner, 1);
1408 else
1409 break;
1410 }
1411 else
1412 break;
1413 }
1414
1415 return inner;
1416 }
显然,确认没有副作用的节点才可以被忽略。函数一直沿着子树下降,直至没有节点可以被忽略。注意,操作符++和—是二元操作符,它隐含了“1”这个操作数。