Ignore:
Timestamp:
Nov 8, 2007, 10:27:21 AM (18 years ago)
Author:
oliver
Message:

Replace the use of floats for immediate values with the use of integers for a 4.5% improvement in SunSpider.

Reviewed by Darin.

Unfortunately this change results in NaN, +Inf, -Inf, and -0 being heap allocated now, but
we should now have faster array access, faster immediate to double conversion, and the
potential to further improve bitwise operators in future.

This also removes the need for unions to avoid strict aliasing problems when extracting
a value from immediates.

  • kjs/JSImmediate.h:

(KJS::):
(KJS::JSImmediate::trueImmediate):
(KJS::JSImmediate::falseImmediate):
(KJS::JSImmediate::undefinedImmediate):
(KJS::JSImmediate::nullImmediate):
(KJS::JSImmediate::toBoolean):

  • kjs/value.h:

(KJS::jsNaN):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/kjs/JSImmediate.h

    r27416 r27602  
    2626#include <wtf/Assertions.h>
    2727#include <wtf/AlwaysInline.h>
     28#include <limits>
     29#include <math.h>
    2830#include <stdarg.h>
    2931#include <stdint.h>
     
    3941/*
    4042 * A JSValue*  is either a pointer to a cell (a heap-allocated object) or an immediate (a type-tagged
    41  * IEEE floating point bit pattern masquerading as a pointer). The low two bits in a JSValue* are available
     43 * signed int masquerading as a pointer). The low two bits in a JSValue* are available
    4244 * for type tagging because allocator alignment guarantees they will be 00 in cell pointers.
    4345 *
     
    4850 *
    4951 * JSImmediate:   XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX                 TT
    50  *             [ high 30 bits: IEEE encoded float ] [ low 2 bits -- type tag ]
    51  *
    52  * The bit "payload" (the hight 30 bits) of a non-numeric immediate is its numeric equivalent. For example,
    53  * the payload of null is 0.0. This makes JSValue::toNumber() a simple bitmask for all immediates.
     52 *               [ high 30 bits: signed int ]      [ low 2 bits -- type tag ]
     53 *
     54 * The bit "payload" (the high 30 bits) is a 30 bit signed int for immediate numbers, a flag to distinguish true/false
     55 * and undefined/null.
    5456 *
    5557 * Notice that the JSType value of NullType is 4, which requires 3 bits to encode. Since we only have 2 bits
    5658 * available for type tagging, we tag the null immediate with UndefinedType, and JSImmediate::type() has
    57  * to sort them out. Null and Undefined don't otherwise get confused because the numeric value of Undefined is
    58  * NaN, not 0.0.
     59 * to sort them out.
    5960 */
    6061
     
    9798    static JSValue* trueImmediate();
    9899    static JSValue* falseImmediate();
    99     static JSValue* NaNImmediate();
    100100    static JSValue* undefinedImmediate();
    101101    static JSValue* nullImmediate();
     
    119119    }
    120120   
    121     // NOTE: With f-strict-aliasing enabled, unions are the only safe way to do type masquerading.
    122 
    123     union FloatUnion {
    124         uint32_t asBits;
    125         float    asFloat;
    126     };
    127 
    128     union DoubleUnion {
    129         uint64_t asBits;
    130         double   asDouble;
    131     };
    132 
    133121    // we support 32-bit platforms with sizes like this
    134122    static const bool is32bit =
     
    149137    static ALWAYS_INLINE JSValue* fromDouble(double d)
    150138    {
    151         FloatUnion floatUnion;
    152         floatUnion.asFloat = static_cast<float>(d);
    153 
    154         // check for data loss from tagging
    155         if ((floatUnion.asBits & TagMask) != 0)
    156             return 0;
    157 
    158         // check for data loss from conversion to float. This
    159         // unfortunately also rejects NaN, however, it costs more to
    160         // do a check that will include NaN than is saved by letting
    161         // NaN be an immediate.
    162         if (floatUnion.asFloat == d)
    163             return tag(floatUnion.asBits, NumberType);
    164 
    165         return 0;
    166     }
    167 
    168     static ALWAYS_INLINE float toFloat(const JSValue* v)
     139        const int32_t intVal = static_cast<int>(d);
     140       
     141        // On 32 bit systems immediate values are restricted to a 30 bit signed value
     142        if ((intVal <= -(1 << 29)) | (intVal >= ((1 << 29) - 1)))
     143            return 0;
     144       
     145        // Check for data loss from conversion to int. This
     146        // will reject NaN, +/-Inf, and -0.
     147        // Happily none of these are as common as raw int values
     148        if ((intVal != d) || (signbit(d) && !intVal))
     149            return 0;
     150       
     151        return tag(intVal << 2, NumberType);
     152    }
     153
     154    static ALWAYS_INLINE double toDouble(const JSValue* v)
    169155    {
    170156        ASSERT(isImmediate(v));
    171 
    172         FloatUnion floatUnion;
    173         floatUnion.asBits = static_cast<uint32_t>(unTag(v));
    174         return floatUnion.asFloat;
    175     }
    176 
    177     static ALWAYS_INLINE double toDouble(const JSValue* v)
    178     {
    179         return toFloat(v);
    180     }
    181 
     157        const int32_t i = static_cast<int32_t>(unTag(v)) >> 2;
     158        if (JSImmediate::getTag(v) == UndefinedType && i)
     159            return std::numeric_limits<double>::quiet_NaN();
     160        return i;
     161    }
     162   
    182163    static ALWAYS_INLINE bool getTruncatedInt32(const JSValue* v, int32_t& i)
    183164    {
    184         float f = toFloat(v);
    185         if (!(f >= -2147483648.0F && f < 2147483648.0F))
     165        i = static_cast<int32_t>(unTag(v)) >> 2;
     166        if (JSImmediate::getTag(v) == UndefinedType && i)
    186167            return false;
    187         i = static_cast<int32_t>(f);
    188168        return isNumber(v);
    189169    }
    190 
     170   
    191171    static ALWAYS_INLINE bool getTruncatedUInt32(const JSValue* v, uint32_t& i)
    192172    {
    193         float f = toFloat(v);
    194         if (!(f >= 0.0F && f < 4294967296.0F))
    195             return false;
    196         i = static_cast<uint32_t>(f);
    197         return isNumber(v);
     173        int32_t& si = reinterpret_cast<int&>(i);
     174        return getTruncatedInt32(v, si) & (si >= 0);
    198175    }
    199176};
     
    206183    static ALWAYS_INLINE JSValue* fromDouble(double d)
    207184    {
    208         DoubleUnion doubleUnion;
    209         doubleUnion.asDouble = d;
    210 
    211         // check for data loss from tagging
    212         if ((doubleUnion.asBits & TagMask) != 0)
    213             return 0;
    214 
    215         return tag(static_cast<uintptr_t>(doubleUnion.asBits), NumberType);
     185        const int64_t intVal = static_cast<int>(d);
     186       
     187        // Technically we could fit a 60 bit signed int here, however that would
     188        // required more branches when extracting int values.
     189        if ((intVal <= -(1L << 29)) | (intVal >= ((1 << 29) - 1)))
     190            return 0;
     191       
     192        // Check for data loss from conversion to int. This
     193        // will reject NaN, +/-Inf, and -0.
     194        // Happily none of these are as common as raw int values
     195        if ((intVal != d) || (signbit(d) && !intVal))
     196            return 0;
     197
     198        return tag(static_cast<uintptr_t>(intVal << 2), NumberType);
    216199    }
    217200
     
    219202    {
    220203        ASSERT(isImmediate(v));
    221 
    222         DoubleUnion doubleUnion;
    223         doubleUnion.asBits = unTag(v);
    224         return doubleUnion.asDouble;
     204        const int32_t i = static_cast<int32_t>(unTag(v) >> 2);
     205        if (JSImmediate::getTag(v) == UndefinedType && i)
     206            return std::numeric_limits<double>::quiet_NaN();
     207        return i;
    225208    }
    226209
    227210    static ALWAYS_INLINE bool getTruncatedInt32(const JSValue* v, int32_t& i)
    228211    {
    229         double d = toDouble(v);
    230         if (!(d >= -2147483648.0 && d < 2147483648.0))
     212        i = static_cast<int32_t>(unTag(v) >> 2);
     213        if (JSImmediate::getTag(v) == UndefinedType && i)
    231214            return false;
    232         i = static_cast<int32_t>(d);
    233215        return isNumber(v);
    234216    }
    235217
    236218    static ALWAYS_INLINE bool getTruncatedUInt32(const JSValue* v, uint32_t& i)
    237     {
    238         double d = toDouble(v);
    239         if (!(d >= 0.0 && d < 4294967296.0))
    240             return false;
    241         i = static_cast<uint32_t>(d);
    242         return isNumber(v);
     219    {     
     220        int& si = reinterpret_cast<int&>(i);
     221        return getTruncatedInt32(v, si) & (si >= 0);
    243222    }
    244223};
    245224
    246 ALWAYS_INLINE JSValue* JSImmediate::trueImmediate() { return tag(FPBitValues<is32bit, is64bit>::oneAsBits, BooleanType); }
    247 ALWAYS_INLINE JSValue* JSImmediate::falseImmediate() { return tag(FPBitValues<is32bit, is64bit>::zeroAsBits, BooleanType); }
    248 ALWAYS_INLINE JSValue* JSImmediate::NaNImmediate() { return tag(FPBitValues<is32bit, is64bit>::nanAsBits, NumberType); }
    249 ALWAYS_INLINE JSValue* JSImmediate::undefinedImmediate() { return tag(FPBitValues<is32bit, is64bit>::nanAsBits, UndefinedType); }
    250 ALWAYS_INLINE JSValue* JSImmediate::nullImmediate() { return tag(FPBitValues<is32bit, is64bit>::zeroAsBits, UndefinedType); }
     225ALWAYS_INLINE JSValue* JSImmediate::trueImmediate() { return tag(1 << 2, BooleanType); }
     226ALWAYS_INLINE JSValue* JSImmediate::falseImmediate() { return tag(0, BooleanType); }
     227ALWAYS_INLINE JSValue* JSImmediate::undefinedImmediate() { return tag(1 << 2, UndefinedType); }
     228ALWAYS_INLINE JSValue* JSImmediate::nullImmediate() { return tag(0, UndefinedType); }
    251229
    252230ALWAYS_INLINE bool JSImmediate::toBoolean(const JSValue* v)
    253231{
    254232    ASSERT(isImmediate(v));
    255 
    256233    uintptr_t bits = unTag(v);
    257     if ((bits << 1) == 0) // -0.0 has the sign bit set
    258         return false;
    259 
    260     return bits != FPBitValues<is32bit, is64bit>::nanAsBits;
     234    return bits != 0 & (JSImmediate::getTag(v) != UndefinedType);
    261235}
    262236
     
    273247ALWAYS_INLINE bool JSImmediate::getUInt32(const JSValue* v, uint32_t& i)
    274248{
    275     double d = toDouble(v);
    276     i = static_cast<uint32_t>(d);
    277     return isNumber(v) & (i == d);
     249    return FPBitValues<is32bit, is64bit>::getTruncatedUInt32(v, i);
    278250}
    279251
Note: See TracChangeset for help on using the changeset viewer.