Ignore:
Timestamp:
May 27, 2020, 7:43:25 PM (5 years ago)
Author:
[email protected]
Message:

hasOwnProperty inside structure property for-in loop should use an opcode like has_structure_property but for hasOwnProperty
https://bugs.webkit.org/show_bug.cgi?id=212248

Reviewed by Keith Miller.

JSTests:

  • microbenchmarks/has-own-property-for-in-loop.js: Added.
  • stress/has-own-property-structure-for-in-loop-correctness.js: Added.

Source/JavaScriptCore:

This patch applies the same principles from r262083 but to hasOwnProperty.

In this patch, we have a fast path for this syntactic pattern when
iterating structure properties:

for (let <p> in <o>)

if (<o>.hasOwnProperty(<p>))

We look for both <p> and <o> as resolve nodes, and we look for them being the
same values both in the header and inside the body.

Using a simple static analysis, when we detect this pattern, we compare the
result of <o>.hasOwnProperty to the original hasOwnProperty function. If
it's the same, we execute the fast path new bytecode has_own_structure_property,
which on the fast path is two loads, a compare and branch, and a materialization of
the boolean constant true.

On the slow path, has_own_structure_property just executes the runtime code
for hasOwnProperty.

In my testing, this seems like it might be 3-5% faster on Speedometer 2's
react subtests. I was getting some noise when running the tests locally,
so I can't say for certain it's a definite speedup. But the data implies
it has a good chance at being a speedup.

  • builtins/BuiltinNames.h:
  • bytecode/BytecodeList.rb:
  • bytecode/BytecodeUseDef.cpp:

(JSC::computeUsesForBytecodeIndexImpl):
(JSC::computeDefsForBytecodeIndexImpl):

  • bytecode/LinkTimeConstant.h:
  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::emitWideJumpIfNotFunctionHasOwnProperty):
(JSC::BytecodeGenerator::recordHasOwnStructurePropertyInForInLoop):
(JSC::BytecodeGenerator::emitHasOwnStructureProperty):
(JSC::BytecodeGenerator::pushStructureForInScope):
(JSC::StructureForInContext::finalize):
(JSC::BytecodeGenerator::findStructureForInContext):

  • bytecompiler/BytecodeGenerator.h:

(JSC::StructureForInContext::StructureForInContext):
(JSC::StructureForInContext::base const):
(JSC::StructureForInContext::addHasOwnPropertyJump):

  • bytecompiler/Label.h:

(JSC::GenericBoundLabel::GenericBoundLabel):

  • bytecompiler/NodesCodegen.cpp:

(JSC::HasOwnPropertyFunctionCallDotNode::emitBytecode):
(JSC::ForInNode::emitBytecode):

  • dfg/DFGAbstractInterpreterInlines.h:

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

  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::parseBlock):

  • dfg/DFGCapabilities.cpp:

(JSC::DFG::capabilityLevel):

  • 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::compileHasStructureProperty):
(JSC::DFG::SpeculativeJIT::compileHasOwnStructurePropertyImpl):
(JSC::DFG::SpeculativeJIT::compileHasOwnStructureProperty):
(JSC::DFG::SpeculativeJIT::compileInStructureProperty):

  • 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::compileHasOwnStructureProperty):

  • jit/JIT.cpp:

(JSC::JIT::privateCompileMainPass):
(JSC::JIT::privateCompileSlowCases):

  • jit/JIT.h:
  • jit/JITOpcodes.cpp:

(JSC::JIT::emit_op_has_structure_propertyImpl):
(JSC::JIT::emit_op_has_structure_property):
(JSC::JIT::emit_op_has_own_structure_property):
(JSC::JIT::emit_op_in_structure_property):

  • jit/JITOpcodes32_64.cpp:

(JSC::JIT::emit_op_has_structure_propertyImpl):
(JSC::JIT::emit_op_has_structure_property):
(JSC::JIT::emit_op_has_own_structure_property):
(JSC::JIT::emit_op_in_structure_property):

  • llint/LowLevelInterpreter.asm:
  • llint/LowLevelInterpreter64.asm:
  • parser/ASTBuilder.h:

(JSC::ASTBuilder::makeFunctionCallNode):

  • parser/NodeConstructors.h:

(JSC::HasOwnPropertyFunctionCallDotNode::HasOwnPropertyFunctionCallDotNode):

  • parser/Nodes.h:
  • runtime/CommonSlowPaths.cpp:

(JSC::SLOW_PATH_DECL):

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

(JSC::JSGlobalObject::init):

  • runtime/ObjectPrototype.cpp:

(JSC::objectPrototypeHasOwnProperty):
(JSC::objectProtoFuncHasOwnProperty):

  • runtime/ObjectPrototype.h:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp

    r261895 r262233  
    18251825        generator.emitLabel(end.get());
    18261826    }
     1827    generator.emitProfileType(returnValue.get(), divotStart(), divotEnd());
     1828    return returnValue.get();
     1829}
     1830
     1831RegisterID* HasOwnPropertyFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
     1832{
     1833    RefPtr<RegisterID> returnValue = generator.finalDestination(dst);
     1834    RefPtr<RegisterID> base = generator.emitNode(m_base);
     1835
     1836    if (m_base->isOptionalChainBase())
     1837        generator.emitOptionalCheck(base.get());
     1838
     1839    generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd());
     1840
     1841    RefPtr<RegisterID> function = generator.emitGetById(generator.newTemporary(), base.get(), generator.propertyNames().hasOwnProperty);
     1842    if (isOptionalChainBase())
     1843        generator.emitOptionalCheck(function.get());
     1844
     1845    RELEASE_ASSERT(m_args->m_listNode && m_args->m_listNode->m_expr && !m_args->m_listNode->m_next); 
     1846    ExpressionNode* argument = m_args->m_listNode->m_expr;
     1847    RELEASE_ASSERT(argument->isResolveNode());
     1848    StructureForInContext* structureContext = nullptr;
     1849    Variable argumentVariable = generator.variable(static_cast<ResolveNode*>(argument)->identifier());
     1850    if (argumentVariable.isLocal()) {
     1851        RegisterID* property = argumentVariable.local();
     1852        structureContext = generator.findStructureForInContext(property);
     1853    }
     1854
     1855    if (structureContext && structureContext->base() == base.get()) {
     1856        Ref<Label> realCall = generator.newLabel();
     1857        Ref<Label> end = generator.newLabel();
     1858
     1859        unsigned branchInsnOffset = generator.emitWideJumpIfNotFunctionHasOwnProperty(function.get(), realCall.get());
     1860        generator.emitHasOwnStructureProperty(returnValue.get(), base.get(), generator.emitNode(argument), structureContext->enumerator());
     1861        generator.emitJump(end.get());
     1862
     1863        generator.emitLabel(realCall.get());
     1864        {
     1865            CallArguments callArguments(generator, m_args);
     1866            generator.move(callArguments.thisRegister(), base.get());
     1867            generator.emitCallInTailPosition(returnValue.get(), function.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd(), DebuggableCall::Yes);
     1868        }
     1869
     1870        generator.emitLabel(end.get());
     1871
     1872        generator.recordHasOwnStructurePropertyInForInLoop(*structureContext, branchInsnOffset, realCall);
     1873    } else {
     1874        CallArguments callArguments(generator, m_args);
     1875        generator.move(callArguments.thisRegister(), base.get());
     1876        generator.emitCallInTailPosition(returnValue.get(), function.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd(), DebuggableCall::Yes);
     1877    }
     1878
    18271879    generator.emitProfileType(returnValue.get(), divotStart(), divotEnd());
    18281880    return returnValue.get();
     
    36963748        generator.emitNode(generator.ignoredResult(), m_lexpr);
    36973749
    3698     RefPtr<RegisterID> base = generator.newTemporary();
    36993750    RefPtr<RegisterID> length;
    37003751    RefPtr<RegisterID> enumerator;
    37013752
    3702     generator.emitNode(base.get(), m_expr);
     3753    RefPtr<RegisterID> base = generator.emitNode(m_expr);
    37033754    RefPtr<RegisterID> local = this->tryGetBoundLocal(generator);
    37043755    RefPtr<RegisterID> enumeratorIndex;
     
    37783829        generator.emitProfileControlFlow(profilerStartOffset);
    37793830
    3780         generator.pushStructureForInScope(local.get(), enumeratorIndex.get(), propertyName.get(), enumerator.get());
     3831        generator.pushStructureForInScope(local.get(), enumeratorIndex.get(), propertyName.get(), enumerator.get(), base.get());
    37813832        generator.emitNode(dst, m_statement);
    37823833        generator.popStructureForInScope(local.get());
Note: See TracChangeset for help on using the changeset viewer.