Changeset 241230 in webkit


Ignore:
Timestamp:
Feb 8, 2019 7:56:32 PM (5 years ago)
Author:
keith_miller@apple.com
Message:

We should only make rope strings when concatenating strings long enough.
https://bugs.webkit.org/show_bug.cgi?id=194465

Reviewed by Saam Barati.

This patch stops us from allocating a rope string if the resulting
rope would be smaller than the size of the JSRopeString object we
would need to allocate.

This patch also adds paths so that we don't unnecessarily allocate
JSString cells for primitives we are going to concatenate with a
string anyway.

  • dfg/DFGOperations.cpp:
  • runtime/CommonSlowPaths.cpp:

(JSC::SLOW_PATH_DECL):

  • runtime/JSString.h:
  • runtime/Operations.cpp:

(JSC::jsAddSlowCase):

  • runtime/Operations.h:

(JSC::jsString):
(JSC::jsAdd):

Location:
trunk/Source/JavaScriptCore
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r241228 r241230  
     12019-02-08  Keith Miller  <keith_miller@apple.com>
     2
     3        We should only make rope strings when concatenating strings long enough.
     4        https://bugs.webkit.org/show_bug.cgi?id=194465
     5
     6        Reviewed by Saam Barati.
     7
     8        This patch stops us from allocating a rope string if the resulting
     9        rope would be smaller than the size of the JSRopeString object we
     10        would need to allocate.
     11
     12        This patch also adds paths so that we don't unnecessarily allocate
     13        JSString cells for primitives we are going to concatenate with a
     14        string anyway.
     15
     16        * dfg/DFGOperations.cpp:
     17        * runtime/CommonSlowPaths.cpp:
     18        (JSC::SLOW_PATH_DECL):
     19        * runtime/JSString.h:
     20        * runtime/Operations.cpp:
     21        (JSC::jsAddSlowCase):
     22        * runtime/Operations.h:
     23        (JSC::jsString):
     24        (JSC::jsAdd):
     25
    1262019-02-08  Saam barati  <sbarati@apple.com>
    227
  • trunk/Source/JavaScriptCore/dfg/DFGOperations.cpp

    r241222 r241230  
    504504    JSValue op2 = JSValue::decode(encodedOp2);
    505505   
    506     ASSERT(!op1.isNumber() || !op2.isNumber());
    507    
    508     if (op1.isString() && !op2.isObject())
    509         return JSValue::encode(jsString(exec, asString(op1), op2.toString(exec)));
    510 
    511     return JSValue::encode(jsAddSlowCase(exec, op1, op2));
     506    return JSValue::encode(jsAddNonNumber(exec, op1, op2));
    512507}
    513508
  • trunk/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp

    r240254 r241230  
    518518    JSValue v1 = GET_C(bytecode.m_lhs).jsValue();
    519519    JSValue v2 = GET_C(bytecode.m_rhs).jsValue();
    520     JSValue result;
    521520
    522521    ArithProfile& arithProfile = *exec->codeBlock()->arithProfileForPC(pc);
    523522    arithProfile.observeLHSAndRHS(v1, v2);
    524523
    525     if (v1.isString() && !v2.isObject()) {
    526         JSString* v2String = v2.toString(exec);
    527         if (LIKELY(!throwScope.exception()))
    528             result = jsString(exec, asString(v1), v2String);
    529     } else if (v1.isNumber() && v2.isNumber())
    530         result = jsNumber(v1.asNumber() + v2.asNumber());
    531     else
    532         result = jsAddSlowCase(exec, v1, v2);
     524    JSValue result = jsAdd(exec, v1, v2);
    533525
    534526    RETURN_WITH_PROFILING(result, {
  • trunk/Source/JavaScriptCore/runtime/JSString.h

    r240965 r241230  
    487487
    488488    friend JSString* jsString(ExecState*, JSString*, JSString*);
     489    friend JSString* jsString(ExecState*, const String&, JSString*);
     490    friend JSString* jsString(ExecState*, JSString*, const String&);
    489491    friend JSString* jsString(ExecState*, JSString*, JSString*, JSString*);
    490492    friend JSString* jsString(ExecState*, const String&, const String&, const String&);
  • trunk/Source/JavaScriptCore/runtime/Operations.cpp

    r238425 r241230  
    5353
    5454    if (p1.isString()) {
    55         JSString* p2String = p2.toString(callFrame);
     55        if (p2.isCell()) {
     56            JSString* p2String = p2.toString(callFrame);
     57            RETURN_IF_EXCEPTION(scope, { });
     58            RELEASE_AND_RETURN(scope, jsString(callFrame, asString(p1), p2String));
     59        }
     60        String p2String = p2.toWTFString(callFrame);
    5661        RETURN_IF_EXCEPTION(scope, { });
    5762        RELEASE_AND_RETURN(scope, jsString(callFrame, asString(p1), p2String));
     
    5964
    6065    if (p2.isString()) {
    61         JSString* p1String = p1.toString(callFrame);
     66        if (p2.isCell()) {
     67            JSString* p1String = p1.toString(callFrame);
     68            RETURN_IF_EXCEPTION(scope, { });
     69            RELEASE_AND_RETURN(scope, jsString(callFrame, p1String, asString(p2)));
     70        }
     71        String p1String = p1.toWTFString(callFrame);
    6272        RETURN_IF_EXCEPTION(scope, { });
    6373        RELEASE_AND_RETURN(scope, jsString(callFrame, p1String, asString(p2)));
  • trunk/Source/JavaScriptCore/runtime/Operations.h

    r238425 r241230  
    3838size_t normalizePrototypeChain(CallFrame*, JSCell*, bool& sawPolyProto);
    3939
     40// This is a lower bound on the extra memory we expect to write if we were to allocate a rope instead of copying.
     41// Note, it doesn't differentiate between 8 and 16-bit strings because 16-bit strings are relatively rare.
     42constexpr unsigned minRopeStringLength = sizeof(JSRopeString);
     43
     44ALWAYS_INLINE JSString* jsString(ExecState* exec, const String& u1, JSString* s2)
     45{
     46    VM& vm = exec->vm();
     47    auto scope = DECLARE_THROW_SCOPE(vm);
     48
     49    unsigned length1 = u1.length();
     50    if (!length1)
     51        return s2;
     52    unsigned length2 = s2->length();
     53    if (!length2)
     54        return jsString(&vm, u1);
     55
     56    if (length1 + length2 >= minRopeStringLength)
     57        return jsString(exec, jsString(&vm, u1), s2);
     58
     59    const String& u2 = s2->value(exec);
     60    RETURN_IF_EXCEPTION(scope, { });
     61    return JSString::create(vm, makeString(u1, u2).releaseImpl().releaseNonNull());
     62}
     63
     64ALWAYS_INLINE JSString* jsString(ExecState* exec, JSString* s1, const String& u2)
     65{
     66    VM& vm = exec->vm();
     67    auto scope = DECLARE_THROW_SCOPE(vm);
     68
     69    unsigned length1 = s1->length();
     70    if (!length1)
     71        return jsString(&vm, u2);
     72    unsigned length2 = u2.length();
     73    if (!length2)
     74        return s1;
     75
     76    if (length1 + length2 >= minRopeStringLength)
     77        return jsString(exec, s1, jsString(&vm, u2));
     78
     79    const String& u1 = s1->value(exec);
     80    RETURN_IF_EXCEPTION(scope, { });
     81    return JSString::create(vm, makeString(u1, u2).releaseImpl().releaseNonNull());
     82}
     83
    4084ALWAYS_INLINE JSString* jsString(ExecState* exec, JSString* s1, JSString* s2)
    4185{
     
    5599    }
    56100
    57     return JSRopeString::create(vm, s1, s2);
     101    if (length1 + length2 >= minRopeStringLength)
     102        return JSRopeString::create(vm, s1, s2);
     103
     104    const String& u1 = s1->value(exec);
     105    RETURN_IF_EXCEPTION(scope, { });
     106    const String& u2 = s2->value(exec);
     107    RETURN_IF_EXCEPTION(scope, { });
     108    return JSString::create(vm, makeString(u1, u2).releaseImpl().releaseNonNull());
    58109}
    59110
     
    80131        return nullptr;
    81132    }
    82     return JSRopeString::create(vm, s1, s2, s3);
     133
     134    if (length1 + length2 + length3 >= minRopeStringLength)
     135        return JSRopeString::create(vm, s1, s2, s3);
     136
     137    const String& u1 = s1->value(exec);
     138    RETURN_IF_EXCEPTION(scope, { });
     139    const String& u2 = s2->value(exec);
     140    RETURN_IF_EXCEPTION(scope, { });
     141    const String& u3 = s3->value(exec);
     142    RETURN_IF_EXCEPTION(scope, { });
     143    return JSString::create(vm, makeString(u1, u2, u3).releaseImpl().releaseNonNull());
    83144}
    84145
     
    110171    }
    111172
    112     return JSRopeString::create(*vm, jsString(vm, u1), jsString(vm, u2), jsString(vm, u3));
     173    if (length1 + length2 + length3 >= minRopeStringLength)
     174        return JSRopeString::create(*vm, jsString(vm, u1), jsString(vm, u2), jsString(vm, u3));
     175
     176    return JSString::create(*vm, makeString(u1, u2, u3).releaseImpl().releaseNonNull());
    113177}
    114178
     
    326390//    4000    Add case: 3 5
    327391
     392
     393ALWAYS_INLINE JSValue jsAddNonNumber(CallFrame* callFrame, JSValue v1, JSValue v2)
     394{
     395    VM& vm = callFrame->vm();
     396    auto scope = DECLARE_THROW_SCOPE(vm);
     397    ASSERT(!v1.isNumber() || !v2.isNumber());
     398
     399    if (v1.isString() && !v2.isObject()) {
     400        if (v2.isString())
     401            RELEASE_AND_RETURN(scope, jsString(callFrame, asString(v1), asString(v2)));
     402        String s2 = v2.toWTFString(callFrame);
     403        RETURN_IF_EXCEPTION(scope, { });
     404        RELEASE_AND_RETURN(scope, jsString(callFrame, asString(v1), s2));
     405    }
     406
     407    // All other cases are pretty uncommon
     408    RELEASE_AND_RETURN(scope, jsAddSlowCase(callFrame, v1, v2));
     409}
     410
    328411ALWAYS_INLINE JSValue jsAdd(CallFrame* callFrame, JSValue v1, JSValue v2)
    329412{
     
    331414        return jsNumber(v1.asNumber() + v2.asNumber());
    332415       
    333     if (v1.isString() && !v2.isObject())
    334         return jsString(callFrame, asString(v1), v2.toString(callFrame));
    335 
    336     // All other cases are pretty uncommon
    337     return jsAddSlowCase(callFrame, v1, v2);
     416    return jsAddNonNumber(callFrame, v1, v2);
    338417}
    339418
Note: See TracChangeset for help on using the changeset viewer.