Ignore:
Timestamp:
May 30, 2020, 8:20:40 PM (5 years ago)
Author:
[email protected]
Message:

[JSC] for-in should allocate new temporary register for base
https://bugs.webkit.org/show_bug.cgi?id=212519
<rdar://problem/63722044>

Reviewed by Saam Barati.

JSTests:

  • microbenchmarks/has-own-property-for-in-loop-with-heap-variable.js: Added.

(assert):
(test1.count):
(test1):

  • microbenchmarks/has-own-property-for-in-loop-with-this.js: Added.

(assert):
(test1.count):
(test1):

  • stress/for-in-body-replace-enumerable.js: Added.

(foo):

  • stress/for-in-enumerable-shadow.js: Added.

(assert):
(test1.count):
(test1):

  • stress/for-in-enumerable-this-arrow.js: Added.

(assert):
(test1):

Source/JavaScriptCore:

While r262233 keeps for-in's enumerated object in variable register if possible to use this register for heuristics driving an optimization,
for-in body can replace the content of this register during enumeration and confuse enumerator.

Instead, we record Variable information in StructureForInContext. This allows us to detect patterns using heap-variables too.
Further, this patch extends pattern-matching code to support ThisNode too.

  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::pushStructureForInScope):

  • bytecompiler/BytecodeGenerator.h:

(JSC::Variable::Variable):
(JSC::Variable::isResolved const):
(JSC::Variable::symbolTableConstantIndex const):
(JSC::Variable::ident const):
(JSC::Variable::offset const):
(JSC::Variable::isLocal const):
(JSC::Variable::local const):
(JSC::Variable::isReadOnly const):
(JSC::Variable::isSpecial const):
(JSC::Variable::isConst const):
(JSC::Variable::setIsReadOnly):
(JSC::Variable::operator== const):
(JSC::StructureForInContext::StructureForInContext):
(JSC::StructureForInContext::baseVariable const):
(JSC::StructureForInContext::base const): Deleted.

  • bytecompiler/NodesCodegen.cpp:

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

  • parser/ASTBuilder.h:

(JSC::ASTBuilder::makeFunctionCallNode):

  • parser/Nodes.h:

(JSC::ExpressionNode::isThisNode const):

File:
1 edited

Legend:

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

    r262233 r262354  
    18531853    }
    18541854
    1855     if (structureContext && structureContext->base() == base.get()) {
     1855    auto canUseFastHasOwnProperty = [&] {
     1856        if (!structureContext)
     1857            return false;
     1858        if (!structureContext->baseVariable())
     1859            return false;
     1860        if (m_base->isResolveNode())
     1861            return generator.variable(static_cast<ResolveNode*>(m_base)->identifier()) == structureContext->baseVariable().value();
     1862        if (m_base->isThisNode()) {
     1863            // After generator.ensureThis (which must be invoked in |base|'s materialization), we can ensure that |this| is in local this-register.
     1864            ASSERT(base);
     1865            return generator.variable(generator.propertyNames().thisIdentifier, ThisResolutionType::Local) == structureContext->baseVariable().value();
     1866        }
     1867        return false;
     1868    };
     1869
     1870    if (canUseFastHasOwnProperty()) {
     1871        // It is possible that base register is variable and each for-in body replaces JS object in the base register with a different one.
     1872        // Even though, this is OK since HasOwnStructureProperty will reject the replaced JS object.
    18561873        Ref<Label> realCall = generator.newLabel();
    18571874        Ref<Label> end = generator.newLabel();
     
    37483765        generator.emitNode(generator.ignoredResult(), m_lexpr);
    37493766
     3767    RefPtr<RegisterID> base = generator.newTemporary();
    37503768    RefPtr<RegisterID> length;
    37513769    RefPtr<RegisterID> enumerator;
    37523770
    3753     RefPtr<RegisterID> base = generator.emitNode(m_expr);
     3771    generator.emitNode(base.get(), m_expr);
    37543772    RefPtr<RegisterID> local = this->tryGetBoundLocal(generator);
    37553773    RefPtr<RegisterID> enumeratorIndex;
     3774
     3775    Optional<Variable> baseVariable;
     3776    if (m_expr->isResolveNode())
     3777        baseVariable = generator.variable(static_cast<ResolveNode*>(m_expr)->identifier());
     3778    else if (m_expr->isThisNode()) {
     3779        // After generator.ensureThis (which must be invoked in |base|'s materialization), we can ensure that |this| is in local this-register.
     3780        ASSERT(base);
     3781        baseVariable = generator.variable(generator.propertyNames().thisIdentifier, ThisResolutionType::Local);
     3782    }
    37563783
    37573784    // Pause at the assignment expression for each for..in iteration.
     
    38293856        generator.emitProfileControlFlow(profilerStartOffset);
    38303857
    3831         generator.pushStructureForInScope(local.get(), enumeratorIndex.get(), propertyName.get(), enumerator.get(), base.get());
     3858        generator.pushStructureForInScope(local.get(), enumeratorIndex.get(), propertyName.get(), enumerator.get(), baseVariable);
    38323859        generator.emitNode(dst, m_statement);
    38333860        generator.popStructureForInScope(local.get());
Note: See TracChangeset for help on using the changeset viewer.