GCC-3.4.6源代码学习笔记(6)

1.2.3. 创建浮点常量节点

GCC中表示浮点常量的节点是下面所示的tree_real_cst

1.2.3.1.    tree_real_cst节点

 

702  struct tree_real_cst GTY(())                                                                                in tree.h

703  {

704    struct tree_common common;

705    struct real_value * real_cst_ptr;

706  };

 

节点tree_real_cst的核心是real_value,它根据有关的标准定义了浮点值。

 

43    struct real_value GTY(())                                                                                   in real.h

44    {

45      ENUM_BITFIELD (real_value_class) class : 2;

46      unsigned int sign : 1;

47      unsigned int signalling : 1;

48      unsigned int canonical : 1;

49      signed int exp : EXP_BITS;

50      unsigned long sig[SIGSZ];

51    };

52   

53    /* Various headers condition prototypes on #ifdef REAL_VALUE_TYPE, so it

54      needs to be a macro. We do need to continue to have a structure tag

55      so that other headers can forward declare it.  */

56    #define REAL_VALUE_TYPE struct real_value

 

在上面的定义中,使用了下面的用于实数值的宏。

 

37    #define SIGNIFICAND_BITS      (128 + HOST_BITS_PER_LONG)                    in real.h

38    #define EXP_BITS        (32 - 5)

39    #define MAX_EXP              ((1 << (EXP_BITS - 1)) - 1)

40    #define SIGSZ                     (SIGNIFICAND_BITS / HOST_BITS_PER_LONG)

41    #define SIG_MSB         ((unsigned long)1 << (HOST_BITS_PER_LONG - 1))

 

可见,在real_value结构中,域is close resembel to float format. Theexp是指数部分,域sig是有效位部分。

1.2.3.1.1.            real_value创建节点

real_value创建REAL_CST节点是个直截了当的过程。

 

466  tree

467  build_real (tree type, REAL_VALUE_TYPE d)                                                   in tree.c

468  {

469    tree v;

470    REAL_VALUE_TYPE *dp;

471    int overflow = 0;

472 

473    /* ??? Used to check for overflow here via CHECK_FLOAT_TYPE.

474      Consider doing it via real_convert now.  */

475 

476    v = make_node (REAL_CST);

477    dp = ggc_alloc (sizeof (REAL_VALUE_TYPE));

478    memcpy (dp, &d, sizeof (REAL_VALUE_TYPE));

479 

480    TREE_TYPE (v) = type;

481    TREE_REAL_CST_PTR (v) = dp;

482    TREE_OVERFLOW (v) = TREE_CONSTANT_OVERFLOW (v) = overflow;

483    return v;

484  }

 

注意481行,在tree_real_cst节点中,域TREE_REAL_CST_PTR指向被创建的real_value对象。

1.2.3.1.2.            从整型常量创建节点

C/C++语言中,在算术表达式里,有所谓的操作数类型提升。在浮点数与整数的算术计算中,整数需要被类型提升至浮点数。这个类型提升由下面的函数执行。

 

507  tree

508  build_real_from_int_cst (tree type, tree i)                                                           in tree.c

509  {

510    tree v;

511    int overflow = TREE_OVERFLOW (i);

512 

513    v = build_real (type, real_value_from_int_cst (type, i));

514 

515    TREE_OVERFLOW (v) |= overflow;

516    TREE_CONSTANT_OVERFLOW (v) |= overflow;

517    return v;

518  }

1.2.3.1.2.1.      从整型常量构建real_value

从整型常量构建代表浮点值的real_value不是容易的事情。在这里入口的函数是real_value_from_int_cst

 

489  REAL_VALUE_TYPE

490  real_value_from_int_cst (tree type, tree i)                                                           in tree.c

491  {

492    REAL_VALUE_TYPE d;

493 

494    /* Clear all bits of the real value type so that we can later do

495      bitwise comparisons to see if two values are the same.  */

496    memset (&d, 0, sizeof d);

497 

498    real_from_integer (&d, type ? TYPE_MODE (type) : VOIDmode,

499                     TREE_INT_CST_LOW (i), TREE_INT_CST_HIGH (i),

500                     TREE_UNSIGNED (TREE_TYPE (i)));

501    return d;

502  }

 

真正的转换由函数real_from_integer实现。

 

1957 void

1958 real_from_integer (REAL_VALUE_TYPE *r, enum machine_mode mode,            in real.c

1959                 unsigned HOST_WIDE_INT low, HOST_WIDE_INT high,

1960                 int unsigned_p)

1961 {

1962   if (low == 0 && high == 0)

1963     get_zero (r, 0);

 

对于常量0,构建很简单。注意到传入的sign被固定成0

 

128  static inline void

129  get_zero (REAL_VALUE_TYPE *r, int sign)                                                       in real.c

130  {

131    memset (r, 0, sizeof (*r));

132    r->sign = sign;

133  }

 

对于其他实数常量,正如我们所了解的,浮点数的精度是有限的,同样可以表达的最大最小值也是有限的。因此real_value中域class用于区分各种情况,它可以是下列值:rvc_zerorvc_normalrvc_infrvc_nan,其中,inf表示无穷,nan表示非有效值,它可由00产生。

对于非0的值,real_from_integer进行以下操作。

 

real_from_integer (continue)

 

1965   else

1966   {

1967     memset (r, 0, sizeof (*r));

1968     r->class = rvc_normal;

1969     r->sign = high < 0 && !unsigned_p;

1970     r->exp = 2 * HOST_BITS_PER_WIDE_INT;

1971

1972     if (r->sign)

1973     {

1974       high = ~high;

1975       if (low == 0)

1976         high += 1;

1977       else

1978         low = -low;

1979     }

1980

1981     if (HOST_BITS_PER_LONG == HOST_BITS_PER_WIDE_INT)

1982     {

1983       r->sig[SIGSZ-1] = high;

1984       r->sig[SIGSZ-2] = low;

1985     }

1986     else if (HOST_BITS_PER_LONG*2 == HOST_BITS_PER_WIDE_INT)

1987     {

1988       r->sig[SIGSZ-1] = high >> (HOST_BITS_PER_LONG - 1) >> 1;

1989       r->sig[SIGSZ-2] = high;

1990       r->sig[SIGSZ-3] = low >> (HOST_BITS_PER_LONG - 1) >> 1;

1991       r->sig[SIGSZ-4] = low;

1992     }

1993     else

1994       abort ();

1995

1996     normalize (r);

1997   }

1998

1999   if (mode != VOIDmode)

2000     real_convert (r, mode, r);

2001 }

 

注意从19721979行的代码它使用了2进制补码exp的初始值是2 * HOST_BITS_PER_WIDE_INT (x86/Linux它是128)

1.2.3.1.2.2.  生成值的规范化

sig保存了值的有效位,但是我们希望值由尽可能的精度,这是所谓的规范化(例如,对于0.000001,由于浮点格式已指定了固定的若干位用于指数部分和尾数部分,因此,如果将它改为1.000000*10-6,那么在使用同样多的位数的情况下,表达的精度提高了1000000倍)。因此,我们需要函数normalize

 

477  static void

478  normalize (REAL_VALUE_TYPE *r)                                                                       in real.c

479  {

480    int shift = 0, exp;

481    int i, j;

482 

483    /* Find the first word that is nonzero.  */

484    for (i = SIGSZ- 1; i >= 0; i--)

485      if (r->sig[i] == 0)

486        shift += HOST_BITS_PER_LONG;

487      else

488        break;

489 

490    /* Zero significand flushes to zero.  */

491    if (i < 0)

492    {

493      r->class = rvc_zero;

494      r->exp = 0;

495      return;

496   }

497 

498    /* Find the first bit that is nonzero.  */

499    for (j = 0; ; j++)

500      if (r->sig[i] & ((unsigned long)1 << (HOST_BITS_PER_LONG - 1 - j)))

501        break;

502    shift += j;

503 

504    if (shift > 0)

505    {

506      exp = r->exp - shift;

507      if (exp > MAX_EXP)

508        get_inf (r, r->sign);

509      else if (exp < -MAX_EXP)

510        get_zero (r, r->sign);

511      else

512      {

513        r->exp = exp;

514        lshift_significand (r, r, shift);

515      }

516    }

517  }

 

记得对于整型常量,我们首先为域exp填入128。这一步是经过深思熟虑的。比如,对于常量1,域exp1(显然,它表示值整数部分的比特数),这个值1将被移到数字串的最左端,跟着小数点后的长串0

 

236  static void

237  lshift_significand (REAL_VALUE_TYPE *r,                                                      in real.c

238                    const REAL_VALUE_TYPE *a, unsigned int n)

239  {

240    unsigned int i, ofs = n / HOST_BITS_PER_LONG;

241 

242    n &= HOST_BITS_PER_LONG - 1;

243    if (n == 0)

244    {

245      for (i = 0; ofs + i < SIGSZ; ++i)

246        r->sig[SIGSZ-1-i] = a->sig[SIGSZ-1-i-ofs];

247      for (; i < SIGSZ; ++i)

248        r->sig[SIGSZ-1-i] = 0;

249    }

250    else

251      for (i = 0; i < SIGSZ; ++i)

252      {

253        r->sig[SIGSZ-1-i]

254           = (((ofs + i >= SIGSZ? 0 : a->sig[SIGSZ-1-i-ofs]) << n)

255             | ((ofs + i + 1 >= SIGSZ? 0 : a->sig[SIGSZ-1-i-ofs-1])

256              >> (HOST_BITS_PER_LONG - n)));

257     }

258  }

 

函数lshift_significand不复杂,它按照要求向左移动比特位。

1.2.3.1.2.3.  转换到实数模式

虽然我们已经将整型常量放入real_value节点,它仍然是整型,和任何的浮点机器模式都不匹配。因此,我们需要进一步将值转换成机器模式所定义的格式。函数real_convert展示了这一过程。

 

2368 void

2369 real_convert (REAL_VALUE_TYPE *r, enum machine_mode mode,                    in real.c

2370            const REAL_VALUE_TYPE *a)

2371 {

2372   const struct real_format *fmt;

2373

2374   fmt = REAL_MODE_FORMAT (mode);

2375   if (fmt == NULL)

2376     abort ();

2377

2378   *r = *a;

2379   round_for_format (fmt, r);

2380

2381   /* round_for_format de-normalizes denormals.  Undo just that part.  */

2382   if (r->class == rvc_normal)

2383     normalize (r);

2384 }

 

149 #define REAL_MODE_FORMAT(MODE) (real_format_for_mode[(MODE)-MIN_MODE_FLOAT])

 

REAL_MODE_FORMAT的定义中real_format_for_mode是一个类型为real_format的全局数组。它记录了特定模式到浮点数转换的细节。它在machmode.def中被初始化,在当前版本中,它包含了2个格式,ieee_single_format&ieee_double_format 对于其他浮点格式,这个数组要被函数process_options 通过选项OVERRIDE_OPTIONS来改写。

 

106  struct real_format                                                                                              in real.h

107  {

108    /* Move to and from the target bytes.  */

109    void (*encode) (const struct real_format *, long *,

110                  const REAL_VALUE_TYPE *);

111    void (*decode) (const struct real_format *, REAL_VALUE_TYPE *,

112                  const long *);

113 

114    /* The radix of the exponent and digits of the significand.  */

115    int b;

116 

117    /* log2(b).  */

118    int log2_b;

119 

120    /* Size of the significand in digits of radix B.  */

121    int p;

122 

123    /* Size of the significant of a NaN, in digits of radix B.  */

124    int pnan;

125 

126    /* The minimum negative integer, x, such that b**(x-1) is normalized.  */

127    int emin;

128 

129   /* The maximum integer, x, such that b**(x-1) is representable.  */

130    int emax;

131 

132    /* The bit position of the sign bit, or -1 for a complex encoding.  */

133    int signbit;

134 

135    /* Properties of the format.  */

136    bool has_nans;

137    bool has_inf;

138    bool has_denorm;

139   bool has_signed_zero;

140    bool qnan_msb_set;

141  };

 

由于域breal_format 实际上可以支持小数点出现在任一地方。对于ieee_single_format&ieee_double_format我们有:

 

2652 const struct real_format ieee_single_format =                                                      in real.c

2653 {

2654   encode_ieee_single,

2655   decode_ieee_single,

2656   2,

2657   1,

2658   24,

2659   24,

2660   -125,

2661   128,

2662   31,

2663   true,

2664   true,

2665   true,

2666   true,

2667   true

2668 };

 

2875 const struct real_format ieee_double_format =                                                     in real.c

2876 {

2877   encode_ieee_double,

2878   decode_ieee_double,

2879   2,

2880   1,

2881   53,

2882   53,

2883   -1021,

2884   1024,

2885   63,

2886   true,

2887   true,

2888   true,

2889   true,

2890   true

2891 };

 

有了上面的了解,函数round_for_format虽然长,但不难理解。既然我们保留了若干的位数作为尾数(significand),为了使值尽可能的精确,我们应该尽量地使用这些位。因此以下将执行必要的位移。

 

2236 static void

2237 round_for_format (const struct real_format *fmt, REAL_VALUE_TYPE *r)         in real.c

2238 {

2239   int p2, np2, i, w;

2240   unsigned long sticky;

2241   bool guard, lsb;

2242   int emin2m1, emax2;

2243

2244   p2 = fmt->p * fmt->log2_b;

2245   emin2m1 = (fmt->emin - 1) * fmt->log2_b;

2246   emax2 = fmt->emax * fmt->log2_b;

2247

2248   np2 = SIGNIFICAND_BITS - p2;

2249   switch (r->class)

2250   {

2251     underflow:

2252       get_zero (r, r->sign);

2253     case rvc_zero:

2254       if (!fmt->has_signed_zero)

2255         r->sign = 0;

2256       return;

2257

2258     overflow:

2259       get_inf (r, r->sign);

2260     case rvc_inf:

2261       return;

2262

2263     case rvc_nan:

2264       clear_significand_below (r, np2);

2265       return;

2266

2267     case rvc_normal:

2268       break;

2269

2270     default:

2271       abort ();

2272   }

2273

2274   /* If we're not base2, normalize the exponent to a multiple of

2275     the true base.  */

2276   if (fmt->log2_b != 1)

2277   {

2278     int shift = r->exp & (fmt->log2_b - 1);

2279     if (shift)

2280     {

2281           shift = fmt->log2_b - shift;

2282           r->sig[0] |= sticky_rshift_significand (r, r, shift);

2283           r->exp += shift;

2284     }

2285   }

2286

2287   /* Check the range of the exponent. If we're out of range,

2288     either underflow or overflow.  */

2289   if (r->exp > emax2)

2290     goto overflow;

2291   else if (r->exp <= emin2m1)

2292   {

2293     int diff;

2294

2295     if (!fmt->has_denorm)

2296     {

2297           /* Don't underflow completely until we've had a chance to round.  */

2298           if (r->exp < emin2m1)

2299             goto underflow;

2300        }

2301     else

2302     {

2303       diff = emin2m1 - r->exp + 1;

2304       if (diff > p2)

2305         goto underflow;

2306

2307       /* De-normalize the significand.  */

2308       r->sig[0] |= sticky_rshift_significand (r, r, diff);

2309       r->exp += diff;

2310     }

2311   }

 

考虑到我们使用的格式可能不是基于2进制的(假设为base进制),real_format中的域log2_b记录了log2(base),而域p记录了尾数以base进制表达的位数,因此在上面的2244行将尾数转换为2进制数。而22452246行相应地算出最大最小值。

我们已经看到,real_value的域exp是值以2进制表达的指数(exponential)部分。如果目标格式不是2进制,而且它的指数部分与2进制的尾数部分有重叠,我们需要通过sticky_rshift_significand对尾数做右移以解决重叠。另外,如果指数部分已溢出,也只有牺牲精度,对尾数右移。

 

168  static bool

169  sticky_rshift_significand (REAL_VALUE_TYPE *r,                                            in real.c

170                         const REAL_VALUE_TYPE *a, unsigned int n)

171  {

172    unsigned long sticky = 0;

173    unsigned int i, ofs = 0;

174 

175    if (n >= HOST_BITS_PER_LONG)

176    {

177      for (i = 0, ofs = n / HOST_BITS_PER_LONG; i < ofs; ++i)

178        sticky |= a->sig[i];

179      n &= HOST_BITS_PER_LONG - 1;

180    }

181 

182    if (n != 0)

183    {

184      sticky |= a->sig[ofs] & (((unsigned long)1 << n) - 1);

185      for (i = 0; i < SIGSZ; ++i)

186      {

187        r->sig[i]

188          = (((ofs + i >= SIGSZ? 0 : a->sig[ofs + i]) >> n)

189             | ((ofs + i + 1 >= SIGSZ? 0 : a->sig[ofs + i + 1])

190              << (HOST_BITS_PER_LONG - n)));

191      }

192    }

193    else

194    {

195      for (i = 0; ofs + i < SIGSZ; ++i)

196        r->sig[i] = a->sig[ofs + i];

197      for (; i < SIGSZ; ++i)

198        r->sig[i] = 0;

199    }

200 

201    return sticky != 0;

202  }

 

如果任一比特被移出,sticky_rshift_significand将返回true。这个返回值(这里为1)会和目标格式的尾数部分的最低位(the least significant bit)进行异或。显然,这个取整操作会减少精度。

然而,如果指数部分超出了目标格式能表达的最大值时,毫无疑问发生了溢出。另外,如果指数部分小于目标格式的规范化的最小值时,所做的操作取决于目标格式是否支持去规范化(denormalize)。对于支持去规范化的格式,如果尾数部分仍然有效,则调用sticky_rshift_significand进行去规范化。

 

round_for_format (continue)

 

2313   /* There are P2 true significand bits, followed by one guard bit,

2314    followed by one sticky bit, followed by stuff. Fold nonzero

2315    stuff into the sticky bit.  */

2316

2317   sticky = 0;

2318   for (i = 0, w = (np2 - 1) / HOST_BITS_PER_LONG; i < w; ++i)

2319     sticky |= r->sig[i];

2320   sticky |=

2321     r->sig[w] & (((unsigned long)1 << ((np2 - 1) % HOST_BITS_PER_LONG)) - 1);

2322

2323   guard = test_significand_bit (r, np2 - 1);

2324   lsb = test_significand_bit (r, np2);

2325

2326   /* Round to even.  */

2327   if (guard && (sticky || lsb))

2328   {

2329     REAL_VALUE_TYPE u;

2330     get_zero (&u, 0);

2331     set_significand_bit (&u, np2);

2332

2333     if (add_significands (r, r, &u))

2334     {

2335          /* Overflow. Means the significand had been all ones, and

2336            is now all zeros. Need to increase the exponent, and

2337            possibly re-normalize it.  */

2338           if (++r->exp > emax2)

2339             goto overflow;

2340          r->sig[SIGSZ-1] = SIG_MSB;

2341

2342           if (fmt->log2_b != 1)

2343           {

2344           int shift = r->exp & (fmt->log2_b - 1);

2345           if (shift)

2346         {

2347           shift = fmt->log2_b - shift;

2348           rshift_significand (r, r, shift);

2349           r->exp += shift;

2350           if (r->exp > emax2)

2351             goto overflow;

2352         }

2353       }

2354     }

2355   }

2356

2357   /* Catch underflow that we deferred until after rounding.  */

2358   if (r->exp <= emin2m1)

2359     goto underflow;

2360

2361   /* Clear out trailing garbage.  */

2362   clear_significand_below (r, np2);

2363 }

 

上面的代码处理由于不同小数点位导致的位移。而round_for_format的最后部分处理不同大小的尾数部分。我们已经看过,对于real_value,用于尾数部分的比特数很大,为128 + HOST_BITS_PER_LONG(对于Linux/x86,它是192),而浮点格式使用比特数要少一些。因此把real_value转换到浮点格式,值截取是需要仔细考虑的问题。

注意在2324行,在np2位的是被截取掉的最高位,在2323guard则是被截取掉的次高位。作为好的截取方法,如果被截取掉的值大于guard,我们需要通过为截取后的值加上1。函数add_significands完成这个加1操作。

 

275  static inline bool

276  add_significands (REAL_VALUE_TYPE *r, const REAL_VALUE_TYPE *a,               in real.c

277                  const REAL_VALUE_TYPE *b)

278  {

279    bool carry = false;

280    int i;

281 

282    for (i = 0; i < SIGSZ; ++i)

283    {

284      unsigned long ai = a->sig[i];

285      unsigned long ri = ai + b->sig[i];

286 

287      if (carry)

288      {

289        carry = ri < ai;

290        carry |= ++ri == 0;

291      }

292      else

293        carry = ri < ai;

294 

295      r->sig[i] = ri;

296    }

297 

298    return carry;

299  }

 

因为尾数被看作无符号数,如果上述的和小于其操作数,表明发生了进位,carry被设置。如果最终和带有进位,我们需要调整指数部分。在这里我们为指数部分的值加上1。如果指数部分原有值为全1,则发生了溢出。如果没有溢出,就要从2340行开始对这个值重新规范化(re-normalize)。

 

你可能感兴趣的:(struct,tree,Integer,Build,float,structure)