Ignore:
Timestamp:
Oct 17, 2017, 5:02:01 AM (8 years ago)
Author:
Yusuke Suzuki
Message:

[JSC] proto getter should be fast
https://bugs.webkit.org/show_bug.cgi?id=178067

Reviewed by Saam Barati.

JSTests:

  • stress/dfg-object-proto-accessor.js: Added.

(shouldBe):
(shouldThrow):
(target):

  • stress/dfg-object-proto-getter.js: Added.

(shouldBe):
(shouldThrow):
(target):

  • stress/dfg-object-prototype-of.js: Added.

(shouldBe):
(shouldThrow):
(target):

  • stress/dfg-reflect-get-prototype-of.js: Added.

(shouldBe):
(shouldThrow):
(target):

  • stress/object-get-prototype-of-filtered.js: Added.

(shouldBe):
(shouldThrow):
(target):
(i.Cocoa):

  • stress/object-get-prototype-of-mono-proto.js: Added.

(shouldBe):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(target):

  • stress/object-get-prototype-of-poly-mono-proto.js: Added.

(shouldBe):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(target):

  • stress/object-get-prototype-of-poly-proto.js: Added.

(shouldBe):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(target):

  • stress/object-proto-getter-filtered.js: Added.

(shouldBe):
(shouldThrow):
(target):
(i.Cocoa):

  • stress/object-proto-getter-poly-mono-proto.js: Added.

(shouldBe):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(target):

  • stress/object-proto-getter-poly-proto.js: Added.

(shouldBe):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(target):

  • stress/object-prototype-proto-accessors-should-throw-on-undefined-this.js:
  • stress/string-proto.js: Added.

(shouldBe):
(target):

Source/JavaScriptCore:

In our ES6 class implementation, we access proto field to retrieve super constructor.
Currently, it is handled as an usual getter call to a generic function. And DFG just emits
Call node for this. It is inefficient since typically we know the prototype of the given
object when accessing object.__proto__ since we emit CheckStructure for this object.
If Structure has mono proto, we can immediately fold it to constant value. If it is poly proto,
we can still change this to efficient access to poly proto slot.

This patch implements GetPrototypeOf DFG node. This node efficiently accesses to prototype of
the given object. And in AI and ByteCodeParser phase, we attempt to fold it to constant.
ByteCodeParser's folding is a bit important since we have callee.__proto__ code to get super
constructor. If we can change this to constant, we can reify CallLinkInfo with this constant.
This paves the way to optimizing ArrayConstructor super calls[1], which is particularly important
for ARES-6 ML.

And we also optimize Reflect.getPrototypeOf and Object.getPrototypeOf with this GetPrototypeOf node.

Currently, proto access for poly proto object is not handled well in IC. But we add code handling
poly proto in GetPrototypeOf since Reflect.getPrototypeOf and Object.getPrototypeOf can use it.
Once IC starts handling poly proto & intrinsic getter well, this code will be used for that too.

This patch improves SixSpeed super.es6 by 3.42x.

baseline patched

super.es6 123.6666+-3.9917 36.1684+-1.0351 definitely 3.4192x faster

[1]: https://bugs.webkit.org/show_bug.cgi?id=178064

  • dfg/DFGAbstractInterpreterInlines.h:

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

  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
(JSC::DFG::ByteCodeParser::handleIntrinsicGetter):
(JSC::DFG::ByteCodeParser::handleGetById):

  • dfg/DFGClobberize.h:

(JSC::DFG::clobberize):

  • dfg/DFGDoesGC.cpp:

(JSC::DFG::doesGC):

  • dfg/DFGFixupPhase.cpp:

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

  • dfg/DFGHeapLocation.cpp:

(WTF::printInternal):

  • dfg/DFGHeapLocation.h:
  • dfg/DFGNode.h:

(JSC::DFG::Node::hasHeapPrediction):
(JSC::DFG::Node::shouldSpeculateFunction):

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

(JSC::DFG::safeToExecute):

  • dfg/DFGSpeculativeJIT.cpp:

(JSC::DFG::SpeculativeJIT::speculateFunction):
(JSC::DFG::SpeculativeJIT::speculateFinalObject):
(JSC::DFG::SpeculativeJIT::compileGetPrototypeOf):

  • dfg/DFGSpeculativeJIT.h:
  • 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::compileGetPrototypeOf):
(JSC::FTL::DFG::LowerDFGToB3::compileInstanceOf):

  • jit/IntrinsicEmitter.cpp:

(JSC::IntrinsicGetterAccessCase::canEmitIntrinsicGetter):
(JSC::IntrinsicGetterAccessCase::emitIntrinsicGetter):

  • runtime/Intrinsic.cpp:

(JSC::intrinsicName):

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

(JSC::JSGlobalObject::init):

  • runtime/JSGlobalObjectFunctions.cpp:

(JSC::globalFuncProtoGetter):

  • runtime/JSGlobalObjectFunctions.h:
  • runtime/ObjectConstructor.cpp:
  • runtime/ReflectObject.cpp:

LayoutTests:

  • js/object-literal-shorthand-construction-expected.txt:
  • js/script-tests/object-literal-shorthand-construction.js:

(set 2):
(get 1):

  • js/script-tests/sloppy-getter-setter-global-object.js:
  • js/sloppy-getter-setter-global-object-expected.txt:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp

    r223318 r223523  
    239239    bool handleDOMJITCall(Node* callee, int resultOperand, const DOMJIT::Signature*, int registerOffset, int argumentCountIncludingThis, SpeculatedType prediction, const ChecksFunctor& insertChecks);
    240240    template<typename ChecksFunctor>
    241     bool handleIntrinsicGetter(int resultOperand, const GetByIdVariant& intrinsicVariant, Node* thisNode, const ChecksFunctor& insertChecks);
     241    bool handleIntrinsicGetter(int resultOperand, SpeculatedType prediction, const GetByIdVariant& intrinsicVariant, Node* thisNode, const ChecksFunctor& insertChecks);
    242242    template<typename ChecksFunctor>
    243243    bool handleTypedArrayConstructor(int resultOperand, InternalFunction*, int registerOffset, int argumentCountIncludingThis, TypedArrayType, const ChecksFunctor& insertChecks);
     
    26002600    }
    26012601
     2602    case ObjectGetPrototypeOfIntrinsic: {
     2603        if (argumentCountIncludingThis != 2)
     2604            return false;
     2605
     2606        insertChecks();
     2607        set(VirtualRegister(resultOperand), addToGraph(GetPrototypeOf, OpInfo(0), OpInfo(prediction), get(virtualRegisterForArgument(1, registerOffset))));
     2608        return true;
     2609    }
     2610
     2611    case ReflectGetPrototypeOfIntrinsic: {
     2612        if (argumentCountIncludingThis != 2)
     2613            return false;
     2614
     2615        insertChecks();
     2616        set(VirtualRegister(resultOperand), addToGraph(GetPrototypeOf, OpInfo(0), OpInfo(prediction), Edge(get(virtualRegisterForArgument(1, registerOffset)), ObjectUse)));
     2617        return true;
     2618    }
     2619
    26022620    case IsTypedArrayViewIntrinsic: {
    26032621        ASSERT(argumentCountIncludingThis == 2);
     
    29472965
    29482966template<typename ChecksFunctor>
    2949 bool ByteCodeParser::handleIntrinsicGetter(int resultOperand, const GetByIdVariant& variant, Node* thisNode, const ChecksFunctor& insertChecks)
     2967bool ByteCodeParser::handleIntrinsicGetter(int resultOperand, SpeculatedType prediction, const GetByIdVariant& variant, Node* thisNode, const ChecksFunctor& insertChecks)
    29502968{
    29512969    switch (variant.intrinsic()) {
     
    30113029        set(VirtualRegister(resultOperand), addToGraph(GetTypedArrayByteOffset, OpInfo(ArrayMode(arrayType).asWord()), thisNode));
    30123030
     3031        return true;
     3032    }
     3033
     3034    case UnderscoreProtoIntrinsic: {
     3035        insertChecks();
     3036
     3037        bool canFold = !variant.structureSet().isEmpty();
     3038        JSValue prototype;
     3039        variant.structureSet().forEach([&] (Structure* structure) {
     3040            auto getPrototypeMethod = structure->classInfo()->methodTable.getPrototype;
     3041            MethodTable::GetPrototypeFunctionPtr defaultGetPrototype = JSObject::getPrototype;
     3042            if (getPrototypeMethod != defaultGetPrototype) {
     3043                canFold = false;
     3044                return;
     3045            }
     3046
     3047            if (structure->hasPolyProto()) {
     3048                canFold = false;
     3049                return;
     3050            }
     3051            if (!prototype)
     3052                prototype = structure->storedPrototype();
     3053            else if (prototype != structure->storedPrototype())
     3054                canFold = false;
     3055        });
     3056
     3057        // OK, only one prototype is found. We perform constant folding here.
     3058        // This information is important for super's constructor call to get new.target constant.
     3059        if (prototype && canFold) {
     3060            set(VirtualRegister(resultOperand), weakJSConstant(prototype));
     3061            return true;
     3062        }
     3063
     3064        set(VirtualRegister(resultOperand), addToGraph(GetPrototypeOf, OpInfo(0), OpInfo(prediction), thisNode));
    30133065        return true;
    30143066    }
     
    37973849    Node* getter = addToGraph(GetGetter, loadedValue);
    37983850
    3799     if (handleIntrinsicGetter(destinationOperand, variant, base,
     3851    if (handleIntrinsicGetter(destinationOperand, prediction, variant, base,
    38003852            [&] () {
    38013853                addToGraph(CheckCell, OpInfo(m_graph.freeze(variant.intrinsicFunction())), getter);
Note: See TracChangeset for help on using the changeset viewer.