Ignore:
Timestamp:
Aug 30, 2008, 11:58:07 PM (17 years ago)
Author:
Darin Adler
Message:

JavaScriptCore:

2008-08-30 Darin Adler <Darin Adler>

Reviewed by Maciej.

1.035x as fast on SunSpider overall.
1.127x as fast on SunSpider string tests.
1.910x as fast on SunSpider string-base64 test.

  • API/JSObjectRef.cpp: (JSObjectMakeFunction): Removed unneeded explicit construction of UString.
  • GNUmakefile.am: Added SmallStrings.h and SmallStrings.cpp.
  • JavaScriptCore.pri: Ditto.
  • JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj: Ditto.
  • JavaScriptCore.xcodeproj/project.pbxproj: Ditto.
  • JavaScriptCoreSources.bkl: Ditto.
  • VM/Machine.cpp: (KJS::jsAddSlowCase): Changed to use a code path that doesn't involve a UString constructor. This avoids an extra jump caused by the "in charge" vs. "not in charge" constructors. (KJS::jsAdd): Ditto. (KJS::jsTypeStringForValue): Adopted jsNontrivialString.
  • kjs/ArrayPrototype.cpp: (KJS::arrayProtoFuncToString): Adopted jsEmptyString. (KJS::arrayProtoFuncToLocaleString): Ditto. (KJS::arrayProtoFuncJoin): Ditto.
  • kjs/BooleanPrototype.cpp: (KJS::booleanProtoFuncToString): Adopted jsNontrivialString.
  • kjs/DateConstructor.cpp: (KJS::callDate): Ditto.
  • kjs/DatePrototype.cpp: (KJS::formatLocaleDate): Adopted jsEmptyString and jsNontrivialString. (KJS::dateProtoFuncToString): Ditto. (KJS::dateProtoFuncToUTCString): Ditto. (KJS::dateProtoFuncToDateString): Ditto. (KJS::dateProtoFuncToTimeString): Ditto. (KJS::dateProtoFuncToLocaleString): Ditto. (KJS::dateProtoFuncToLocaleDateString): Ditto. (KJS::dateProtoFuncToLocaleTimeString): Ditto. (KJS::dateProtoFuncToGMTString): Ditto.
  • kjs/ErrorPrototype.cpp: (KJS::ErrorPrototype::ErrorPrototype): Ditto. (KJS::errorProtoFuncToString): Ditto.
  • kjs/JSGlobalData.h: Added SmallStrings.
  • kjs/JSString.cpp: (KJS::jsString): Eliminated the overload that takes a const char*. Added code to use SmallStrings to get strings of small sizes rather than creating a new JSString every time. (KJS::jsSubstring): Added. Used when creating a string from a substring to avoid creating a JSString in cases where the substring will end up empty or as one character. (KJS::jsOwnedString): Added the same code as in jsString.
  • kjs/JSString.h: Added new functions jsEmptyString, jsSingleCharacterString, jsSingleCharacterSubstring, jsSubstring, and jsNontrivialString for various cases where we want to create JSString, and want special handling for small strings. (KJS::JSString::JSString): Added an overload that takes a PassRefPtr of a UString::Rep so you don't have to construct a UString; PassRefPtr can be more efficient. (KJS::jsEmptyString): Added. (KJS::jsSingleCharacterString): Added. (KJS::jsSingleCharacterSubstring): Added. (KJS::jsNontrivialString): Added. (KJS::JSString::getIndex): Adopted jsSingleCharacterSubstring. (KJS::JSString::getStringPropertySlot): Ditto.
  • kjs/NumberPrototype.cpp: (KJS::numberProtoFuncToFixed): Adopted jsNontrivialString. (KJS::numberProtoFuncToExponential): Ditto. (KJS::numberProtoFuncToPrecision): Ditto.
  • kjs/ObjectPrototype.cpp: (KJS::objectProtoFuncToLocaleString): Adopted toThisJSString. (KJS::objectProtoFuncToString): Adopted jsNontrivialString.
  • kjs/RegExpConstructor.cpp: Separated the lastInput value that's used with the lastOvector to return matches from the input value that can be changed via JavaScript. They will be equal in many cases, but not all. (KJS::RegExpConstructor::performMatch): Set input. (KJS::RegExpMatchesArray::RegExpMatchesArray): Ditto. (KJS::RegExpMatchesArray::fillArrayInstance): Adopted jsSubstring. Also, use input rather than lastInput in the appropriate place. (KJS::RegExpConstructor::getBackref): Adopted jsSubstring and jsEmptyString. Added code to handle the case where there is no backref -- before this depended on range checking in UString::substr which is not present in jsSubstring. (KJS::RegExpConstructor::getLastParen): Ditto. (KJS::RegExpConstructor::getLeftContext): Ditto. (KJS::RegExpConstructor::getRightContext): Ditto. (KJS::RegExpConstructor::getValueProperty): Use input rather than lastInput. Also adopt jsEmptyString. (KJS::RegExpConstructor::putValueProperty): Ditto. (KJS::RegExpConstructor::input): Ditto.
  • kjs/RegExpPrototype.cpp: (KJS::regExpProtoFuncToString): Adopt jsNonTrivialString. Also changed to use UString::append to append single characters rather than using += and a C-style string.
  • kjs/SmallStrings.cpp: Added. (KJS::SmallStringsStorage::SmallStringsStorage): Construct the buffer and UString::Rep for all 256 single-character strings for the U+0000 through U+00FF. This covers all the values used in the base64 test as well as most values seen elsewhere on the web as well. It's possible that later we might fix this to only work for U+0000 through U+007F but the others are used quite a bit in the current version of the base64 test. (KJS::SmallStringsStorage::~SmallStringsStorage): Free memory. (KJS::SmallStrings::SmallStrings): Create a set of small strings, initially not created; created later when they are used. (KJS::SmallStrings::~SmallStrings): Deallocate. Not left compiler generated because the SmallStringsStorage class's destructor needs to be visible. (KJS::SmallStrings::mark): Mark all the strings. (KJS::SmallStrings::createEmptyString): Create a cell for the empty string. Called only the first time. (KJS::SmallStrings::createSingleCharacterString): Create a cell for one of the single-character strings. Called only the first time.
  • kjs/SmallStrings.h: Added.
  • kjs/StringConstructor.cpp: (KJS::stringFromCharCodeSlowCase): Factored out of strinFromCharCode. Only used for cases where the caller does not pass exactly one argument. (KJS::stringFromCharCode): Adopted jsSingleCharacterString. (KJS::callStringConstructor): Adopted jsEmptyString.
  • kjs/StringObject.cpp: (KJS::StringObject::StringObject): Adopted jsEmptyString.
  • kjs/StringPrototype.cpp: (KJS::stringProtoFuncReplace): Adopted jsSubstring. (KJS::stringProtoFuncCharAt): Adopted jsEmptyString and jsSingleCharacterSubstring and also added a special case when the index is an immediate number to avoid conversion to and from floating point, since that's the common case. (KJS::stringProtoFuncCharCodeAt): Ditto. (KJS::stringProtoFuncMatch): Adopted jsSubstring and jsEmptyString. (KJS::stringProtoFuncSlice): Adopted jsSubstring and jsSingleCharacterSubstring. Also got rid of some unneeded locals and removed unneeded code to set the length property of the array, since it is automatically updated as values are added to the array. (KJS::stringProtoFuncSplit): Adopted jsEmptyString. (KJS::stringProtoFuncSubstr): Adopted jsSubstring. (KJS::stringProtoFuncSubstring): Ditto.
  • kjs/collector.cpp: (KJS::Heap::collect): Added a call to mark SmallStrings.
  • kjs/ustring.cpp: (KJS::UString::expandedSize): Made this a static member function since it doesn't need to look at any data members. (KJS::UString::expandCapacity): Use a non-inline function, makeNull, to set the rep to null in failure cases. This avoids adding a PIC branch for the normal case when there is no failure. (KJS::UString::expandPreCapacity): Ditto. (KJS::UString::UString): Ditto. (KJS::concatenate): Refactored the concatenation constructor into this separate function. Calling the concatenation constructor was leading to an extra branch because of the in-charge vs. not-in-charge versions not both being inlined, and this was showing up as nearly 1% on Shark. Also added a special case for when the second string is a single character, since it's a common idiom to build up a string that way and we can do things much more quickly, without involving memcpy for example. Also adopted the non-inline function, nullRep, for the same reason given for makeNull above. (KJS::UString::append): Adopted makeNull for failure cases. (KJS::UString::operator=): Ditto. (KJS::UString::toDouble): Added a special case for converting single character strings to numbers. We're doing this a ton of times while running the base64 test. (KJS::operator==): Added special cases so we can compare single-character strings without calling memcmp. Later we might want to special case other short lengths similarly. (KJS::UString::makeNull): Added. (KJS::UString::nullRep): Added.
  • kjs/ustring.h: Added declarations for the nullRep and makeNull. Changed expandedSize to be a static member function. Added a declaration of the concatenate function. Removed the concatenation constructor. Rewrote operator+ to use the concatenate function.

WebCore:

2008-08-30 Darin Adler <Darin Adler>

Reviewed by Maciej.

  • bindings/js/JSDOMWindowBase.cpp: (WebCore::windowProtoFuncAToB): Adopted jsEmptyString. (WebCore::windowProtoFuncBToA): Ditto.
  • bindings/js/JSEventListener.cpp: (WebCore::JSLazyEventListener::eventParameterName): Adopted jsNontrivialString.
  • bindings/js/JSSVGLazyEventListener.cpp: (WebCore::JSSVGLazyEventListener::eventParameterName): Ditto.

LayoutTests:

2008-08-30 Darin Adler <Darin Adler>

Reviewed by Maciej.

  • updated incorrect results that reflected a bug in the RegExp object
  • fast/js/regexp-caching-expected.txt: Updated results to correctly show that $1 through $9, lastMatch, lastParen, leftContext, and rightContext are left alone both when a program changes the value of RegExp.input and when it performs an unsuccessful match. The new results match Gecko behavior (I tested both Firefox 2 and 3).
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/kjs/ustring.cpp

    r35691 r36006  
    395395
    396396// put these early so they can be inlined
    397 inline size_t UString::expandedSize(size_t size, size_t otherSize) const
     397inline size_t UString::expandedSize(size_t size, size_t otherSize)
    398398{
    399399    // Do the size calculation in two parts, returning overflowIndicator if
     
    432432        if (!r->buf) {
    433433            r->buf = oldBuf;
    434             m_rep = &Rep::null;
     434            makeNull();
    435435            return;
    436436        }
     
    455455        UChar* newBuf = allocChars(newCapacity);
    456456        if (!newBuf) {
    457             m_rep = &Rep::null;
     457            makeNull();
    458458            return;
    459459        }
     
    485485    UChar* d = allocChars(length);
    486486    if (!d)
    487         m_rep = &Rep::null;
     487        makeNull();
    488488    else {
    489489        for (size_t i = 0; i < length; i++)
     
    519519}
    520520
    521 
    522 UString::UString(const UString& a, const UString& b)
    523 {
    524     a.rep()->checkConsistency();
    525     b.rep()->checkConsistency();
    526 
    527     int aSize = a.size();
    528     int aOffset = a.m_rep->offset;
    529     int bSize = b.size();
    530     int bOffset = b.m_rep->offset;
     521PassRefPtr<UString::Rep> concatenate(UString::Rep* a, UString::Rep* b)
     522{
     523    a->checkConsistency();
     524    b->checkConsistency();
     525
     526    int aSize = a->size();
     527    int aOffset = a->offset;
     528    int bSize = b->size();
     529    int bOffset = b->offset;
    531530    int length = aSize + bSize;
    532531
    533532    // possible cases:
    534533
    535     if (aSize == 0) {
    536         // a is empty
    537         m_rep = b.m_rep;
    538     } else if (bSize == 0) {
    539         // b is empty
    540         m_rep = a.m_rep;
    541     } else if (aOffset + aSize == a.usedCapacity() && aSize >= minShareSize && 4 * aSize >= bSize &&
    542                (-bOffset != b.usedPreCapacity() || aSize >= bSize)) {
     534    // a is empty
     535    if (aSize == 0)
     536        return b;
     537    // b is empty
     538    if (bSize == 0)
     539        return a;
     540
     541    if (bSize == 1 && aOffset + aSize == a->baseString->usedCapacity && aOffset + length <= a->baseString->capacity) {
     542        // b is a single character (common fast case)
     543        a->baseString->usedCapacity = aOffset + length;
     544        a->data()[aSize] = b->data()[0];
     545        return UString::Rep::create(a, 0, length);
     546    }
     547
     548    if (aOffset + aSize == a->baseString->usedCapacity && aSize >= minShareSize && 4 * aSize >= bSize &&
     549               (-bOffset != b->baseString->usedPreCapacity || aSize >= bSize)) {
    543550        // - a reaches the end of its buffer so it qualifies for shared append
    544551        // - also, it's at least a quarter the length of b - appending to a much shorter
     
    547554        UString x(a);
    548555        x.expandCapacity(aOffset + length);
    549         if (a.data() && x.data()) {
    550             memcpy(const_cast<UChar*>(a.data() + aSize), b.data(), bSize * sizeof(UChar));
    551             m_rep = Rep::create(a.m_rep, 0, length);
    552         } else
    553             m_rep = &Rep::null;
    554     } else if (-bOffset == b.usedPreCapacity() && bSize >= minShareSize  && 4 * bSize >= aSize) {
     556        if (!a->data() || !x.data())
     557            return 0;
     558        memcpy(a->data() + aSize, b->data(), bSize * sizeof(UChar));
     559        PassRefPtr<UString::Rep> result = UString::Rep::create(a, 0, length);
     560
     561        a->checkConsistency();
     562        b->checkConsistency();
     563        result->checkConsistency();
     564
     565        return result;
     566    }
     567
     568    if (-bOffset == b->baseString->usedPreCapacity && bSize >= minShareSize  && 4 * bSize >= aSize) {
    555569        // - b reaches the beginning of its buffer so it qualifies for shared prepend
    556570        // - also, it's at least a quarter the length of a - prepending to a much shorter
     
    558572        UString y(b);
    559573        y.expandPreCapacity(-bOffset + aSize);
    560         if (b.data() && y.data()) {
    561             memcpy(const_cast<UChar *>(b.data() - aSize), a.data(), aSize * sizeof(UChar));
    562             m_rep = Rep::create(b.m_rep, -aSize, length);
    563         } else
    564             m_rep = &Rep::null;
    565     } else {
    566         // a does not qualify for append, and b does not qualify for prepend, gotta make a whole new string
    567         size_t newCapacity = expandedSize(length, 0);
    568         UChar* d = allocChars(newCapacity);
    569         if (!d)
    570             m_rep = &Rep::null;
    571         else {
    572             memcpy(d, a.data(), aSize * sizeof(UChar));
    573             memcpy(d + aSize, b.data(), bSize * sizeof(UChar));
    574             m_rep = Rep::create(d, length);
    575             m_rep->capacity = newCapacity;
    576         }
    577     }
    578     a.rep()->checkConsistency();
    579     b.rep()->checkConsistency();
    580     m_rep->checkConsistency();
     574        if (!b->data() || !y.data())
     575            return 0;
     576        memcpy(b->data() - aSize, a->data(), aSize * sizeof(UChar));
     577        PassRefPtr<UString::Rep> result = UString::Rep::create(b, -aSize, length);
     578
     579        a->checkConsistency();
     580        b->checkConsistency();
     581        result->checkConsistency();
     582
     583        return result;
     584    }
     585
     586    // a does not qualify for append, and b does not qualify for prepend, gotta make a whole new string
     587    size_t newCapacity = UString::expandedSize(length, 0);
     588    UChar* d = allocChars(newCapacity);
     589    if (!d)
     590        return 0;
     591    memcpy(d, a->data(), aSize * sizeof(UChar));
     592    memcpy(d + aSize, b->data(), bSize * sizeof(UChar));
     593    PassRefPtr<UString::Rep> result = UString::Rep::create(d, length);
     594    result->capacity = newCapacity;
     595
     596    a->checkConsistency();
     597    b->checkConsistency();
     598    result->checkConsistency();
     599
     600    return result;
    581601}
    582602
     
    807827        UChar* d = allocChars(newCapacity);
    808828        if (!d)
    809             m_rep = &Rep::null;
     829            makeNull();
    810830        else {
    811831            memcpy(d, data(), thisSize * sizeof(UChar));
     
    856876        UChar* d = allocChars(newCapacity);
    857877        if (!d)
    858             m_rep = &Rep::null;
     878            makeNull();
    859879        else {
    860880            memcpy(d, data(), thisSize * sizeof(UChar));
     
    909929        UChar* d = allocChars(newCapacity);
    910930        if (!d)
    911             m_rep = &Rep::null;
     931            makeNull();
    912932        else {
    913933            memcpy(d, data(), thisSize * sizeof(UChar));
     
    937957        UChar* d = allocChars(newCapacity);
    938958        if (!d)
    939             m_rep = &Rep::null;
     959            makeNull();
    940960        else {
    941961            d[0] = c;
     
    965985        UChar* d = allocChars(newCapacity);
    966986        if (!d)
    967             m_rep = &Rep::null;
     987            makeNull();
    968988        else {
    969989            memcpy(d, data(), length * sizeof(UChar));
     
    10431063        d = allocChars(l);
    10441064        if (!d) {
    1045             m_rep = &Rep::null;
     1065            makeNull();
    10461066            return *this;
    10471067        }
     
    10761096double UString::toDouble(bool tolerateTrailingJunk, bool tolerateEmptyString) const
    10771097{
    1078     double d;
     1098    if (size() == 1) {
     1099        UChar c = data()[0];
     1100        if (isASCIIDigit(c))
     1101            return c - '0';
     1102        if (isASCIISpace(c) && tolerateEmptyString)
     1103            return 0;
     1104        return NaN;
     1105    }
    10791106
    10801107    // FIXME: If tolerateTrailingJunk is true, then we want to tolerate non-8-bit junk
     
    10921119    if (*c == '\0')
    10931120        return tolerateEmptyString ? 0.0 : NaN;
     1121
     1122    double d;
    10941123
    10951124    // hex number ?
     
    13351364bool operator==(const UString& s1, const UString& s2)
    13361365{
    1337     if (s1.m_rep->len != s2.m_rep->len)
    1338         return false;
    1339 
    1340     return (memcmp(s1.m_rep->data(), s2.m_rep->data(), s1.m_rep->len * sizeof(UChar)) == 0);
     1366    int size = s1.size();
     1367    switch (size) {
     1368        case 0:
     1369            return !s2.size();
     1370        case 1:
     1371            return s2.size() == 1 && s1.data()[0] == s2.data()[0];
     1372        default:
     1373            return s2.size() == size && memcmp(s1.data(), s2.data(), size * sizeof(UChar)) == 0;
     1374    }
    13411375}
    13421376
     
    14491483}
    14501484
     1485// For use in error handling code paths -- having this not be inlined helps avoid PIC branches to fetch the global on Mac OS X.
     1486NEVER_INLINE void UString::makeNull()
     1487{
     1488    m_rep = &Rep::null;
     1489}
     1490
     1491// For use in error handling code paths -- having this not be inlined helps avoid PIC branches to fetch the global on Mac OS X.
     1492NEVER_INLINE UString::Rep* UString::nullRep()
     1493{
     1494    return &Rep::null;
     1495}
     1496
    14511497} // namespace KJS
Note: See TracChangeset for help on using the changeset viewer.