In the rest of int_const_binop, the operation result which now are saved in low and hi, will be combined into a tree object with corresponding fields set. Below notrunc if nonzero, indicates don’t trucate to fit the type; overflow records if overflow occurs in the procedure, and no_overflow if nonzero, indicates the operation won’t lead to 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 }
Above at line 1338, if the expression is of type of size_t, and the result can fit in single HOST_WIDE_INT, and no overflow in the procedure, it invokes size_int_type_wide to cache the result within the hashtable size_htab (in the function, it will invoke force_fit_type to trucate the result and record overflow if happen). Since the precision of size_t is 32 on our assuming target, other situation always denotes overflow. But it is possible that the overflow hasn’t been recorded, line 1355 checks for this negligence.
After getting the length of the array, in following, it will determine the size of element. At line 1645 below, TYPE_PACKED returns nonzero if that objects of this type should be laid out in as compact a way as possible. And at line 1657, integer_one_node refers to the global unique tree object of constant 1.
At line 1669, the size of array is expressed by number of bits, while at line 1680, the size is expressed in unit (usually, bytes).
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 }
Macro ROUND_TYPE_ALIGN is not defined under Linux/x86. At line 1692, BITS_PER_UNIT for linux is 8 (means byte), see that array at least must be aligned at boundary of byte (it is not a problem for C/C++, in which the size of element is times of byte). And at line 1694, TYPE_USER_ALIGN will return nonzero if align attribute is used in the declaration.
Further, at line 1697, MEMBER_TYPE_FORCES_BLK is not defined. TREE_TYPE of the node of the array returns the type of element for array, at line 1703, TYPE_NO_FORCE_BLK returns nonzero if the element, has type of RECORD_TYPE, UNION_TYPE or QUAL_UNION_TYPE, and has BLKmode only because it lacks the alignment requirement for its size. In such case or element is not of BLKmode, mode_for_size_tree will try to see if any suitable mode other than BLKmode can be found.
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 }
See that for type having variable size or having extra large size ( > 1000), it will be assigned BLKmode. And mode_for_size at line 241 will also set BLKmode if no other mode is fitting.
Note that at line 1714, STRICT_ALIGNMENT if nonzero, means move instructions will actually fail to work when given unaligned data. And condition at line 1715 checks if the data is somewhat packed form.
save_expr wraps expression into a node of SAVE_EXPR. Do this to any expression which may be used in more than one place, but must be evaluated only once (for example: ++j += k; will be expanded to ++j = ++j + k; by the compiler, but ++j has side-effect, evaluates it twice will cause different result. Thus the compiler must compress other evaluations except those required by original semantics).
Normally, expand_expr would reevaluate the expression each time. Calling save_expr produces something that is evaluated and recorded the first time expand_expr is called on it. Subsequent calls to expand_expr just reuse the recorded value.
The call to expand_expr that generates code that actually computes the value is the first call at compile time. Subsequent calls at compile time generate code to use the saved value. This produces correct result provided that at run time control always flows through the instruction made by the first expand_expr before reaching the other places where the save_expr was evaluated. The caller of save_expr, must make sure this is so.
Constants, and certain read-only nodes, are returned with no SAVE_EXPR because that is safe. Expressions containing placeholders are not touched.
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 }
At line 1351 above, skip_simple_arithmetic extracts the necessary part of the expression for wrapping inside SAVE_EXPR. It can reduce the number of expression to be wrapped, and speed up the compilation.
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 }
Obviously, only nodes known without side-effect can be ignored. The function steps down the subtree till no node can be ignored anymore. Note that opreator “++” and “—” are binary operator, they contain implicit operand “1”.