Ignore:
Timestamp:
Jan 2, 2021, 12:46:24 PM (4 years ago)
Author:
[email protected]
Message:

[JSC] Remove unnecessary mov bytecodes when performing simple object pattern destructuring to variables
https://bugs.webkit.org/show_bug.cgi?id=220219

Reviewed by Alexey Shvayka.

JSTests:

  • stress/object-pattern-simple-fast-path.js: Added.

(shouldBe):
(shouldThrow):
(test1):

Source/JavaScriptCore:

Currently, we are first puts object pattern's expression into temporary variable, and then, we store it into local variable register.

The following code

({ data } = object);

emits this kind of bytecode.

get_by_id dst:loc10, base:loc9, property:0
mov dst:loc6, src:loc10

However, this should be

get_by_id dst:loc6, base:loc9, property:0

We are emitting many unnecessary movs since this destructuring pattern is common. Increasing amount of mov (1) discourages inlining unnecessarily and (2) simply makes
bytecode memory large. Since this is very common pattern, we should carefully optimize it to remove such unnecessary movs.

This patch looks into pattern when performing object pattern destructuring. And avoid emitting mov when it is possible. There are some cases we cannot remove movs, so
this patch's writableDirectBindingIfPossible looks into whether this is possible (& profitable).

  • bytecompiler/NodesCodegen.cpp:

(JSC::ObjectPatternNode::bindValue const):
(JSC::BindingNode::writableDirectBindingIfPossible const):
(JSC::BindingNode::finishDirectBindingAssignment const):
(JSC::AssignmentElementNode::writableDirectBindingIfPossible const):
(JSC::AssignmentElementNode::finishDirectBindingAssignment const):

  • parser/Nodes.h:

(JSC::DestructuringPatternNode::writableDirectBindingIfPossible const):
(JSC::DestructuringPatternNode::finishDirectBindingAssignment const):

File:
1 edited

Legend:

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

    r270874 r271121  
    52655265        const auto& target = m_targetPatterns[i];
    52665266        if (target.bindingType == BindingType::Element) {
    5267             RefPtr<RegisterID> temp = generator.newTemporary();
     5267            // If the destructuring becomes get_by_id and mov, then we should store results directly to the local's binding.
     5268            // From
     5269            //     get_by_id          dst:loc10, base:loc9, property:0
     5270            //     mov                dst:loc6, src:loc10
     5271            // To
     5272            //     get_by_id          dst:loc6, base:loc9, property:0
     5273            auto writableDirectBindingIfPossible = [&]() -> RegisterID* {
     5274                // The following pattern is possible. In that case, after setting |data| local variable, we need to store property name into the set.
     5275                // So, old property name |data| result must be kept before setting it into |data|.
     5276                //     ({ [data]: data, ...obj } = object);
     5277                if (m_containsRestElement && m_containsComputedProperty && target.propertyExpression)
     5278                    return nullptr;
     5279                // default value can include a reference to local variable. So filling value to a local variable can differ result.
     5280                // We give up fast path if default value includes non constant.
     5281                // For example,
     5282                //     ({ data = data } = object);
     5283                if (target.defaultValue && !target.defaultValue->isConstant())
     5284                    return nullptr;
     5285                return target.pattern->writableDirectBindingIfPossible(generator);
     5286            };
     5287
     5288            auto finishDirectBindingAssignment = [&]() {
     5289                ASSERT(writableDirectBindingIfPossible());
     5290                target.pattern->finishDirectBindingAssignment(generator);
     5291            };
     5292
     5293            RefPtr<RegisterID> temp;
     5294            RegisterID* directBinding = writableDirectBindingIfPossible();
     5295            if (directBinding)
     5296                temp = directBinding;
     5297            else
     5298                temp = generator.newTemporary();
     5299
    52685300            RefPtr<RegisterID> propertyName;
    52695301            if (!target.propertyExpression) {
     
    52975329            if (target.defaultValue)
    52985330                assignDefaultValueIfUndefined(generator, temp.get(), target.defaultValue);
    5299             target.pattern->bindValue(generator, temp.get());
     5331            if (directBinding)
     5332                finishDirectBindingAssignment();
     5333            else
     5334                target.pattern->bindValue(generator, temp.get());
    53005335        } else {
    53015336            ASSERT(target.bindingType == BindingType::RestElement);
     
    53325367}
    53335368
     5369RegisterID* BindingNode::writableDirectBindingIfPossible(BytecodeGenerator& generator) const
     5370{
     5371    Variable var = generator.variable(m_boundProperty);
     5372    bool isReadOnly = var.isReadOnly() && m_bindingContext != AssignmentContext::ConstDeclarationStatement;
     5373    if (RegisterID* local = var.local()) {
     5374        if (m_bindingContext == AssignmentContext::AssignmentExpression) {
     5375            if (generator.needsTDZCheck(var))
     5376                return nullptr;
     5377        }
     5378        if (isReadOnly)
     5379            return nullptr;
     5380        return local;
     5381    }
     5382    return nullptr;
     5383}
     5384
     5385void BindingNode::finishDirectBindingAssignment(BytecodeGenerator& generator) const
     5386{
     5387    ASSERT(writableDirectBindingIfPossible(generator));
     5388    Variable var = generator.variable(m_boundProperty);
     5389    RegisterID* local = var.local();
     5390    generator.emitProfileType(local, var, divotStart(), divotEnd());
     5391    if (m_bindingContext == AssignmentContext::DeclarationStatement || m_bindingContext == AssignmentContext::ConstDeclarationStatement)
     5392        generator.liftTDZCheckIfPossible(var);
     5393}
     5394
    53345395void BindingNode::bindValue(BytecodeGenerator& generator, RegisterID* value) const
    53355396{
     
    53745435{
    53755436    identifiers.append(m_boundProperty);
     5437}
     5438
     5439RegisterID* AssignmentElementNode::writableDirectBindingIfPossible(BytecodeGenerator& generator) const
     5440{
     5441    if (!m_assignmentTarget->isResolveNode())
     5442        return nullptr;
     5443    ResolveNode* lhs = static_cast<ResolveNode*>(m_assignmentTarget);
     5444    Variable var = generator.variable(lhs->identifier());
     5445    bool isReadOnly = var.isReadOnly();
     5446    if (RegisterID* local = var.local()) {
     5447        if (generator.needsTDZCheck(var))
     5448            return nullptr;
     5449        if (isReadOnly)
     5450            return nullptr;
     5451        return local;
     5452    }
     5453    return nullptr;
     5454}
     5455
     5456void AssignmentElementNode::finishDirectBindingAssignment(BytecodeGenerator& generator) const
     5457{
     5458    ASSERT_UNUSED(generator, writableDirectBindingIfPossible(generator));
     5459    ResolveNode* lhs = static_cast<ResolveNode*>(m_assignmentTarget);
     5460    Variable var = generator.variable(lhs->identifier());
     5461    RegisterID* local = var.local();
     5462    generator.emitProfileType(local, divotStart(), divotEnd());
    53765463}
    53775464
Note: See TracChangeset for help on using the changeset viewer.