Ignore:
Timestamp:
Nov 1, 2017, 6:25:21 AM (8 years ago)
Author:
Yusuke Suzuki
Message:

[DFG][FTL] Introduce StringSlice
https://bugs.webkit.org/show_bug.cgi?id=178934

Reviewed by Saam Barati.

JSTests:

  • microbenchmarks/string-slice-empty.js: Added.

(slice):

  • microbenchmarks/string-slice-one-char.js: Added.

(slice):

  • microbenchmarks/string-slice.js: Added.

(slice):

Source/JavaScriptCore:

String.prototype.slice is one of the most frequently called function in ARES-6/Babylon.
This patch introduces StringSlice DFG node to optimize it in DFG and FTL.

This patch's StringSlice node optimizes the following things.

  1. Empty string generation is accelerated. It is fully executed inline.
  2. One char string generation is accelerated. < 0x100 character is supported right now.

It is the same to charAt acceleration.

  1. We calculate start and end index in DFG/FTL with Int32Use information and call optimized

operation.

We do not inline (3)'s operation right now since we do not have a way to call bmalloc allocation from DFG / FTL.
And we do not optimize String.prototype.{substring,substr} right now. But they can be optimized based on this change
in subsequent changes.

This patch improves ARES-6/Babylon performance by 3% in steady state.

Baseline:

Running... Babylon ( 1 to go)
firstIteration: 50.05 +- 13.68 ms
averageWorstCase: 16.80 +- 1.27 ms
steadyState: 7.53 +- 0.22 ms

Patched:

Running... Babylon ( 1 to go)
firstIteration: 50.91 +- 13.41 ms
averageWorstCase: 16.12 +- 0.99 ms
steadyState: 7.30 +- 0.29 ms

  • dfg/DFGAbstractInterpreterInlines.h:

(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):

  • dfg/DFGBackwardsPropagationPhase.cpp:

(JSC::DFG::BackwardsPropagationPhase::propagate):

  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::handleIntrinsicCall):

  • dfg/DFGClobberize.h:

(JSC::DFG::clobberize):

  • dfg/DFGDoesGC.cpp:

(JSC::DFG::doesGC):

  • dfg/DFGFixupPhase.cpp:

(JSC::DFG::FixupPhase::fixupNode):

  • dfg/DFGNodeType.h:
  • dfg/DFGOperations.cpp:
  • dfg/DFGOperations.h:
  • dfg/DFGPredictionPropagationPhase.cpp:
  • dfg/DFGSafeToExecute.h:

(JSC::DFG::safeToExecute):

  • dfg/DFGSpeculativeJIT.cpp:

(JSC::DFG::SpeculativeJIT::compileStringSlice):
(JSC::DFG::SpeculativeJIT::emitPopulateSliceIndex):
(JSC::DFG::SpeculativeJIT::compileArraySlice):
(JSC::DFG::SpeculativeJIT::compileArrayIndexOf):

  • dfg/DFGSpeculativeJIT.h:

(JSC::DFG::SpeculativeJIT::callOperation):

  • dfg/DFGSpeculativeJIT32_64.cpp:

(JSC::DFG::SpeculativeJIT::compile):

  • dfg/DFGSpeculativeJIT64.cpp:

(JSC::DFG::SpeculativeJIT::compile):

  • ftl/FTLCapabilities.cpp:

(JSC::FTL::canCompile):

  • ftl/FTLLowerDFGToB3.cpp:

(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::populateSliceRange):
(JSC::FTL::DFG::LowerDFGToB3::compileArraySlice):
(JSC::FTL::DFG::LowerDFGToB3::compileStringSlice):

  • jit/JITOperations.h:
  • runtime/Intrinsic.cpp:

(JSC::intrinsicName):

  • runtime/Intrinsic.h:
  • runtime/StringPrototype.cpp:

(JSC::StringPrototype::finishCreation):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp

    r224258 r224276  
    11761176            compileUnreachable();
    11771177            break;
     1178        case StringSlice:
     1179            compileStringSlice();
     1180            break;
    11781181        case ToLowerCase:
    11791182            compileToLowerCase();
     
    43024305    }
    43034306
     4307    std::pair<LValue, LValue> populateSliceRange(LValue start, LValue end, LValue length)
     4308    {
     4309        // end can be nullptr.
     4310        ASSERT(start);
     4311        ASSERT(length);
     4312
     4313        auto pickIndex = [&] (LValue index) {
     4314            return m_out.select(m_out.greaterThanOrEqual(index, m_out.int32Zero),
     4315                m_out.select(m_out.above(index, length), length, index),
     4316                m_out.select(m_out.lessThan(m_out.add(length, index), m_out.int32Zero), m_out.int32Zero, m_out.add(length, index)));
     4317        };
     4318
     4319        LValue endBoundary = length;
     4320        if (end)
     4321            endBoundary = pickIndex(end);
     4322        LValue startIndex = pickIndex(start);
     4323        return std::make_pair(startIndex, endBoundary);
     4324    }
     4325
    43044326    void compileArraySlice()
    43054327    {
     
    43084330        LValue sourceStorage = lowStorage(m_node->numChildren() == 3 ? m_graph.varArgChild(m_node, 2) : m_graph.varArgChild(m_node, 3));
    43094331        LValue inputLength = m_out.load32(sourceStorage, m_heaps.Butterfly_publicLength);
    4310 
    4311         LValue endBoundary;
    4312         if (m_node->numChildren() == 3)
    4313             endBoundary = m_out.load32(sourceStorage, m_heaps.Butterfly_publicLength);
    4314         else {
    4315             endBoundary = lowInt32(m_graph.varArgChild(m_node, 2));
    4316             endBoundary = m_out.select(m_out.greaterThanOrEqual(endBoundary, m_out.constInt32(0)),
    4317                 m_out.select(m_out.above(endBoundary, inputLength), inputLength, endBoundary),
    4318                 m_out.select(m_out.lessThan(m_out.add(inputLength, endBoundary), m_out.constInt32(0)), m_out.constInt32(0), m_out.add(inputLength, endBoundary)));
    4319         }
    4320 
    4321         LValue startIndex = lowInt32(m_graph.varArgChild(m_node, 1));
    4322         startIndex = m_out.select(m_out.greaterThanOrEqual(startIndex, m_out.constInt32(0)),
    4323             m_out.select(m_out.above(startIndex, inputLength), inputLength, startIndex),
    4324             m_out.select(m_out.lessThan(m_out.add(inputLength, startIndex), m_out.constInt32(0)), m_out.constInt32(0), m_out.add(inputLength, startIndex)));
     4332        LValue start = lowInt32(m_graph.varArgChild(m_node, 1));
     4333        LValue end = nullptr;
     4334        if (m_node->numChildren() != 3)
     4335            end = lowInt32(m_graph.varArgChild(m_node, 2));
     4336
     4337        auto range = populateSliceRange(start, end, inputLength);
     4338        LValue startIndex = range.first;
     4339        LValue endBoundary = range.second;
    43254340
    43264341        LValue resultLength = m_out.select(m_out.below(startIndex, endBoundary),
     
    1070210717        DFG_ASSERT(m_graph, m_node, m_node->isBinaryUseKind(UntypedUse));
    1070310718        nonSpeculativeCompare(intFunctor, fallbackFunction);
     10719    }
     10720
     10721    void compileStringSlice()
     10722    {
     10723        LBasicBlock emptyCase = m_out.newBlock();
     10724        LBasicBlock notEmptyCase = m_out.newBlock();
     10725        LBasicBlock oneCharCase = m_out.newBlock();
     10726        LBasicBlock bitCheckCase = m_out.newBlock();
     10727        LBasicBlock is8Bit = m_out.newBlock();
     10728        LBasicBlock is16Bit = m_out.newBlock();
     10729        LBasicBlock bitsContinuation = m_out.newBlock();
     10730        LBasicBlock bigCharacter = m_out.newBlock();
     10731        LBasicBlock slowCase = m_out.newBlock();
     10732        LBasicBlock continuation = m_out.newBlock();
     10733
     10734        LValue string = lowString(m_node->child1());
     10735        LValue length = m_out.load32NonNegative(string, m_heaps.JSString_length);
     10736        LValue start = lowInt32(m_node->child2());
     10737        LValue end = nullptr;
     10738        if (m_node->child3())
     10739            end = lowInt32(m_node->child3());
     10740
     10741        auto range = populateSliceRange(start, end, length);
     10742        LValue from = range.first;
     10743        LValue to = range.second;
     10744
     10745        LValue span = m_out.sub(to, from);
     10746        m_out.branch(m_out.lessThanOrEqual(span, m_out.int32Zero), unsure(emptyCase), unsure(notEmptyCase));
     10747
     10748        Vector<ValueFromBlock, 4> results;
     10749
     10750        LBasicBlock lastNext = m_out.appendTo(emptyCase, notEmptyCase);
     10751        results.append(m_out.anchor(m_out.weakPointer(m_graph, jsEmptyString(&vm()))));
     10752        m_out.jump(continuation);
     10753
     10754        m_out.appendTo(notEmptyCase, oneCharCase);
     10755        m_out.branch(m_out.equal(span, m_out.int32One), unsure(oneCharCase), unsure(slowCase));
     10756
     10757        m_out.appendTo(oneCharCase, bitCheckCase);
     10758        LValue stringImpl = m_out.loadPtr(string, m_heaps.JSString_value);
     10759        m_out.branch(m_out.isNull(stringImpl), unsure(slowCase), unsure(bitCheckCase));
     10760
     10761        m_out.appendTo(bitCheckCase, is8Bit);
     10762        LValue storage = m_out.loadPtr(stringImpl, m_heaps.StringImpl_data);
     10763        m_out.branch(
     10764            m_out.testIsZero32(
     10765                m_out.load32(stringImpl, m_heaps.StringImpl_hashAndFlags),
     10766                m_out.constInt32(StringImpl::flagIs8Bit())),
     10767            unsure(is16Bit), unsure(is8Bit));
     10768
     10769        m_out.appendTo(is8Bit, is16Bit);
     10770        // FIXME: Need to cage strings!
     10771        // https://bugs.webkit.org/show_bug.cgi?id=174924
     10772        ValueFromBlock char8Bit = m_out.anchor(m_out.load8ZeroExt32(m_out.baseIndex(m_heaps.characters8, storage, m_out.zeroExtPtr(from))));
     10773        m_out.jump(bitsContinuation);
     10774
     10775        m_out.appendTo(is16Bit, bigCharacter);
     10776        LValue char16BitValue = m_out.load16ZeroExt32(m_out.baseIndex(m_heaps.characters16, storage, m_out.zeroExtPtr(from)));
     10777        ValueFromBlock char16Bit = m_out.anchor(char16BitValue);
     10778        m_out.branch(
     10779            m_out.aboveOrEqual(char16BitValue, m_out.constInt32(0x100)),
     10780            rarely(bigCharacter), usually(bitsContinuation));
     10781
     10782        m_out.appendTo(bigCharacter, bitsContinuation);
     10783        results.append(m_out.anchor(vmCall(
     10784            Int64, m_out.operation(operationSingleCharacterString),
     10785            m_callFrame, char16BitValue)));
     10786        m_out.jump(continuation);
     10787
     10788        m_out.appendTo(bitsContinuation, slowCase);
     10789        LValue character = m_out.phi(Int32, char8Bit, char16Bit);
     10790        LValue smallStrings = m_out.constIntPtr(vm().smallStrings.singleCharacterStrings());
     10791        results.append(m_out.anchor(m_out.loadPtr(m_out.baseIndex(
     10792            m_heaps.singleCharacterStrings, smallStrings, m_out.zeroExtPtr(character)))));
     10793        m_out.jump(continuation);
     10794
     10795        m_out.appendTo(slowCase, continuation);
     10796        results.append(m_out.anchor(vmCall(pointerType(), m_out.operation(operationStringSubstr), m_callFrame, string, from, span)));
     10797        m_out.jump(continuation);
     10798
     10799        m_out.appendTo(continuation, lastNext);
     10800        setJSValue(m_out.phi(pointerType(), results));
    1070410801    }
    1070510802
Note: See TracChangeset for help on using the changeset viewer.