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

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接受两个操作数,在它的参数列表中,l1h1是第一个操作数的高低位部分,而l2h2是第二个操作数的高低位部分。

 

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行,这个进位被加入高位部分。而对于高位部分,这种情况则表明发生了溢出。溢出发生时,如果AB有相同的符号位,但ASUM符号位符号位相反。下面的宏根据这个原理来检测溢出。

 

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  }

 

为了处理溢出的问题,320321行的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  }

 

为了看清325FOR循环的作用,我们使用下面的例子来展示其过程。假定在乘法中整型为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_orighnum_orig被除数(分子),lden_orighden_orig除数(分母),lquohquo商,lremhrem余数。

 

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 * dabc * (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 (注意这个例子中base10)。

扩大后,表达式变为: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, &ltwice, &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的余下部分则是根据类型对结果进行取整。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值