Changeset 242397 in webkit


Ignore:
Timestamp:
Mar 4, 2019 3:27:43 PM (5 years ago)
Author:
ysuzuki@apple.com
Message:

[JSC] Store bits for JSRopeString in 3 stores
https://bugs.webkit.org/show_bug.cgi?id=195234

Reviewed by Saam Barati.

JSTests:

  • stress/null-rope-and-collectors.js: Added.

Source/JavaScriptCore:

This patch cleans up the initialization of JSRopeString fields in DFG and FTL.
Previously, we store some part of data separately. Instead, this patch calculates
the data first by bit operations and store calculated data with fewer stores.

This patch also cleans up is8Bit and isSubstring flags. We put them in lower bits
of the first fiber instead of the upper 16 bits. Since we only have 3 bit flags, (isRope, is8Bit, isSubstring),
we can put them into the lower 3 bits, they are always empty due to alignment.

  • bytecode/AccessCase.cpp:

(JSC::AccessCase::generateImpl): A bit clean up of StringLength IC to give a chance of unnecessary mov removal.

  • dfg/DFGSpeculativeJIT.cpp:

(JSC::DFG::SpeculativeJIT::canBeRope):
(JSC::DFG::SpeculativeJIT::compileGetArrayLength):
(JSC::DFG::SpeculativeJIT::compileMakeRope):

  • dfg/DFGSpeculativeJIT.h:
  • ftl/FTLAbstractHeapRepository.cpp:

(JSC::FTL::AbstractHeapRepository::AbstractHeapRepository):

  • ftl/FTLAbstractHeapRepository.h:
  • ftl/FTLLowerDFGToB3.cpp:

(JSC::FTL::DFG::LowerDFGToB3::compileMakeRope):
(JSC::FTL::DFG::LowerDFGToB3::isRopeString):
(JSC::FTL::DFG::LowerDFGToB3::isNotRopeString):

  • runtime/JSString.cpp:

(JSC::JSString::visitChildren):

  • runtime/JSString.h:

(JSC::JSString::is8Bit const):
(JSC::JSString::isSubstring const):

  • tools/JSDollarVM.cpp:

(JSC::functionCreateNullRopeString):
(JSC::JSDollarVM::finishCreation):

Location:
trunk
Files:
1 added
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r242280 r242397  
     12019-03-04  Yusuke Suzuki  <ysuzuki@apple.com>
     2
     3        [JSC] Store bits for JSRopeString in 3 stores
     4        https://bugs.webkit.org/show_bug.cgi?id=195234
     5
     6        Reviewed by Saam Barati.
     7
     8        * stress/null-rope-and-collectors.js: Added.
     9
    1102019-03-01  Dominik Infuehr  <dinfuehr@igalia.com>
    211
  • trunk/Source/JavaScriptCore/ChangeLog

    r242386 r242397  
     12019-03-04  Yusuke Suzuki  <ysuzuki@apple.com>
     2
     3        [JSC] Store bits for JSRopeString in 3 stores
     4        https://bugs.webkit.org/show_bug.cgi?id=195234
     5
     6        Reviewed by Saam Barati.
     7
     8        This patch cleans up the initialization of JSRopeString fields in DFG and FTL.
     9        Previously, we store some part of data separately. Instead, this patch calculates
     10        the data first by bit operations and store calculated data with fewer stores.
     11
     12        This patch also cleans up is8Bit and isSubstring flags. We put them in lower bits
     13        of the first fiber instead of the upper 16 bits. Since we only have 3 bit flags, (isRope, is8Bit, isSubstring),
     14        we can put them into the lower 3 bits, they are always empty due to alignment.
     15
     16        * bytecode/AccessCase.cpp:
     17        (JSC::AccessCase::generateImpl): A bit clean up of StringLength IC to give a chance of unnecessary mov removal.
     18        * dfg/DFGSpeculativeJIT.cpp:
     19        (JSC::DFG::SpeculativeJIT::canBeRope):
     20        (JSC::DFG::SpeculativeJIT::compileGetArrayLength):
     21        (JSC::DFG::SpeculativeJIT::compileMakeRope):
     22        * dfg/DFGSpeculativeJIT.h:
     23        * ftl/FTLAbstractHeapRepository.cpp:
     24        (JSC::FTL::AbstractHeapRepository::AbstractHeapRepository):
     25        * ftl/FTLAbstractHeapRepository.h:
     26        * ftl/FTLLowerDFGToB3.cpp:
     27        (JSC::FTL::DFG::LowerDFGToB3::compileMakeRope):
     28        (JSC::FTL::DFG::LowerDFGToB3::isRopeString):
     29        (JSC::FTL::DFG::LowerDFGToB3::isNotRopeString):
     30        * runtime/JSString.cpp:
     31        (JSC::JSString::visitChildren):
     32        * runtime/JSString.h:
     33        (JSC::JSString::is8Bit const):
     34        (JSC::JSString::isSubstring const):
     35        * tools/JSDollarVM.cpp:
     36        (JSC::functionCreateNullRopeString):
     37        (JSC::JSDollarVM::finishCreation):
     38
    1392019-03-04  Joseph Pecoraro  <pecoraro@apple.com>
    240
  • trunk/Source/JavaScriptCore/bytecode/AccessCase.cpp

    r242252 r242397  
    12161216        jit.loadPtr(CCallHelpers::Address(baseGPR, JSString::offsetOfValue()), scratchGPR);
    12171217        auto isRope = jit.branchIfRopeStringImpl(scratchGPR);
    1218         jit.load32(CCallHelpers::Address(scratchGPR, StringImpl::lengthMemoryOffset()), scratchGPR);
     1218        jit.load32(CCallHelpers::Address(scratchGPR, StringImpl::lengthMemoryOffset()), valueRegs.payloadGPR());
    12191219        auto done = jit.jump();
    12201220
    12211221        isRope.link(&jit);
    1222         jit.load32(CCallHelpers::Address(baseGPR, JSRopeString::offsetOfLength()), scratchGPR);
     1222        jit.load32(CCallHelpers::Address(baseGPR, JSRopeString::offsetOfLength()), valueRegs.payloadGPR());
    12231223
    12241224        done.link(&jit);
    1225         jit.boxInt32(scratchGPR, valueRegs);
     1225        jit.boxInt32(valueRegs.payloadGPR(), valueRegs);
    12261226        state.succeed();
    12271227        return;
  • trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp

    r242286 r242397  
    68986898}
    68996899
     6900bool SpeculativeJIT::canBeRope(Edge& edge)
     6901{
     6902    if (m_state.forNode(edge).isType(SpecStringIdent))
     6903        return false;
     6904    // If this value is LazyValue, it will be converted to JSString, and the result must be non-rope string.
     6905    String string = edge->tryGetString(m_graph);
     6906    if (!string.isNull())
     6907        return false;
     6908    return true;
     6909}
     6910
    69006911void SpeculativeJIT::compileGetArrayLength(Node* node)
    69016912{
     
    69356946        GPRReg tempGPR = temp.gpr();
    69366947
     6948        bool needsRopeCase = canBeRope(node->child1());
     6949
    69376950        m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSString::offsetOfValue()), tempGPR);
    6938         auto isRope = m_jit.branchIfRopeStringImpl(tempGPR);
     6951        CCallHelpers::Jump isRope;
     6952        if (needsRopeCase)
     6953            isRope = m_jit.branchIfRopeStringImpl(tempGPR);
    69396954        m_jit.load32(MacroAssembler::Address(tempGPR, StringImpl::lengthMemoryOffset()), resultGPR);
    6940         auto done = m_jit.jump();
    6941 
    6942         isRope.link(&m_jit);
    6943         m_jit.load32(CCallHelpers::Address(baseGPR, JSRopeString::offsetOfLength()), resultGPR);
    6944 
    6945         done.link(&m_jit);
     6955        if (needsRopeCase) {
     6956            auto done = m_jit.jump();
     6957
     6958            isRope.link(&m_jit);
     6959            m_jit.load32(CCallHelpers::Address(baseGPR, JSRopeString::offsetOfLength()), resultGPR);
     6960
     6961            done.link(&m_jit);
     6962        }
    69466963        int32Result(resultGPR, node);
    69476964        break;
     
    1342613443    emitAllocateJSCell(resultGPR, JITAllocator::constant(allocatorValue), allocatorGPR, TrustedImmPtr(m_jit.graph().registerStructure(m_jit.vm()->stringStructure.get())), scratchGPR, slowPath);
    1342713444
    13428     m_jit.orPtr(TrustedImm32(JSString::isRopeInPointer), opGPRs[0], allocatorGPR);
    13429     m_jit.storePtr(allocatorGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber0()));
    13430 
    13431     m_jit.move(opGPRs[1], scratchGPR);
    13432     m_jit.store32(scratchGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber1Lower()));
    13433     m_jit.rshiftPtr(TrustedImm32(32), scratchGPR);
    13434     m_jit.store16(scratchGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber1Upper()));
    13435 
    13436     if (numOpGPRs == 3) {
    13437         m_jit.move(opGPRs[2], scratchGPR);
    13438         m_jit.store32(scratchGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber2Lower()));
    13439         m_jit.rshiftPtr(TrustedImm32(32), scratchGPR);
    13440         m_jit.store16(scratchGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber2Upper()));
    13441     } else {
    13442         m_jit.storeZero32(CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber2Lower()));
    13443         m_jit.storeZero16(CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber2Upper()));
    13444     }
     13445    // This puts nullptr for the first fiber. It makes visitChildren safe even if this JSRopeString is discarded due to the speculation failure in the following path.
     13446    m_jit.storePtr(TrustedImmPtr(JSString::isRopeInPointer), CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber0()));
    1344513447
    1344613448    {
     
    1344913451            m_jit.move(TrustedImm32(string->length()), allocatorGPR);
    1345013452        } else {
    13451             bool canBeRope = !m_state.forNode(edges[0]).isType(SpecStringIdent);
     13453            bool needsRopeCase = canBeRope(edges[0]);
    1345213454            m_jit.loadPtr(CCallHelpers::Address(opGPRs[0], JSString::offsetOfValue()), scratch2GPR);
    1345313455            CCallHelpers::Jump isRope;
    13454             if (canBeRope)
     13456            if (needsRopeCase)
    1345513457                isRope = m_jit.branchIfRopeStringImpl(scratch2GPR);
    1345613458
     
    1345813460            m_jit.load32(CCallHelpers::Address(scratch2GPR, StringImpl::lengthMemoryOffset()), allocatorGPR);
    1345913461
    13460             if (canBeRope) {
     13462            if (needsRopeCase) {
    1346113463                auto done = m_jit.jump();
    1346213464
    1346313465                isRope.link(&m_jit);
    13464                 m_jit.load16(CCallHelpers::Address(opGPRs[0], JSRopeString::offsetOfFlags()), scratchGPR);
     13466                m_jit.load32(CCallHelpers::Address(opGPRs[0], JSRopeString::offsetOfFlags()), scratchGPR);
    1346513467                m_jit.load32(CCallHelpers::Address(opGPRs[0], JSRopeString::offsetOfLength()), allocatorGPR);
    1346613468                done.link(&m_jit);
     
    1348513487                    TrustedImm32(string->length()), allocatorGPR));
    1348613488        } else {
    13487             bool canBeRope = !m_state.forNode(edges[i]).isType(SpecStringIdent);
     13489            bool needsRopeCase = canBeRope(edges[i]);
    1348813490            m_jit.loadPtr(CCallHelpers::Address(opGPRs[i], JSString::offsetOfValue()), scratch2GPR);
    1348913491            CCallHelpers::Jump isRope;
    13490             if (canBeRope)
     13492            if (needsRopeCase)
    1349113493                isRope = m_jit.branchIfRopeStringImpl(scratch2GPR);
    1349213494
    13493             m_jit.and16(CCallHelpers::Address(scratch2GPR, StringImpl::flagsOffset()), scratchGPR);
     13495            m_jit.and32(CCallHelpers::Address(scratch2GPR, StringImpl::flagsOffset()), scratchGPR);
    1349413496            speculationCheck(
    1349513497                Uncountable, JSValueSource(), nullptr,
     
    1349713499                    CCallHelpers::Overflow,
    1349813500                    CCallHelpers::Address(scratch2GPR, StringImpl::lengthMemoryOffset()), allocatorGPR));
    13499             if (canBeRope) {
     13501            if (needsRopeCase) {
    1350013502                auto done = m_jit.jump();
    1350113503
    1350213504                isRope.link(&m_jit);
    13503                 m_jit.and16(CCallHelpers::Address(opGPRs[i], JSRopeString::offsetOfFlags()), scratchGPR);
     13505                m_jit.and32(CCallHelpers::Address(opGPRs[i], JSRopeString::offsetOfFlags()), scratchGPR);
    1350413506                m_jit.load32(CCallHelpers::Address(opGPRs[i], JSRopeString::offsetOfLength()), scratch2GPR);
    1350513507                speculationCheck(
     
    1351113513        }
    1351213514    }
    13513     m_jit.store16(scratchGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFlags()));
     13515
    1351413516    if (!ASSERT_DISABLED) {
    1351513517        CCallHelpers::Jump ok = m_jit.branch32(
     
    1351813520        ok.link(&m_jit);
    1351913521    }
    13520     m_jit.store32(allocatorGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfLength()));
     13522
     13523    static_assert(StringImpl::flagIs8Bit() == JSRopeString::is8BitInPointer, "");
     13524    m_jit.and32(TrustedImm32(StringImpl::flagIs8Bit()), scratchGPR);
     13525    m_jit.orPtr(opGPRs[0], scratchGPR);
     13526    m_jit.orPtr(TrustedImmPtr(JSString::isRopeInPointer), scratchGPR);
     13527    m_jit.storePtr(scratchGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber0()));
     13528
     13529    m_jit.move(opGPRs[1], scratchGPR);
     13530    m_jit.lshiftPtr(TrustedImm32(32), scratchGPR);
     13531    m_jit.orPtr(allocatorGPR, scratchGPR);
     13532    m_jit.storePtr(scratchGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber1()));
     13533
     13534    if (numOpGPRs == 2) {
     13535        m_jit.move(opGPRs[1], scratchGPR);
     13536        m_jit.rshiftPtr(TrustedImm32(32), scratchGPR);
     13537        m_jit.storePtr(scratchGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber2()));
     13538    } else {
     13539        m_jit.move(opGPRs[1], scratchGPR);
     13540        m_jit.rshiftPtr(TrustedImm32(32), scratchGPR);
     13541        m_jit.move(opGPRs[2], scratch2GPR);
     13542        m_jit.lshiftPtr(TrustedImm32(16), scratch2GPR);
     13543        m_jit.orPtr(scratch2GPR, scratchGPR);
     13544        m_jit.storePtr(scratchGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber2()));
     13545    }
     13546
    1352113547    auto isNonEmptyString = m_jit.branchTest32(CCallHelpers::NonZero, allocatorGPR);
    1352213548
  • trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h

    r242252 r242397  
    578578    bool isKnownNotCell(Node* node) { return !(m_state.forNode(node).m_type & SpecCell); }
    579579    bool isKnownNotOther(Node* node) { return !(m_state.forNode(node).m_type & SpecOther); }
     580
     581    bool canBeRope(Edge&);
    580582   
    581583    UniquedStringImpl* identifierUID(unsigned index)
  • trunk/Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.cpp

    r242252 r242397  
    8787    JSCell_cellState.changeParent(&JSCell_usefulBytes);
    8888    JSRopeString_flags.changeParent(&JSRopeString_fiber0);
     89    JSRopeString_length.changeParent(&JSRopeString_fiber1);
    8990
    9091    RELEASE_ASSERT(!JSCell_freeListNext.offset());
  • trunk/Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h

    r242252 r242397  
    9090    macro(JSPropertyNameEnumerator_indexLength, JSPropertyNameEnumerator::indexedLengthOffset()) \
    9191    macro(JSRopeString_flags, JSRopeString::offsetOfFlags()) \
     92    macro(JSRopeString_length, JSRopeString::offsetOfLength()) \
    9293    macro(JSRopeString_fiber0, JSRopeString::offsetOfFiber0()) \
    93     macro(JSRopeString_length, JSRopeString::offsetOfLength()) \
    94     macro(JSRopeString_fiber1Lower, JSRopeString::offsetOfFiber1Lower()) \
    95     macro(JSRopeString_fiber1Upper, JSRopeString::offsetOfFiber1Upper()) \
    96     macro(JSRopeString_fiber2Lower, JSRopeString::offsetOfFiber2Lower()) \
    97     macro(JSRopeString_fiber2Upper, JSRopeString::offsetOfFiber2Upper()) \
     94    macro(JSRopeString_fiber1, JSRopeString::offsetOfFiber1()) \
     95    macro(JSRopeString_fiber2, JSRopeString::offsetOfFiber2()) \
    9896    macro(JSScope_next, JSScope::offsetOfNext()) \
    9997    macro(JSSymbolTableObject_symbolTable, JSSymbolTableObject::offsetOfSymbolTable()) \
  • trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp

    r242252 r242397  
    65256525        }
    65266526       
     6527        LBasicBlock emptyCase = m_out.newBlock();
    65276528        LBasicBlock slowPath = m_out.newBlock();
    65286529        LBasicBlock continuation = m_out.newBlock();
     
    65336534            m_out.constIntPtr(allocator.localAllocator()), vm().stringStructure.get(), slowPath);
    65346535       
    6535         m_out.storePtr(m_out.bitOr(kids[0], m_out.constIntPtr(JSString::isRopeInPointer)), result, m_heaps.JSRopeString_fiber0);
    6536 
    6537         m_out.store32(m_out.castToInt32(kids[1]), result, m_heaps.JSRopeString_fiber1Lower);
    6538         m_out.store32As16(m_out.castToInt32(m_out.lShr(kids[1], m_out.constInt32(32))), result, m_heaps.JSRopeString_fiber1Upper);
    6539 
    6540         if (numKids == 3) {
    6541             m_out.store32(m_out.castToInt32(kids[2]), result, m_heaps.JSRopeString_fiber2Lower);
    6542             m_out.store32As16(m_out.castToInt32(m_out.lShr(kids[2], m_out.constInt32(32))), result, m_heaps.JSRopeString_fiber2Upper);
    6543         } else {
    6544             m_out.store32(m_out.int32Zero, result, m_heaps.JSRopeString_fiber2Lower);
    6545             m_out.store32As16(m_out.int32Zero, result, m_heaps.JSRopeString_fiber2Upper);
    6546         }
     6536        // This puts nullptr for the first fiber. It makes visitChildren safe even if this JSRopeString is discarded due to the speculation failure in the following path.
     6537        m_out.storePtr(m_out.constIntPtr(JSString::isRopeInPointer), result, m_heaps.JSRopeString_fiber0);
    65476538
    65486539        auto getFlagsAndLength = [&] (Edge& edge, LValue child) {
     
    65616552
    65626553            LBasicBlock lastNext = m_out.appendTo(ropeCase, notRopeCase);
    6563             ValueFromBlock flagsForRope = m_out.anchor(m_out.load16ZeroExt32(child, m_heaps.JSRopeString_flags));
     6554            ValueFromBlock flagsForRope = m_out.anchor(m_out.load32NonNegative(child, m_heaps.JSRopeString_flags));
    65646555            ValueFromBlock lengthForRope = m_out.anchor(m_out.load32NonNegative(child, m_heaps.JSRopeString_length));
    65656556            m_out.jump(continuation);
     
    65926583            flagsAndLength = mergeFlagsAndLength(edges[i], kids[i], flagsAndLength);
    65936584        }
    6594         m_out.store32As16(flagsAndLength.flags, result, m_heaps.JSRopeString_flags);
    6595         m_out.store32(flagsAndLength.length, result, m_heaps.JSRopeString_length);
     6585
     6586        m_out.storePtr(
     6587            m_out.bitOr(
     6588                m_out.bitOr(kids[0], m_out.constIntPtr(JSString::isRopeInPointer)),
     6589                m_out.bitAnd(m_out.constIntPtr(JSRopeString::is8BitInPointer), m_out.zeroExtPtr(flagsAndLength.flags))),
     6590            result, m_heaps.JSRopeString_fiber0);
     6591        m_out.storePtr(
     6592            m_out.bitOr(m_out.zeroExtPtr(flagsAndLength.length), m_out.shl(kids[1], m_out.constInt32(32))),
     6593            result, m_heaps.JSRopeString_fiber1);
     6594        if (numKids == 2)
     6595            m_out.storePtr(m_out.lShr(kids[1], m_out.constInt32(32)), result, m_heaps.JSRopeString_fiber2);
     6596        else
     6597            m_out.storePtr(m_out.bitOr(m_out.lShr(kids[1], m_out.constInt32(32)), m_out.shl(kids[2], m_out.constInt32(16))), result, m_heaps.JSRopeString_fiber2);
    65966598       
    65976599        mutatorFence();
    6598         ValueFromBlock fastResult = m_out.anchor(m_out.select(m_out.isZero32(flagsAndLength.length), weakPointer(jsEmptyString(&m_graph.m_vm)), result));
     6600        ValueFromBlock fastResult = m_out.anchor(result);
     6601        m_out.branch(m_out.isZero32(flagsAndLength.length), rarely(emptyCase), usually(continuation));
     6602
     6603        LBasicBlock lastNext = m_out.appendTo(emptyCase, slowPath);
     6604        ValueFromBlock emptyResult = m_out.anchor(weakPointer(jsEmptyString(&m_graph.m_vm)));
    65996605        m_out.jump(continuation);
    66006606       
    6601         LBasicBlock lastNext = m_out.appendTo(slowPath, continuation);
     6607        m_out.appendTo(slowPath, continuation);
    66026608        LValue slowResultValue;
    66036609        VM& vm = this->vm();
     
    66276633       
    66286634        m_out.appendTo(continuation, lastNext);
    6629         setJSValue(m_out.phi(Int64, fastResult, slowResult));
     6635        setJSValue(m_out.phi(Int64, fastResult, emptyResult, slowResult));
    66306636    }
    66316637   
     
    1568115687                    return m_out.booleanFalse;
    1568215688            }
     15689            String value = edge->tryGetString(m_graph);
     15690            if (!value.isNull()) {
     15691                // If this value is LazyValue, it will be converted to JSString, and the result must be non-rope string.
     15692                return m_out.booleanFalse;
     15693            }
    1568315694        }
    1568415695
     
    1569415705                if (value.isCell() && value.asCell()->type() == StringType && !asString(value)->isRope())
    1569515706                    return m_out.booleanTrue;
     15707            }
     15708            String value = edge->tryGetString(m_graph);
     15709            if (!value.isNull()) {
     15710                // If this value is LazyValue, it will be converted to JSString, and the result must be non-rope string.
     15711                return m_out.booleanTrue;
    1569615712            }
    1569715713        }
  • trunk/Source/JavaScriptCore/runtime/JSString.cpp

    r242252 r242397  
    115115    uintptr_t pointer = thisObject->m_fiber;
    116116    if (pointer & isRopeInPointer) {
    117         if ((pointer & JSRopeString::stringMask) == JSRopeString::substringSentinel()) {
     117        if (pointer & JSRopeString::isSubstringInPointer) {
    118118            visitor.appendUnbarriered(static_cast<JSRopeString*>(thisObject)->fiber1());
    119119            return;
  • trunk/Source/JavaScriptCore/runtime/JSString.h

    r242299 r242397  
    8787//              0                        8        10               16                       32                                     48
    8888// JSString     [   ID      ][  header  ][   String pointer      0]
    89 // JSRopeString [   ID      ][  header  ][ flags ][ 1st fiber    1][  length  ][2nd lower32][2nd upper16][3rd lower16][3rd upper32]
     89// JSRopeString [   ID      ][  header  ][   1st fiber         xyz][  length  ][2nd lower32][2nd upper16][3rd lower16][3rd upper32]
    9090//                                                               ^
    91 //                                                            isRope bit
     91//                                            x:(is8Bit),y:(isSubstring),z:(isRope) bit flags
    9292class JSString : public JSCell {
    9393public:
     
    267267    friend class JSString;
    268268public:
     269    // We use lower 3bits of fiber0 for flags. These bits are usable due to alignment, and it is OK even in 32bit architecture.
     270    static constexpr uintptr_t is8BitInPointer = static_cast<uintptr_t>(StringImpl::flagIs8Bit());
     271    static constexpr uintptr_t isSubstringInPointer = 0x2;
     272    static_assert(is8BitInPointer == 0b100, "");
     273    static_assert(isSubstringInPointer == 0b010, "");
     274    static_assert(isRopeInPointer == 0b001, "");
     275    static constexpr uintptr_t stringMask = ~(isRopeInPointer | is8BitInPointer | isSubstringInPointer);
    269276#if CPU(ADDRESS64)
    270277    static_assert(sizeof(uintptr_t) == sizeof(uint64_t), "");
    271     static constexpr uintptr_t flagMask = 0xffff000000000000ULL;
    272     static constexpr uintptr_t stringMask = ~(flagMask | isRopeInPointer);
    273     static_assert(StringImpl::flagIs8Bit() == 0b100, "");
    274     static constexpr uintptr_t is8BitInPointer = static_cast<uintptr_t>(StringImpl::flagIs8Bit()) << 48;
    275 
    276278    class CompactFibers {
    277279    public:
     
    290292        JSString* fiber2() const
    291293        {
    292             return bitwise_cast<JSString*>(static_cast<uintptr_t>(m_fiber2Lower) | (static_cast<uintptr_t>(m_fiber2Upper) << 32));
     294            return bitwise_cast<JSString*>(static_cast<uintptr_t>(m_fiber2Lower) | (static_cast<uintptr_t>(m_fiber2Upper) << 16));
    293295        }
    294296        void initializeFiber2(JSString* fiber)
    295297        {
    296298            uintptr_t pointer = bitwise_cast<uintptr_t>(fiber);
    297             m_fiber2Lower = static_cast<uint32_t>(pointer);
    298             m_fiber2Upper = static_cast<uint16_t>(pointer >> 32);
     299            m_fiber2Lower = static_cast<uint16_t>(pointer);
     300            m_fiber2Upper = static_cast<uint32_t>(pointer >> 16);
    299301        }
    300302
     
    306308
    307309        static ptrdiff_t offsetOfLength() { return OBJECT_OFFSETOF(CompactFibers, m_length); }
    308         static ptrdiff_t offsetOfFiber1Lower() { return OBJECT_OFFSETOF(CompactFibers, m_fiber1Lower); }
    309         static ptrdiff_t offsetOfFiber1Upper() { return OBJECT_OFFSETOF(CompactFibers, m_fiber1Upper); }
    310         static ptrdiff_t offsetOfFiber2Lower() { return OBJECT_OFFSETOF(CompactFibers, m_fiber2Lower); }
    311         static ptrdiff_t offsetOfFiber2Upper() { return OBJECT_OFFSETOF(CompactFibers, m_fiber2Upper); }
     310        static ptrdiff_t offsetOfFiber1() { return OBJECT_OFFSETOF(CompactFibers, m_length); }
     311        static ptrdiff_t offsetOfFiber2() { return OBJECT_OFFSETOF(CompactFibers, m_fiber1Upper); }
    312312
    313313    private:
     
    315315        uint32_t m_fiber1Lower { 0 };
    316316        uint16_t m_fiber1Upper { 0 };
    317         uint16_t m_fiber2Upper { 0 };
    318         uint32_t m_fiber2Lower { 0 };
     317        uint16_t m_fiber2Lower { 0 };
     318        uint32_t m_fiber2Upper { 0 };
    319319    };
    320320    static_assert(sizeof(CompactFibers) == sizeof(void*) * 2, "");
    321321#else
    322     static constexpr uintptr_t stringMask = ~(isRopeInPointer);
    323 
    324322    class CompactFibers {
    325323    public:
     
    348346        }
    349347
    350         void initializeIs8Bit(bool flag)
    351         {
    352             if (flag)
    353                 m_flags |= static_cast<uintptr_t>(StringImpl::flagIs8Bit());
    354             else
    355                 m_flags &= ~static_cast<uintptr_t>(StringImpl::flagIs8Bit());
    356         }
    357 
    358         bool is8Bit()
    359         {
    360             return m_flags & static_cast<uintptr_t>(StringImpl::flagIs8Bit());
    361         }
    362 
    363348        static ptrdiff_t offsetOfLength() { return OBJECT_OFFSETOF(CompactFibers, m_length); }
     349        static ptrdiff_t offsetOfFiber1() { return OBJECT_OFFSETOF(CompactFibers, m_fiber1); }
     350        static ptrdiff_t offsetOfFiber2() { return OBJECT_OFFSETOF(CompactFibers, m_fiber2); }
    364351
    365352    private:
    366353        uint32_t m_length { 0 };
    367         uint32_t m_flags { 0 };
    368354        JSString* m_fiber1 { nullptr };
    369355        JSString* m_fiber2 { nullptr };
     
    457443    void initializeIs8Bit(bool flag) const
    458444    {
    459 #if CPU(ADDRESS64)
    460445        if (flag)
    461446            m_fiber |= is8BitInPointer;
    462447        else
    463448            m_fiber &= ~is8BitInPointer;
    464 #else
    465         m_compactFibers.initializeIs8Bit(flag);
    466 #endif
     449    }
     450
     451    void initializeIsSubstring(bool flag) const
     452    {
     453        if (flag)
     454            m_fiber |= isSubstringInPointer;
     455        else
     456            m_fiber &= ~isSubstringInPointer;
    467457    }
    468458
     
    471461        ASSERT(length <= MaxLength);
    472462        m_compactFibers.initializeLength(length);
     463    }
     464
     465    JSRopeString(VM& vm)
     466        : JSString(vm)
     467    {
     468        initializeIsSubstring(false);
     469        initializeLength(0);
     470        initializeIs8Bit(true);
     471        initializeFiber0(nullptr);
     472        initializeFiber1(nullptr);
     473        initializeFiber2(nullptr);
    473474    }
    474475
     
    550551public:
    551552    static ptrdiff_t offsetOfLength() { return OBJECT_OFFSETOF(JSRopeString, m_compactFibers) + CompactFibers::offsetOfLength(); } // 32byte width.
     553    static ptrdiff_t offsetOfFlags() { return offsetOfValue(); }
    552554    static ptrdiff_t offsetOfFiber0() { return offsetOfValue(); }
    553 #if CPU(ADDRESS64)
    554     static ptrdiff_t offsetOfFlags() { return offsetOfValue() + sizeof(uint16_t) * 3; } // 16byte width.
    555     static ptrdiff_t offsetOfFiber1Lower() { return OBJECT_OFFSETOF(JSRopeString, m_compactFibers) + CompactFibers::offsetOfFiber1Lower(); } // 32byte width.
    556     static ptrdiff_t offsetOfFiber1Upper() { return OBJECT_OFFSETOF(JSRopeString, m_compactFibers) + CompactFibers::offsetOfFiber1Upper(); } // 16byte width.
    557     static ptrdiff_t offsetOfFiber2Lower() { return OBJECT_OFFSETOF(JSRopeString, m_compactFibers) + CompactFibers::offsetOfFiber2Lower(); } // 32byte width.
    558     static ptrdiff_t offsetOfFiber2Upper() { return OBJECT_OFFSETOF(JSRopeString, m_compactFibers) + CompactFibers::offsetOfFiber2Upper(); } // 16byte width.
    559 #elif USE(JSVALUE64)
    560     // FIXME: This is an temporary workaround to make JSC built on ARM64_32. Once we start calculating bits before storing them to JSRopeString,
    561     // we do not need to have such a detailed information as an offset. After that, what we only need is offsetOfFiber0, offsetOfFiber1, and offsetOfFiber2.
    562     // https://bugs.webkit.org/show_bug.cgi?id=195234
    563     static ptrdiff_t offsetOfFlags() { ASSERT_NOT_REACHED(); return 0; }
    564     static ptrdiff_t offsetOfFiber1Lower() { ASSERT_NOT_REACHED(); return 0; }
    565     static ptrdiff_t offsetOfFiber1Upper() { ASSERT_NOT_REACHED(); return 0; }
    566     static ptrdiff_t offsetOfFiber2Lower() { ASSERT_NOT_REACHED(); return 0; }
    567     static ptrdiff_t offsetOfFiber2Upper() { ASSERT_NOT_REACHED(); return 0; }
    568 #endif
     555    static ptrdiff_t offsetOfFiber1() { return OBJECT_OFFSETOF(JSRopeString, m_compactFibers) + CompactFibers::offsetOfFiber1(); }
     556    static ptrdiff_t offsetOfFiber2() { return OBJECT_OFFSETOF(JSRopeString, m_compactFibers) + CompactFibers::offsetOfFiber2(); }
    569557
    570558    static constexpr unsigned s_maxInternalRopeLength = 3;
     559
     560    // This JSRopeString is only used to simulate half-baked JSRopeString in DFG and FTL MakeRope. If OSR exit happens in
     561    // the middle of MakeRope due to string length overflow, we have half-baked JSRopeString which is the same to the result
     562    // of this function. This half-baked JSRopeString will not be exposed to users, but still collectors can see it due to
     563    // the conservative stack scan. This JSRopeString is used to test the collector with such a half-baked JSRopeString.
     564    // Because this JSRopeString breaks the JSString's invariant (only one singleton JSString can be zero length), almost all the
     565    // operations in JS fail to handle this string correctly.
     566    static JSRopeString* createNullForTesting(VM& vm)
     567    {
     568        JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
     569        newString->finishCreation(vm);
     570        ASSERT(!newString->length());
     571        ASSERT(newString->isRope());
     572        ASSERT(fiber0() == nullptr);
     573        return newString;
     574    }
    571575
    572576private:
     
    576580        newString->finishCreation(vm);
    577581        ASSERT(newString->length());
     582        ASSERT(newString->isRope());
    578583        return newString;
    579584    }
     
    583588        newString->finishCreation(vm);
    584589        ASSERT(newString->length());
     590        ASSERT(newString->isRope());
    585591        return newString;
    586592    }
     
    591597        newString->finishCreationSubstring(vm, exec);
    592598        ASSERT(newString->length());
     599        ASSERT(newString->isRope());
    593600        return newString;
    594601    }
     
    599606        newString->finishCreationSubstringOfResolved(vm);
    600607        ASSERT(newString->length());
     608        ASSERT(newString->isRope());
    601609        return newString;
    602610    }
     
    686694    }
    687695
    688     static constexpr uintptr_t notSubstringSentinel()
    689     {
    690         return 0;
    691     }
    692 
    693     static constexpr uintptr_t substringSentinel()
    694     {
    695         return 2;
    696     }
    697 
    698     bool isSubstring() const
    699     {
    700         return (m_fiber & stringMask) == substringSentinel();
    701     }
    702 
    703     void initializeIsSubstring(bool isSubstring)
    704     {
    705         m_fiber |= (isSubstring ? substringSentinel() : notSubstringSentinel());
    706     }
    707 
    708696    static_assert(s_maxInternalRopeLength >= 2, "");
    709697    mutable CompactFibers m_compactFibers;
     
    727715    uintptr_t pointer = m_fiber;
    728716    if (pointer & isRopeInPointer) {
    729 #if CPU(ADDRESS64)
    730717        // Do not load m_fiber twice. We should use the information in pointer.
    731718        // Otherwise, JSRopeString may be converted to JSString between the first and second accesses.
    732719        return pointer & JSRopeString::is8BitInPointer;
    733 #else
    734         // It is OK to load flag since even if JSRopeString is converted to JSString, this flag still exists.
    735         return jsCast<const JSRopeString*>(this)->m_compactFibers.is8Bit();
    736 #endif
    737720    }
    738721    return bitwise_cast<StringImpl*>(pointer)->is8Bit();
     
    10471030inline bool JSString::isSubstring() const
    10481031{
    1049     return isRope() && static_cast<const JSRopeString*>(this)->isSubstring();
     1032    return m_fiber & JSRopeString::isSubstringInPointer;
    10501033}
    10511034
  • trunk/Source/JavaScriptCore/tools/JSDollarVM.cpp

    r242109 r242397  
    17221722}
    17231723
     1724static EncodedJSValue JSC_HOST_CALL functionCreateNullRopeString(ExecState* exec)
     1725{
     1726    VM& vm = exec->vm();
     1727    JSLockHolder lock(vm);
     1728    return JSValue::encode(JSRopeString::createNullForTesting(vm));
     1729}
     1730
    17241731static EncodedJSValue JSC_HOST_CALL functionCreateImpureGetter(ExecState* exec)
    17251732{
     
    22212228    addFunction(vm, "createProxy", functionCreateProxy, 1);
    22222229    addFunction(vm, "createRuntimeArray", functionCreateRuntimeArray, 0);
     2230    addFunction(vm, "createNullRopeString", functionCreateNullRopeString, 0);
    22232231
    22242232    addFunction(vm, "createImpureGetter", functionCreateImpureGetter, 1);
Note: See TracChangeset for help on using the changeset viewer.