当调用build_array_type时,第二个参数index_type应该是由上一节所创建的索引节点。注意C++中没有函数数组类型,因为C++函数拥有常量地址。
3790 tree
3791 build_array_type (tree elt_type, tree index_type) in tree.c
3792 {
3793 tree t;
3794 unsigned int hashcode;
3795
3796 if (TREE_CODE (elt_type) == FUNCTION_TYPE)
3797 {
3798 error ("arrays of functions are not meaningful");
3799 elt_type = integer_type_node;
3800 }
3801
3802 /* Make sure TYPE_POINTER_TO (elt_type) is filled in. */
3803 build_pointer_type (elt_type);
3804
3805 /* Allocate the array after the pointer type,
3806 in case we free it in type_hash_canon. */
3807 t = make_node (ARRAY_TYPE);
3808 TREE_TYPE (t) = elt_type;
3809 TYPE_DOMAIN (t) = index_type;
3810
3811 if (index_type == 0)
3812 {
3813 return t;
3814 }
3815
3816 hashcode = TYPE_HASH (elt_type) + TYPE_HASH (index_type);
3817 t = type_hash_canon (hashcode, t);
3818
3819 if (!COMPLETE_TYPE_P (t))
3820 layout_type (t);
3821 return t;
3822 }
在C++中,数组名可作为数组的首地址,数组名加索引为指向对应元素的指针。例如:
int a[5];
int *p = a + 3; // but can’t be int &p = a +3;
因此在3803行,为元素创建指针类型(创建该数组类型时,元素的类型节点肯定已创建了,但其指针类型未必)。
对于数组类型,我们需要知道数组按比特计算的长度,数组按字节计算的长度,数组的对齐量,连同其元素的相关信息。
1528 void
1529 layout_type (tree type) in stor-layout.c
1530 {
1531 if (type == 0)
1532 abort ();
1533
1534 /* Do nothing if type has been laid out before. */
1535 if (TYPE_SIZE (type))
1536 return;
1537
1538 switch (TREE_CODE (type))
1539 {
…
1619 case ARRAY_TYPE:
1620 {
1621 tree index = TYPE_DOMAIN (type);
1622 tree element = TREE_TYPE (type);
1623
1624 build_pointer_type (element);
1625
1626 /* We need to know both bounds in order to compute the size. */
1627 if (index && TYPE_MAX_VALUE (index) && TYPE_MIN_VALUE (index)
1628 && TYPE_SIZE (element))
1629 {
1630 tree ub = TYPE_MAX_VALUE (index);
1631 tree lb = TYPE_MIN_VALUE (index);
1632 tree length;
1633 tree element_size;
1634
1635 /* The initial subtraction should happen in the original type so
1636 that (possible) negative values are handled appropriately. */
1637 length = size_binop (PLUS_EXPR, size_one_node,
1638 convert (sizetype,
1639 fold (build (MINUS_EXPR,
1640 TREE_TYPE (lb),
1641 ub, lb))));
C++允许类似int a[] = {…};这样的数组定义。对于这样的定义,数组类型的布局需要推迟到完成对”{…}”部分的解析。同时由于模板的引入,元素可为某个模板类型。模板类型的大小往往是不固定的,取决于具现时的模板实参,编译器会把这个模板类型的大小定义为0。这种情况下,数组的布局也需要推迟到具现时。
不过即便推迟了布局,我们最终还是会走进来的。否则编译器迟早会给出一个错误消息,停下来。因为没有布局信息,我们将不知道如何在内存中创建这个类型的实例。
在上面1637行,size_binop按参数code所指定的算术操作,尝试结合操作数arg0和arg1。 在这里需要处理的表达式是(上边界 – 下边界)+ 1,它是数组元素的个数。在1637行, size_one_node是数值为1的整型树节点。它被当作常量1使用。
1594 tree
1595 size_binop (enum tree_code code, tree arg0, tree arg1) in fold-const.c
1596 {
1597 tree type = TREE_TYPE (arg0);
1598
1599 if (TREE_CODE (type) != INTEGER_TYPE || ! TYPE_IS_SIZETYPE (type)
1600 || type != TREE_TYPE (arg1))
1601 abort ();
1602
1603 /* Handle the special case of two integer constants faster. */
1604 if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
1605 {
1606 /* And some specific cases even faster than that. */
1607 if (code == PLUS_EXPR && integer_zerop (arg0))
1608 return arg1;
1609 else if ((code == MINUS_EXPR || code == PLUS_EXPR)
1610 && integer_zerop (arg1))
1611 return arg0;
1612 else if (code == MULT_EXPR && integer_onep (arg0))
1613 return arg1;
1614
1615 /* Handle general case of two integer constants. */
1616 return int_const_binop (code, arg0, arg1, 0);
1617 }
1618
1619 if (arg0 == error_mark_node || arg1 == error_mark_node)
1620 return error_mark_node;
1621
1622 return fold (build (code, type, arg0, arg1));
1623 }
如果arg0和arg1不都是整型常量,这个表达式需要更为复杂的处理,则它被交由1622行的fold。
首先查看是否存在可以消除的项。如果参数expr是整型常量0或是复数常量0,函数integer_zerop返回1。
579 int
580 integer_zerop (tree expr) in tree.c
581 {
582 STRIP_NOPS (expr);
583
584 return ((TREE_CODE (expr) == INTEGER_CST
585 && ! TREE_CONSTANT_OVERFLOW (expr)
586 && TREE_INT_CST_LOW (expr) == 0
587 && TREE_INT_CST_HIGH (expr) == 0)
588 || (TREE_CODE (expr) == COMPLEX_CST
589 && integer_zerop (TREE_REALPART (expr))
590 && integer_zerop (TREE_IMAGPART (expr))));
591 }
下面,给定一个树形式的表达式,STRIP_NOPS剥除所有不改变机器模式的,最外层的转换表达式NOP_EXPR和NON_LVALUE_EXPR。
405 #define STRIP_NOPS(EXP) / in tree.h
406 while ((TREE_CODE (EXP) == NOP_EXPR /
407 || TREE_CODE (EXP) == CONVERT_EXPR /
408 || TREE_CODE (EXP) == NON_LVALUE_EXPR) /
409 && TREE_OPERAND (EXP, 0) != error_mark_node /
410 && (TYPE_MODE (TREE_TYPE (EXP)) /
411 == TYPE_MODE (TREE_TYPE (TREE_OPERAND (EXP, 0))))) /
412 (EXP) = TREE_OPERAND (EXP, 0)
其中,NOP_EXPR表示不需要产生代码的转换。NON_LVALUE_EXPR返回和参数EXP相同的值,但保证其为右值。这些表达式都不改变它们参数的值。
同样在1612行,如果参数expr是整型常量1或是复数常量1,integer_onep返回1。
596 int
597 integer_onep (tree expr) in tree.c
598 {
599 STRIP_NOPS (expr);
600
601 return ((TREE_CODE (expr) == INTEGER_CST
602 && ! TREE_CONSTANT_OVERFLOW (expr)
603 && TREE_INT_CST_LOW (expr) == 1
604 && TREE_INT_CST_HIGH (expr) == 0)
605 || (TREE_CODE (expr) == COMPLEX_CST
606 && integer_onep (TREE_REALPART (expr))
607 && integer_zerop (TREE_IMAGPART (expr))));
608 }
如果没有操作数可以被消除,那么就需要1616行的int_const_binop进行计算。
1185 static tree
1186 int_const_binop (enum tree_code code, tree arg1, tree arg2, int notrunc) in fold-const.c
1187 {
1188 unsigned HOST_WIDE_INT int1l, int2l;
1189 HOST_WIDE_INT int1h, int2h;
1190 unsigned HOST_WIDE_INT low;
1191 HOST_WIDE_INT hi;
1192 unsigned HOST_WIDE_INT garbagel;
1193 HOST_WIDE_INT garbageh;
1194 tree t;
1195 tree type = TREE_TYPE (arg1);
1196 int uns = TREE_UNSIGNED (type);
1197 int is_sizetype
1198 = (TREE_CODE (type) == INTEGER_TYPE && TYPE_IS_SIZETYPE (type));
1199 int overflow = 0;
1200 int no_overflow = 0;
1201
1202 int1l = TREE_INT_CST_LOW (arg1);
1203 int1h = TREE_INT_CST_HIGH (arg1);
1204 int2l = TREE_INT_CST_LOW (arg2);
1205 int2h = TREE_INT_CST_HIGH (arg2);
1206
1207 switch (code)
1208 {
1209 case BIT_IOR_EXPR:
1210 low = int1l | int2l, hi = int1h | int2h;
1211 break;
1212
1213 case BIT_XOR_EXPR:
1214 low = int1l ^ int2l, hi = int1h ^ int2h;
1215 break;
1216
1217 case BIT_AND_EXPR:
1218 low = int1l & int2l, hi = int1h & int2h;
1219 break;
1220
1221 case RSHIFT_EXPR:
1222 int2l = -int2l;
1223 case LSHIFT_EXPR:
1224 /* It's unclear from the C standard whether shifts can overflow.
1225 The following code ignores overflow; perhaps a C standard
1226 interpretation ruling is needed. */
1227 lshift_double (int1l, int1h, int2l, TYPE_PRECISION (type),
1228 &low, &hi, !uns);
1229 no_overflow = 1;
1230 break;
1231
1232 case RROTATE_EXPR:
1233 int2l = - int2l;
1234 case LROTATE_EXPR:
1235 lrotate_double (int1l, int1h, int2l, TYPE_PRECISION (type),
1236 &low, &hi);
1237 break;
对于位操作,只需把相应的高位部分和低位部分(在前端整型常量有128位)分别进行计算。
1.6.3.1.1.1.1. 左移
而对于移位操作,对于有符号数是需要符号位扩展的。在函数lshift_double中,参数ll是被移位数的低位部分,而hl是高位部分,count则是要移动的位数,计算结果存放在lv和hv中,arith非零指明是进行算术左移,否则为逻辑左移。
364 void
365 lshift_double (unsigned HOST_WIDE_INT l1, HOST_WIDE_INT h1, in fold-const.c
366 HOST_WIDE_INT count, unsigned int prec,
367 unsigned HOST_WIDE_INT *lv, HOST_WIDE_INT *hv, int arith)
368 {
369 unsigned HOST_WIDE_INT signmask;
370
371 if (count < 0)
372 {
373 rshift_double (l1, h1, -count, prec, lv, hv, arith);
374 return;
375 }
376
377 #ifdef SHIFT_COUNT_TRUNCATED
378 if (SHIFT_COUNT_TRUNCATED)
379 count %= prec;
380 #endif
381
382 if (count >= 2 * HOST_BITS_PER_WIDE_INT)
383 {
384 /* Shifting by the host word size is undefined according to the
385 ANSI standard, so we must handle this as a special case. */
386 *hv = 0;
387 *lv = 0;
388 }
389 else if (count >= HOST_BITS_PER_WIDE_INT)
390 {
391 *hv = l1 << (count - HOST_BITS_PER_WIDE_INT);
392 *lv = 0;
393 }
394 else
395 {
396 *hv = (((unsigned HOST_WIDE_INT) h1 << count)
397 | (l1 >> (HOST_BITS_PER_WIDE_INT - count - 1) >> 1));
398 *lv = l1 << count;
399 }
400
401 /* Sign extend all bits that are beyond the precision. */
402
403 signmask = -((prec > HOST_BITS_PER_WIDE_INT
404 ? ((unsigned HOST_WIDE_INT) *hv
405 >> (prec - HOST_BITS_PER_WIDE_INT - 1))
406 : (*lv >> (prec - 1))) & 1);
407
408 if (prec >= 2 * HOST_BITS_PER_WIDE_INT)
409 ;
410 else if (prec >= HOST_BITS_PER_WIDE_INT)
411 {
412 *hv &= ~((HOST_WIDE_INT) (-1) << (prec - HOST_BITS_PER_WIDE_INT));
413 *hv |= signmask << (prec - HOST_BITS_PER_WIDE_INT);
414 }
415 else
416 {
417 *hv = signmask;
418 *lv &= ~((unsigned HOST_WIDE_INT) (-1) << prec);
419 *lv |= signmask << prec;
420 }
421 }
虽然左移操作没有符号位的扩展,但如下图所示,在值的精度小于2*HOST_WIDE_INT时,超出精度外的位也需要符号位扩展。如果移位后的结果超出精度,超出的位需要被扩展符号位来填充。
图2精度外的位需要被符号位填充
图3超出精度的位需要被符号位填充