Ignore:
Timestamp:
Oct 5, 2016, 10:20:10 PM (9 years ago)
Author:
Yusuke Suzuki
Message:

[DOMJIT] Add initial CheckDOM and CallDOM implementations
https://bugs.webkit.org/show_bug.cgi?id=162941

Reviewed by Filip Pizlo.

JSTests:

  • stress/domjit-getter-poly.js: Added.

(shouldBe):
(access):

  • stress/domjit-getter-proto.js: Added.

(shouldBe):
(access):

  • stress/domjit-getter-super-poly.js: Added.

(shouldBe):
(access):

  • stress/domjit-getter.js: Added.

(shouldBe):
(access):

Source/JavaScriptCore:

This patch implements a prototype of DOMJIT accelerated getter.
We add two new DFG nodes, CheckDOM and CallDOM.

CheckDOM is used to filter inappropriate |this| object for DOM getter. Its functionality
is equivalent to jsDynamicCast's Check. You can use like "CheckDOM, @1, JSNode::info()",
and this CheckDOM incurs a BadType exit if the class of the given @1 is not a subclass of
JSNode::info().

CallDOM is used to emit actual DOM operations. It takes GlobalObject and checked DOM
object. And it returns JSValue as its result.

Both CheckDOM and CallDOM can take a DOMJIT::Patchpoint. This is somewhat code snippet
generator, and is injectable to DFG and FTL. DFG and FTL set up registers correctly
according to DOMJIT::Patchpoint's requirement and invoke this patchpoint generator to emit code.
While CallDOM always requires a patchpoint, ideally CheckDOM does not require it since
isSubclassOf check can be implemented in DFG / FTL side. However, some classes have a
faster way to query isSubclassOf. For example, JSNode in WebCore introduces a special
JSType to optimize this query. CheckDOM's patchpoint gives us a chance to emit special
faster code for such a case.

By leveraging above nodes, we can construct DOMJIT accelerated getter. When DFG recognizes the
given getter call is CustomGetter and it has DOMJIT::GetterSetter information, DFG emits the above nodes.
We implemented a prototype in jsc.cpp shell as DOMJITGetter to test the functionality.

Notes about the future extensions.

  1. Currently, we do not allow CallDOM to emit any function calls. This will be extended by adding addSlowPathCall functionality to DOMJIT::Patchpoint later. Interesting thing is that we need to create an abstraction over DFG slow path call and FTL slow path call!
  1. CheckDOM is not handled in DFGTypeCheckHoistingPhase yet. And we have a chance to merge several CheckDOM into one. For example, given CheckDOM A and CheckDOM B to the same target. If A is subclass of B, we can merge them to CheckDOM A.

(JSC::B3::Effects::forCheck):

  • b3/B3Value.cpp:

(JSC::B3::Value::effects):

  • bytecode/GetByIdStatus.cpp:

(JSC::GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback):
(JSC::GetByIdStatus::makesCalls):
(JSC::GetByIdStatus::dump):

  • bytecode/GetByIdStatus.h:

(JSC::GetByIdStatus::GetByIdStatus):
(JSC::GetByIdStatus::isCustom):
(JSC::GetByIdStatus::takesSlowPath):
(JSC::GetByIdStatus::isSimple): Deleted.

  • bytecode/SpeculatedType.cpp:

(JSC::speculationFromClassInfo):

  • dfg/DFGAbstractInterpreter.h:

(JSC::DFG::AbstractInterpreter::filterClassInfo):

  • dfg/DFGAbstractInterpreterInlines.h:

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

  • dfg/DFGAbstractValue.cpp:

(JSC::DFG::AbstractValue::filterClassInfo):

  • dfg/DFGAbstractValue.h:
  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::handleDOMJITGetter):
(JSC::DFG::ByteCodeParser::handleGetById):

  • dfg/DFGClobberize.h:

(JSC::DFG::clobberize):

  • dfg/DFGConstantFoldingPhase.cpp:

(JSC::DFG::ConstantFoldingPhase::foldConstants):

  • dfg/DFGDoesGC.cpp:

(JSC::DFG::doesGC):

  • dfg/DFGFixupPhase.cpp:

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

  • dfg/DFGNode.h:

(JSC::DFG::Node::hasHeapPrediction):
(JSC::DFG::Node::hasDOMJIT):
(JSC::DFG::Node::domJIT):
(JSC::DFG::Node::hasClassInfo):
(JSC::DFG::Node::classInfo):
(JSC::DFG::Node::OpInfoWrapper::OpInfoWrapper):
(JSC::DFG::Node::OpInfoWrapper::operator=):

  • dfg/DFGNodeType.h:
  • dfg/DFGOpInfo.h:

(JSC::DFG::OpInfo::OpInfo):

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

(JSC::DFG::safeToExecute):

  • dfg/DFGSpeculativeJIT.cpp:

(JSC::DFG::allocateTemporaryRegistersForPatchpoint):
(JSC::DFG::SpeculativeJIT::compileCallDOM):
(JSC::DFG::SpeculativeJIT::compileCheckDOM):

  • dfg/DFGSpeculativeJIT.h:
  • dfg/DFGSpeculativeJIT32_64.cpp:

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

  • dfg/DFGSpeculativeJIT64.cpp:

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

  • dfg/DFGStructureAbstractValue.cpp:

(JSC::DFG::StructureAbstractValue::filterClassInfoSlow):
(JSC::DFG::StructureAbstractValue::isSubClassOf):

  • dfg/DFGStructureAbstractValue.h:

(JSC::DFG::StructureAbstractValue::filterClassInfo):
(JSC::DFG::StructureAbstractValue::filter): Deleted.

  • domjit/DOMJITPatchpointParams.h: Copied from Source/JavaScriptCore/dfg/DFGOpInfo.h.

(JSC::DOMJIT::PatchpointParams::~PatchpointParams):
(JSC::DOMJIT::PatchpointParams::size):
(JSC::DOMJIT::PatchpointParams::at):
(JSC::DOMJIT::PatchpointParams::operator[]):
(JSC::DOMJIT::PatchpointParams::gpScratch):
(JSC::DOMJIT::PatchpointParams::fpScratch):
(JSC::DOMJIT::PatchpointParams::PatchpointParams):

  • domjit/DOMJITReg.h: Added.

(JSC::DOMJIT::Reg::Reg):
(JSC::DOMJIT::Reg::isGPR):
(JSC::DOMJIT::Reg::isFPR):
(JSC::DOMJIT::Reg::isJSValueRegs):
(JSC::DOMJIT::Reg::gpr):
(JSC::DOMJIT::Reg::fpr):
(JSC::DOMJIT::Reg::jsValueRegs):

  • ftl/FTLCapabilities.cpp:

(JSC::FTL::canCompile):

  • ftl/FTLLowerDFGToB3.cpp:

(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileCheckDOM):
(JSC::FTL::DFG::LowerDFGToB3::compileCallDOM):
(JSC::FTL::DFG::LowerDFGToB3::compileUnreachable): Deleted.

  • jit/AssemblyHelpers.h:
  • jsc.cpp:

(WTF::DOMJITNode::DOMJITNode):
(WTF::DOMJITNode::createStructure):
(WTF::DOMJITNode::create):
(WTF::DOMJITNode::value):
(WTF::DOMJITNode::offsetOfValue):
(WTF::DOMJITGetter::DOMJITGetter):
(WTF::DOMJITGetter::createStructure):
(WTF::DOMJITGetter::create):
(WTF::DOMJITGetter::DOMJITNodeDOMJIT::DOMJITNodeDOMJIT):
(WTF::DOMJITGetter::domJITNodeGetterSetter):
(WTF::DOMJITGetter::finishCreation):
(WTF::DOMJITGetter::customGetter):
(GlobalObject::finishCreation):
(functionCreateDOMJITNodeObject):
(functionCreateDOMJITGetterObject):

Source/WTF:

  • wtf/Box.h:

(WTF::Box::Box):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/jsc.cpp

    r206653 r206846  
    2929#include "CodeBlock.h"
    3030#include "Completion.h"
     31#include "DOMJITGetterSetter.h"
     32#include "DOMJITPatchpoint.h"
     33#include "DOMJITPatchpointParams.h"
    3134#include "Disassembler.h"
    3235#include "Exception.h"
     
    7376#include <wtf/CurrentTime.h>
    7477#include <wtf/MainThread.h>
     78#include <wtf/NeverDestroyed.h>
    7579#include <wtf/StringPrintStream.h>
    7680#include <wtf/text/StringBuilder.h>
     
    538542};
    539543
     544class DOMJITNode : public JSNonFinalObject {
     545public:
     546    DOMJITNode(VM& vm, Structure* structure)
     547        : Base(vm, structure)
     548    {
     549    }
     550
     551    DECLARE_INFO;
     552    typedef JSNonFinalObject Base;
     553    static const unsigned StructureFlags = Base::StructureFlags;
     554
     555    static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
     556    {
     557        return Structure::create(vm, globalObject, prototype, TypeInfo(JSC::JSType(LastJSCObjectType + 1), StructureFlags), info());
     558    }
     559
     560    static DOMJITNode* create(VM& vm, Structure* structure)
     561    {
     562        DOMJITNode* getter = new (NotNull, allocateCell<DOMJITNode>(vm.heap, sizeof(DOMJITNode))) DOMJITNode(vm, structure);
     563        getter->finishCreation(vm);
     564        return getter;
     565    }
     566
     567    int32_t value() const
     568    {
     569        return m_value;
     570    }
     571
     572    static ptrdiff_t offsetOfValue() { return OBJECT_OFFSETOF(DOMJITNode, m_value); }
     573
     574private:
     575    int32_t m_value { 42 };
     576};
     577
     578class DOMJITGetter : public DOMJITNode {
     579public:
     580    DOMJITGetter(VM& vm, Structure* structure)
     581        : Base(vm, structure)
     582    {
     583    }
     584
     585    DECLARE_INFO;
     586    typedef DOMJITNode Base;
     587    static const unsigned StructureFlags = Base::StructureFlags;
     588
     589    static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
     590    {
     591        return Structure::create(vm, globalObject, prototype, TypeInfo(JSC::JSType(LastJSCObjectType + 1), StructureFlags), info());
     592    }
     593
     594    static DOMJITGetter* create(VM& vm, Structure* structure)
     595    {
     596        DOMJITGetter* getter = new (NotNull, allocateCell<DOMJITGetter>(vm.heap, sizeof(DOMJITGetter))) DOMJITGetter(vm, structure);
     597        getter->finishCreation(vm);
     598        return getter;
     599    }
     600
     601    class DOMJITNodeDOMJIT : public DOMJIT::GetterSetter {
     602    public:
     603        DOMJITNodeDOMJIT()
     604            : DOMJIT::GetterSetter(DOMJITGetter::customGetter, nullptr, DOMJITNode::info())
     605        {
     606        }
     607
     608        Ref<DOMJIT::Patchpoint> checkDOM() override
     609        {
     610            Ref<DOMJIT::Patchpoint> patchpoint = DOMJIT::Patchpoint::create();
     611            patchpoint->setGenerator([=](CCallHelpers& jit, const DOMJIT::PatchpointParams& params) {
     612                CCallHelpers::JumpList failureCases;
     613                failureCases.append(jit.branch8(
     614                    CCallHelpers::NotEqual,
     615                    CCallHelpers::Address(params[0].gpr(), JSCell::typeInfoTypeOffset()),
     616                    CCallHelpers::TrustedImm32(JSC::JSType(LastJSCObjectType + 1))));
     617                return failureCases;
     618            });
     619            return patchpoint;
     620        }
     621
     622        Ref<DOMJIT::Patchpoint> callDOM() override
     623        {
     624            Ref<DOMJIT::Patchpoint> patchpoint = DOMJIT::Patchpoint::create();
     625            patchpoint->setGenerator([=](CCallHelpers& jit, const DOMJIT::PatchpointParams& params) {
     626                JSValueRegs results = params[0].jsValueRegs();
     627                GPRReg dom = params[2].gpr();
     628
     629                jit.load32(CCallHelpers::Address(dom, DOMJITNode::offsetOfValue()), results.payloadGPR());
     630                jit.boxInt32(results.payloadGPR(), results);
     631                return CCallHelpers::JumpList();
     632            });
     633            return patchpoint;
     634        }
     635    };
     636
     637    static DOMJIT::GetterSetter* domJITNodeGetterSetter()
     638    {
     639        static NeverDestroyed<DOMJITNodeDOMJIT> graph;
     640        return &graph.get();
     641    }
     642
     643private:
     644    void finishCreation(VM& vm)
     645    {
     646        Base::finishCreation(vm);
     647        DOMJIT::GetterSetter* domJIT = domJITNodeGetterSetter();
     648        CustomGetterSetter* customGetterSetter = CustomGetterSetter::create(vm, domJIT->getter(), domJIT->setter(), domJIT);
     649        putDirectCustomAccessor(vm, Identifier::fromString(&vm, "customGetter"), customGetterSetter, ReadOnly | CustomAccessor);
     650    }
     651
     652    static EncodedJSValue customGetter(ExecState* exec, EncodedJSValue thisValue, PropertyName)
     653    {
     654        VM& vm = exec->vm();
     655        auto scope = DECLARE_THROW_SCOPE(vm);
     656
     657        DOMJITNode* thisObject = jsDynamicCast<DOMJITNode*>(JSValue::decode(thisValue));
     658        if (!thisObject)
     659            return throwVMTypeError(exec, scope);
     660        return JSValue::encode(jsNumber(thisObject->value()));
     661    }
     662};
    540663
    541664const ClassInfo Element::s_info = { "Element", &Base::s_info, 0, CREATE_METHOD_TABLE(Element) };
     
    544667const ClassInfo ImpureGetter::s_info = { "ImpureGetter", &Base::s_info, 0, CREATE_METHOD_TABLE(ImpureGetter) };
    545668const ClassInfo CustomGetter::s_info = { "CustomGetter", &Base::s_info, 0, CREATE_METHOD_TABLE(CustomGetter) };
     669const ClassInfo DOMJITNode::s_info = { "DOMJITNode", &Base::s_info, 0, CREATE_METHOD_TABLE(DOMJITNode) };
     670const ClassInfo DOMJITGetter::s_info = { "DOMJITGetter", &Base::s_info, 0, CREATE_METHOD_TABLE(DOMJITGetter) };
    546671const ClassInfo RuntimeArray::s_info = { "RuntimeArray", &Base::s_info, 0, CREATE_METHOD_TABLE(RuntimeArray) };
    547672const ClassInfo SimpleObject::s_info = { "SimpleObject", &Base::s_info, 0, CREATE_METHOD_TABLE(SimpleObject) };
     
    572697static EncodedJSValue JSC_HOST_CALL functionCreateImpureGetter(ExecState*);
    573698static EncodedJSValue JSC_HOST_CALL functionCreateCustomGetterObject(ExecState*);
     699static EncodedJSValue JSC_HOST_CALL functionCreateDOMJITNodeObject(ExecState*);
     700static EncodedJSValue JSC_HOST_CALL functionCreateDOMJITGetterObject(ExecState*);
    574701static EncodedJSValue JSC_HOST_CALL functionCreateBuiltin(ExecState*);
    575702static EncodedJSValue JSC_HOST_CALL functionCreateGlobalObject(ExecState*);
     
    856983        addFunction(vm, "createImpureGetter", functionCreateImpureGetter, 1);
    857984        addFunction(vm, "createCustomGetterObject", functionCreateCustomGetterObject, 0);
     985        addFunction(vm, "createDOMJITNodeObject", functionCreateDOMJITNodeObject, 0);
     986        addFunction(vm, "createDOMJITGetterObject", functionCreateDOMJITGetterObject, 0);
    858987        addFunction(vm, "createBuiltin", functionCreateBuiltin, 2);
    859988        addFunction(vm, "createGlobalObject", functionCreateGlobalObject, 0);
     
    13601489}
    13611490
     1491EncodedJSValue JSC_HOST_CALL functionCreateDOMJITNodeObject(ExecState* exec)
     1492{
     1493    JSLockHolder lock(exec);
     1494    Structure* structure = DOMJITNode::createStructure(exec->vm(), exec->lexicalGlobalObject(), DOMJITGetter::create(exec->vm(), DOMJITGetter::createStructure(exec->vm(), exec->lexicalGlobalObject(), jsNull())));
     1495    DOMJITNode* result = DOMJITNode::create(exec->vm(), structure);
     1496    return JSValue::encode(result);
     1497}
     1498
     1499EncodedJSValue JSC_HOST_CALL functionCreateDOMJITGetterObject(ExecState* exec)
     1500{
     1501    JSLockHolder lock(exec);
     1502    Structure* structure = DOMJITGetter::createStructure(exec->vm(), exec->lexicalGlobalObject(), jsNull());
     1503    DOMJITGetter* result = DOMJITGetter::create(exec->vm(), structure);
     1504    return JSValue::encode(result);
     1505}
     1506
    13621507EncodedJSValue JSC_HOST_CALL functionSetImpureGetterDelegate(ExecState* exec)
    13631508{
Note: See TracChangeset for help on using the changeset viewer.