您的位置:首页 > 编程语言

GCC-3.4.6源代码学习笔记(10续3)

2010-03-05 12:39 507 查看
1.6.3.1.1.1.4. 加法和减法
回到int_const_binop,接下来的操作是加法和减法。

int_const_binop (continue)

1239 case PLUS_EXPR:
1240 overflow = add_double (int1l, int1h, int2l, int2h, &low, &hi);
1241 break;
1242
1243 case MINUS_EXPR:
1244 neg_double (int2l, int2h, &low, &hi);
1245 add_double (int1l, int1h, low, hi, &low, &hi);
1246 overflow = OVERFLOW_SUM_SIGN (hi, int2h, int1h);
1247 break;
1248
1249 case MULT_EXPR:
1250 overflow = mul_double (int1l, int1h, int2l, int2h, &low, &hi);
1251 break;
1252
1253 case TRUNC_DIV_EXPR:
1254 case FLOOR_DIV_EXPR: case CEIL_DIV_EXPR:
1255 case EXACT_DIV_EXPR:
1256 /* This is a shortcut for a common special case. */
1257 if (int2h == 0 && (HOST_WIDE_INT) int2l > 0
1258 && ! TREE_CONSTANT_OVERFLOW (arg1)
1259 && ! TREE_CONSTANT_OVERFLOW (arg2)
1260 && int1h == 0 && (HOST_WIDE_INT) int1l >= 0)
1261 {
1262 if (code == CEIL_DIV_EXPR)
1263 int1l += int2l - 1;
1264
1265 low = int1l / int2l, hi = 0;
1266 break;
1267 }
1268
1269 /* ... fall through ... */

函数add_double接受两个操作数,在它的参数列表中,l1和h1是第一个操作数的高低位部分,而l2和h2是第二个操作数的高低位部分。

261 int
262 add_double (unsigned HOST_WIDE_INT l1, HOST_WIDE_INT h1, in fold-const.c
263 unsigned HOST_WIDE_INT l2, HOST_WIDE_INT h2,
264 unsigned HOST_WIDE_INT *lv, HOST_WIDE_INT *hv)
265 {
266 unsigned HOST_WIDE_INT l;
267 HOST_WIDE_INT h;
268
269 l = l1 + l2;
270 h = h1 + h2 + (l < l1);
271
272 *lv = l;
273 *hv = h;
274 return OVERFLOW_SUM_SIGN (h1, h2, h);
275 }

如果低位部分的和变小了,这表明有进位,在上面的270行,这个进位被加入高位部分。而对于高位部分,这种情况则表明发生了溢出。溢出发生时,如果A和B有相同的符号位,但A和SUM符号位符号位相反。下面的宏根据这个原理来检测溢出。

136 #define OVERFLOW_SUM_SIGN(a, b, sum) ((~((a) ^ (b)) & ((a) ^ (sum))) < 0)

neg_double看上去有些奇怪,这是因为使用了2进制补码的缘故。

282 int
283 neg_double (unsigned HOST_WIDE_INT l1, HOST_WIDE_INT h1, in fold-const.c
284 unsigned HOST_WIDE_INT *lv, HOST_WIDE_INT *hv)
285 {
286 if (l1 == 0)
287 {
288 *lv = 0;
289 *hv = - h1;
290 return (*hv & h1) < 0;
291 }
292 else
293 {
294 *lv = -l1;
295 *hv = ~h1;
296 return 0;
297 }
298 }
1.6.3.1.1.1.5. 乘法
要实现正确而有效的乘法,需要仔细处理溢出情况,因为乘法的结果的位数会是乘数的双倍。

307 int
308 mul_double (unsigned HOST_WIDE_INT l1, HOST_WIDE_INT h1, in fold-const.c
309 unsigned HOST_WIDE_INT l2, HOST_WIDE_INT h2,
310 unsigned HOST_WIDE_INT *lv, HOST_WIDE_INT *hv)
311 {
312 HOST_WIDE_INT arg1[4];
313 HOST_WIDE_INT arg2[4];
314 HOST_WIDE_INT prod[4 * 2];
315 unsigned HOST_WIDE_INT carry;
316 int i, j, k;
317 unsigned HOST_WIDE_INT toplow, neglow;
318 HOST_WIDE_INT tophigh, neghigh;
319
320 encode (arg1, l1, h1);
321 encode (arg2, l2, h2);
322
323 memset (prod, 0, sizeof prod);
324
325 for (i = 0; i < 4; i++)
326 {
327 carry = 0;
328 for (j = 0; j < 4; j++)
329 {
330 k = i + j;
331 /* This product is <= 0xFFFE0001, the sum <= 0xFFFF0000. */
332 carry += arg1[i] * arg2[j];
333 /* Since prod[p] < 0xFFFF, this sum <= 0xFFFFFFFF. */
334 carry += prod[k];
335 prod[k] = LOWPART (carry);
336 carry = HIGHPART (carry);
337 }
338 prod[i + 4] = carry;
339 }
340
341 decode (prod, lv, hv); /* This ignores prod[4] through prod[4*2-1] */
342
343 /* Check for overflow by calculating the top half of the answer in full;
344 it should agree with the low half's sign bit. */
345 decode (prod + 4, &toplow, &tophigh);
346 if (h1 < 0)
347 {
348 neg_double (l2, h2, &neglow, &neghigh);
349 add_double (neglow, neghigh, toplow, tophigh, &toplow, &tophigh);
350 }
351 if (h2 < 0)
352 {
353 neg_double (l1, h1, &neglow, &neghigh);
354 add_double (neglow, neghigh, toplow, tophigh, &toplow, &tophigh);
355 }
356 return (*hv < 0 ? ~(toplow & tophigh) : toplow | tophigh) != 0;
357 }

为了处理溢出的问题,320和321行的encode会把乘数放入双倍数尺寸的buffer中。

153 static void in fold-const.c
154 encode (HOST_WIDE_INT *words, unsigned HOST_WIDE_INT low, HOST_WIDE_INT hi)
155 {
156 words[0] = LOWPART (low);
157 words[1] = HIGHPART (low);
158 words[2] = LOWPART (hi);
159 words[3] = HIGHPART (hi);
160 }

为了看清325行FOR循环的作用,我们使用下面的例子来展示其过程。假定在乘法中整型为2位大小,结果临时放在4位大小的缓存中。

65 (then h1 = 6, l1 = 5)
X 23 (then h2 = 2, l2 = 3)
l1 * l2 ( = 15)
+ carry ( = 0)
+ prod[0+0] ( = 0)
carry ( = 1) ß 15 à prod[0+0] ( = 5) (end of l1 * l2)
h1 * l2 ( = 18)
+ carry ( = 1)
+ prod[1+0] ( = 0)
carry ( = 1) ß19 à prod[1+0] ( = 9) (end of h1 * l2, prod[0+2] = carry = 1)
l1 * h2 ( = 10)
+ carry ( = 0)
+ prod[0+1] ( = 9)
carry ( = 1) ß 19 à prod[0+1] ( = 8) (end of l1 * h2)
h1 * h2 ( = 12)
+ carry ( = 1)
+ prod[1+1] ( = 1)
carry ( = 1) ß 14 à prod[1+1] ( = 4) (end of h1 * h2, prod[1+2] = carry = 1)

lowpart = prod[0] + prod[1]*10 = 95
highpart = prod[2] + prod[3]*10 = 14;
result = 1495 (在2位大小的整型中,溢出)
图7 乘法的过程
另一方面,decode将相应的prod合成出结果。

167 static void
168 decode (HOST_WIDE_INT *words, unsigned HOST_WIDE_INT *low, in fold-const.c
169 HOST_WIDE_INT *hi)
170 {
171 *low = words[0] + words[1] * BASE;
172 *hi = words[2] + words[3] * BASE;
173 }

147 #define BASE ((unsigned HOST_WIDE_INT) 1 << HOST_BITS_PER_WIDE_INT / 2)
1.6.3.1.1.1.3. 除法和取整
整型常量的算术计算中最复杂的部分是除法和取整。当进行整数除法时,其结果包含商(quotient)和余数(remainder)。通常有多种余数和商的方案。函数div_and_round_double提供了这个机制。

int_const_binop (continue)

1271 case ROUND_DIV_EXPR:
1272 if (int2h == 0 && int2l == 1)
1273 {
1274 low = int1l, hi = int1h;
1275 break;
1276 }
1277 if (int1l == int2l && int1h == int2h
1278 && ! (int1l == 0 && int1h == 0))
1279 {
1280 low = 1, hi = 0;
1281 break;
1282 }
1283 overflow = div_and_round_double (code, uns, int1l, int1h, int2l, int2h,
1284 &low, &hi, &garbagel, &garbageh);
1285 break;
1286
1287 case TRUNC_MOD_EXPR:
1288 case FLOOR_MOD_EXPR: case CEIL_MOD_EXPR:
1289 /* This is a shortcut for a common special case. */
1290 if (int2h == 0 && (HOST_WIDE_INT) int2l > 0
1291 && ! TREE_CONSTANT_OVERFLOW (arg1)
1292 && ! TREE_CONSTANT_OVERFLOW (arg2)
1293 && int1h == 0 && (HOST_WIDE_INT) int1l >= 0)
1294 {
1295 if (code == CEIL_MOD_EXPR)
1296 int1l += int2l - 1;
1297 low = int1l % int2l, hi = 0;
1298 break;
1299 }
1300
1301 /* ... fall through ... */
1302
1303 case ROUND_MOD_EXPR:
1304 overflow = div_and_round_double (code, uns,
1305 int1l, int1h, int2l, int2h,
1306 &garbagel, &garbageh, &low, &hi);
1307 break;
1308
1309 case MIN_EXPR:
1310 case MAX_EXPR:
1311 if (uns)
1312 low = (((unsigned HOST_WIDE_INT) int1h
1313 < (unsigned HOST_WIDE_INT) int2h)
1314 || (((unsigned HOST_WIDE_INT) int1h
1315 == (unsigned HOST_WIDE_INT) int2h)
1316 && int1l < int2l));
1317 else
1318 low = (int1h < int2h
1319 || (int1h == int2h && int1l < int2l));
1320
1321 if (low == (code == MIN_EXPR))
1322 low = int1l, hi = int1h;
1323 else
1324 low = int2l, hi = int2h;
1325 break;
1326
1327 default:
1328 abort ();
1329 }

函数div_and_round_double的参数有:code – 操作码,uns – 是否为有符号的除法,lnum_orig和hnum_orig – 被除数(分子),lden_orig和hden_orig – 除数(分母),lquo和hquo – 商,lrem和hrem – 余数。

540 int
541 div_and_round_double (enum tree_code code, int uns, in fold-const.c
542 unsigned HOST_WIDE_INT lnum_orig, /* num == numerator == dividend */
543 HOST_WIDE_INT hnum_orig,
544 unsigned HOST_WIDE_INT lden_orig, /* den == denominator == divisor */
545 HOST_WIDE_INT hden_orig,
546 unsigned HOST_WIDE_INT *lquo,
547 HOST_WIDE_INT *hquo, unsigned HOST_WIDE_INT *lrem,
548 HOST_WIDE_INT *hrem)
549 {
550 int quo_neg = 0;
551 HOST_WIDE_INT num[4 + 1]; /* extra element for scaling. */
552 HOST_WIDE_INT den[4], quo[4];
553 int i, j;
554 unsigned HOST_WIDE_INT work;
555 unsigned HOST_WIDE_INT carry = 0;
556 unsigned HOST_WIDE_INT lnum = lnum_orig;
557 HOST_WIDE_INT hnum = hnum_orig;
558 unsigned HOST_WIDE_INT lden = lden_orig;
559 HOST_WIDE_INT hden = hden_orig;
560 int overflow = 0;
561
562 if (hden == 0 && lden == 0)
563 overflow = 1, lden = 1;
564
565 /* Calculate quotient sign and convert operands to unsigned. */
566 if (!uns)
567 {
568 if (hnum < 0)
569 {
570 quo_neg = ~ quo_neg;
571 /* (minimum integer) / (-1) is the only overflow case. */
572 if (neg_double (lnum, hnum, &lnum, &hnum)
573 && ((HOST_WIDE_INT) lden & hden) == -1)
574 overflow = 1;
575 }
576 if (hden < 0)
577 {
578 quo_neg = ~ quo_neg;
579 neg_double (lden, hden, &lden, &hden);
580 }
581 }
582
583 if (hnum == 0 && hden == 0)
584 { /* single precision */
585 *hquo = *hrem = 0;
586 /* This unsigned division rounds toward zero. */
587 *lquo = lnum / lden;
588 goto finish_up;
589 }
590
591 if (hnum == 0)
592 { /* trivial case: dividend < divisor */
593 /* hden != 0 already checked. */
594 *hquo = *lquo = 0;
595 *hrem = hnum;
596 *lrem = lnum;
597 goto finish_up;
598 }
599
600 memset (quo, 0, sizeof quo);
601
602 memset (num, 0, sizeof num); /* to zero 9th element */
603 memset (den, 0, sizeof den);
604
605 encode (num, lnum, hnum);
606 encode (den, lden, hden);
607
608 /* Special code for when the divisor < BASE. */
609 if (hden == 0 && lden < (unsigned HOST_WIDE_INT) BASE)
610 {
611 /* hnum != 0 already checked. */
612 for (i = 4 - 1; i >= 0; i--)
613 {
614 work = num[i] + carry * BASE;
615 quo[i] = work / lden;
616 carry = work % lden;
617 }
618 }
619 else
620 {
621 /* Full double precision division,
622 with thanks to Don Knuth's "Seminumerical Algorithms". */
623 int num_hi_sig, den_hi_sig;
624 unsigned HOST_WIDE_INT quo_est, scale;
625
626 /* Find the highest nonzero divisor digit. */
627 for (i = 4 - 1;; i--)
628 if (den[i] != 0)
629 {
630 den_hi_sig = i;
631 break;
632 }
633
634 /* Insure that the first digit of the divisor is at least BASE/2.
635 This is required by the quotient digit estimation algorithm. */
636
637 scale = BASE / (den[den_hi_sig] + 1);
638 if (scale > 1)
639 { /* scale divisor and dividend */
640 carry = 0;
641 for (i = 0; i <= 4 - 1; i++)
642 {
643 work = (num[i] * scale) + carry;
644 num[i] = LOWPART (work);
645 carry = HIGHPART (work);
646 }
647
648 num[4] = carry;
649 carry = 0;
650 for (i = 0; i <= 4 - 1; i++)
651 {
652 work = (den[i] * scale) + carry;
653 den[i] = LOWPART (work);
654 carry = HIGHPART (work);
655 if (den[i] != 0) den_hi_sig = i;
656 }
657 }

从637行开始的代码片段是算法的重要部分。它把除数和被除数同时同倍扩大,使得除数的最高位部分不小于Base/2。这样做的原因,考虑下面的表达式:
abc * d和abc * (d+1),后者可以被转换为abc * d + abc。如果a大于Base/2(对于十进制,它的值是5),在后一个表达式的值中多出的abc将在结果中导致多一个进位,这使它与前一个表达式的值很容易就区分开了。

div_and_round_double (continue)

659 num_hi_sig = 4;
660
661 /* Main loop */
662 for (i = num_hi_sig - den_hi_sig - 1; i >= 0; i--)
663 {
664 /* Guess the next quotient digit, quo_est, by dividing the first
665 two remaining dividend digits by the high order quotient digit.
666 quo_est is never low and is at most 2 high. */
667 unsigned HOST_WIDE_INT tmp;
668
669 num_hi_sig = i + den_hi_sig + 1;
670 work = num[num_hi_sig] * BASE + num[num_hi_sig - 1];
671 if (num[num_hi_sig] != den[den_hi_sig])
672 quo_est = work / den[den_hi_sig];
673 else
674 quo_est = BASE - 1;
675
676 /* Refine quo_est so it's usually correct, and at most one high. */
677 tmp = work - quo_est * den[den_hi_sig];
678 if (tmp < BASE
679 && (den[den_hi_sig - 1] * quo_est
680 > (tmp * BASE + num[num_hi_sig - 2])))
681 quo_est--;
682
683 /* Try QUO_EST as the quotient digit, by multiplying the
684 divisor by QUO_EST and subtracting from the remaining dividend.
685 Keep in mind that QUO_EST is the I - 1st digit. */
686
687 carry = 0;
688 for (j = 0; j <= den_hi_sig; j++)
689 {
690 work = quo_est * den[j] + carry;
691 carry = HIGHPART (work);
692 work = num[i + j] - LOWPART (work);
693 num[i + j] = LOWPART (work);
694 carry += HIGHPART (work) != 0;
695 }
696
697 /* If quo_est was high by one, then num[i] went negative and
698 we need to correct things. */
699 if (num[num_hi_sig] < (HOST_WIDE_INT) carry)
700 {
701 quo_est--;
702 carry = 0; /* add divisor back in */
703 for (j = 0; j <= den_hi_sig; j++)
704 {
705 work = num[i + j] + den[j] + carry;
706 carry = HIGHPART (work);
707 num[i + j] = LOWPART (work);
708 }
709
710 num [num_hi_sig] += carry;
711 }
712
713 /* Store the quotient digit. */
714 quo[i] = quo_est;
715 }
716 }

结合上面的代码,考虑以下例子:789 / 125 (注意这个例子中base是10)。
扩大后,表达式变为:3945 / 725,而且我们得到下面的数组:
num[4] = 0, num[3] = 3, num[2] = 9, num[1] = 4, num[0] = 5
den[3] = 0, den[2] = 7, den[1] = 2, den[0] = 5, den_hi_sig = 2.
对于662行第一个循环,i = 4-2-1 = 2,商不会多于2个位数。在这个循环里,quo[1] = 0。对于第二个循环,它以以下方式工作。
3945 (num[0])
carry ß 25 (725 *5)
0 à num[0]
3940
carryß10 (725 *5)
2 (carry)
2 à num[1]
3900
carryß35 (725 * 5)
1 (carry)
3 à num[2]
3000
3 (carry)
0 à num[3]
quot[0] = 5, quot[1] = 0
图8 除法和取整

div_and_round_double (continue)

718 decode (quo, lquo, hquo);
719
720 finish_up:
721 /* If result is negative, make it so. */
722 if (quo_neg)
723 neg_double (*lquo, *hquo, lquo, hquo);
724
725 /* compute trial remainder: rem = num - (quo * den) */
726 mul_double (*lquo, *hquo, lden_orig, hden_orig, lrem, hrem);
727 neg_double (*lrem, *hrem, lrem, hrem);
728 add_double (lnum_orig, hnum_orig, *lrem, *hrem, lrem, hrem);
729
730 switch (code)
731 {
732 case TRUNC_DIV_EXPR:
733 case TRUNC_MOD_EXPR: /* round toward zero */
734 case EXACT_DIV_EXPR: /* for this one, it shouldn't matter */
735 return overflow;
736
737 case FLOOR_DIV_EXPR:
738 case FLOOR_MOD_EXPR: /* round toward negative infinity */
739 if (quo_neg && (*lrem != 0 || *hrem != 0)) /* ratio < 0 && rem != 0 */
740 {
741 /* quo = quo - 1; */
742 add_double (*lquo, *hquo, (HOST_WIDE_INT) -1, (HOST_WIDE_INT) -1,
743 lquo, hquo);
744 }
745 else
746 return overflow;
747 break;
748
749 case CEIL_DIV_EXPR:
750 case CEIL_MOD_EXPR: /* round toward positive infinity */
751 if (!quo_neg && (*lrem != 0 || *hrem != 0)) /* ratio > 0 && rem != 0 */
752 {
753 add_double (*lquo, *hquo, (HOST_WIDE_INT) 1, (HOST_WIDE_INT) 0,
754 lquo, hquo);
755 }
756 else
757 return overflow;
758 break;
759
760 case ROUND_DIV_EXPR:
761 case ROUND_MOD_EXPR: /* round to closest integer */
762 {
763 unsigned HOST_WIDE_INT labs_rem = *lrem;
764 HOST_WIDE_INT habs_rem = *hrem;
765 unsigned HOST_WIDE_INT labs_den = lden, ltwice;
766 HOST_WIDE_INT habs_den = hden, htwice;
767
768 /* Get absolute values. */
769 if (*hrem < 0)
770 neg_double (*lrem, *hrem, &labs_rem, &habs_rem);
771 if (hden < 0)
772 neg_double (lden, hden, &labs_den, &habs_den);
773
774 /* If (2 * abs (lrem) >= abs (lden)) */
775 mul_double ((HOST_WIDE_INT) 2, (HOST_WIDE_INT) 0,
776 labs_rem, habs_rem, <wice, &htwice);
777
778 if (((unsigned HOST_WIDE_INT) habs_den
779 < (unsigned HOST_WIDE_INT) htwice)
780 || (((unsigned HOST_WIDE_INT) habs_den
781 == (unsigned HOST_WIDE_INT) htwice)
782 && (labs_den < ltwice)))
783 {
784 if (*hquo < 0)
785 /* quo = quo - 1; */
786 add_double (*lquo, *hquo,
787 (HOST_WIDE_INT) -1, (HOST_WIDE_INT) -1, lquo, hquo);
788 else
789 /* quo = quo + 1; */
790 add_double (*lquo, *hquo, (HOST_WIDE_INT) 1, (HOST_WIDE_INT) 0,
791 lquo, hquo);
792 }
793 else
794 return overflow;
795 }
796 break;
797
798 default:
799 abort ();
800 }
801
802 /* Compute true remainder: rem = num - (quo * den) */
803 mul_double (*lquo, *hquo, lden_orig, hden_orig, lrem, hrem);
804 neg_double (*lrem, *hrem, lrem, hrem);
805 add_double (lnum_orig, hnum_orig, *lrem, *hrem, lrem, hrem);
806 return overflow;
807 }

div_and_round_double的余下部分则是根据类型对结果进行取整。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: