⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 249117 in webkit


Ignore:
Timestamp:
Aug 26, 2019, 2:09:35 PM (7 years ago)
Author:
Ross Kirsling
Message:

[JSC] Ensure x?.y ?? z is fast
https://bugs.webkit.org/show_bug.cgi?id=200875

Reviewed by Yusuke Suzuki.

JSTests:

  • stress/nullish-coalescing.js:

Source/JavaScriptCore:

We anticipate x?.y ?? z to quickly become a common idiom in JS. With a little bytecode rearrangement,
we can avoid the "load undefined and check it" dance in the middle and just turn this into two jumps.

Before:

(get x)

----- jundefined_or_null
| (get y)
| --- jmp

| (load undefined)

  • jnundefined_or_null

| (get z)

end

After:

(get x)

--- jundefined_or_null
| (get y)
| - jnundefined_or_null

| (get z)
end

  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::popOptionalChainTarget): Added specialization.

  • bytecompiler/BytecodeGenerator.h:
  • bytecompiler/NodesCodegen.cpp:

(JSC::CoalesceNode::emitBytecode):
(JSC::OptionalChainNode::emitBytecode):

  • parser/ASTBuilder.h:

(JSC::ASTBuilder::makeDeleteNode):
(JSC::ASTBuilder::makeCoalesceNode): Added.
(JSC::ASTBuilder::makeBinaryNode):

  • parser/NodeConstructors.h:

(JSC::CoalesceNode::CoalesceNode):

  • parser/Nodes.h:

(JSC::ExpressionNode::isDeleteNode const): Added. (Replaces OptionalChainNode::m_isDelete.)

Location:
trunk
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r249075 r249117  
     12019-08-26  Ross Kirsling  <ross.kirsling@sony.com>
     2
     3        [JSC] Ensure x?.y ?? z is fast
     4        https://bugs.webkit.org/show_bug.cgi?id=200875
     5
     6        Reviewed by Yusuke Suzuki.
     7
     8        * stress/nullish-coalescing.js:
     9
    1102019-08-23  Tadeu Zagallo  <tzagallo@apple.com>
    211
  • trunk/JSTests/stress/nullish-coalescing.js

    r248829 r249117  
    9090shouldThrowSyntaxError('0 || 1 && 2 | 3 ^ 4 & 5 == 6 != 7 === 8 !== 9 < 0 > 1 <= 2 >= 3 << 4 >> 5 >>> 6 + 7 - 8 * 9 / 0 % 1 ** 2 ?? 3');
    9191shouldThrowSyntaxError('3 ?? 2 ** 1 % 0 / 9 * 8 - 7 + 6 >>> 5 >> 4 << 3 >= 2 <= 1 > 0 < 9 !== 8 === 7 != 6 == 5 & 4 ^ 3 | 2 && 1 || 0');
     92
     93shouldBe(null?.x ?? 3, 3);
     94shouldBe(({})?.x ?? 3, 3);
     95shouldBe(({ x: 0 })?.x ?? 3, 0);
     96shouldBe(null?.() ?? 3, 3);
     97shouldBe((() => 0)?.() ?? 3, 0);
     98shouldBe(({ x: 0 })?.[null?.a ?? 'x'] ?? 3, 0);
     99shouldBe((() => 0)?.(null?.a ?? 'x') ?? 3, 0);
  • trunk/Source/JavaScriptCore/ChangeLog

    r249095 r249117  
     12019-08-26  Ross Kirsling  <ross.kirsling@sony.com>
     2
     3        [JSC] Ensure x?.y ?? z is fast
     4        https://bugs.webkit.org/show_bug.cgi?id=200875
     5
     6        Reviewed by Yusuke Suzuki.
     7
     8        We anticipate `x?.y ?? z` to quickly become a common idiom in JS. With a little bytecode rearrangement,
     9        we can avoid the "load undefined and check it" dance in the middle and just turn this into two jumps.
     10
     11        Before:
     12                (get x)
     13          ----- jundefined_or_null
     14          |     (get y)
     15          | --- jmp
     16          > |   (load undefined)
     17            > - jnundefined_or_null
     18              | (get z)
     19              > end
     20
     21        After:
     22                (get x)
     23            --- jundefined_or_null
     24            |   (get y)
     25            | - jnundefined_or_null
     26            > | (get z)
     27              > end
     28
     29        * bytecompiler/BytecodeGenerator.cpp:
     30        (JSC::BytecodeGenerator::popOptionalChainTarget): Added specialization.
     31        * bytecompiler/BytecodeGenerator.h:
     32        * bytecompiler/NodesCodegen.cpp:
     33        (JSC::CoalesceNode::emitBytecode):
     34        (JSC::OptionalChainNode::emitBytecode):
     35        * parser/ASTBuilder.h:
     36        (JSC::ASTBuilder::makeDeleteNode):
     37        (JSC::ASTBuilder::makeCoalesceNode): Added.
     38        (JSC::ASTBuilder::makeBinaryNode):
     39        * parser/NodeConstructors.h:
     40        (JSC::CoalesceNode::CoalesceNode):
     41        * parser/Nodes.h:
     42        (JSC::ExpressionNode::isDeleteNode const): Added. (Replaces OptionalChainNode::m_isDelete.)
     43
    1442019-08-26  Carlos Alberto Lopez Perez  <clopez@igalia.com>
    245
  • trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp

    r248829 r249117  
    49704970}
    49714971
     4972void BytecodeGenerator::popOptionalChainTarget()
     4973{
     4974    ASSERT(m_optionalChainTargetStack.size());
     4975    emitLabel(m_optionalChainTargetStack.takeLast().get());
     4976}
     4977
    49724978void BytecodeGenerator::popOptionalChainTarget(RegisterID* dst, bool isDelete)
    49734979{
    4974     ASSERT(m_optionalChainTargetStack.size());
    4975 
    49764980    Ref<Label> endLabel = newLabel();
    49774981    emitJump(endLabel.get());
    49784982
    4979     emitLabel(m_optionalChainTargetStack.takeLast().get());
     4983    popOptionalChainTarget();
    49804984    emitLoad(dst, isDelete ? jsBoolean(true) : jsUndefined());
    49814985
  • trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h

    r248846 r249117  
    961961
    962962        void pushOptionalChainTarget();
    963         void popOptionalChainTarget(RegisterID* dst, bool isDelete = false);
     963        void popOptionalChainTarget();
     964        void popOptionalChainTarget(RegisterID* dst, bool isDelete);
    964965        void emitOptionalCheck(RegisterID* src);
    965966
  • trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp

    r248829 r249117  
    24092409{
    24102410    RefPtr<RegisterID> temp = generator.tempDestination(dst);
    2411     Ref<Label> target = generator.newLabel();
    2412 
     2411    Ref<Label> endLabel = generator.newLabel();
     2412
     2413    if (m_hasAbsorbedOptionalChain)
     2414        generator.pushOptionalChainTarget();
    24132415    generator.emitNode(temp.get(), m_expr1);
    2414     generator.emitJumpIfFalse(generator.emitIsUndefinedOrNull(generator.newTemporary(), temp.get()), target.get());
     2416    generator.emitJumpIfFalse(generator.emitIsUndefinedOrNull(generator.newTemporary(), temp.get()), endLabel.get());
     2417
     2418    if (m_hasAbsorbedOptionalChain)
     2419        generator.popOptionalChainTarget();
    24152420    generator.emitNodeInTailPosition(temp.get(), m_expr2);
    2416     generator.emitLabel(target.get());
    2417 
     2421
     2422    generator.emitLabel(endLabel.get());
    24182423    return generator.move(dst, temp.get());
    24192424}
     
    24292434    generator.emitNodeInTailPosition(finalDest.get(), m_expr);
    24302435    if (m_isOutermost)
    2431         generator.popOptionalChainTarget(finalDest.get(), m_isDelete);
     2436        generator.popOptionalChainTarget(finalDest.get(), m_expr->isDeleteNode());
    24322437
    24332438    return finalDest.get();
  • trunk/Source/JavaScriptCore/parser/ASTBuilder.h

    r248829 r249117  
    153153    ExpressionNode* makeBitAndNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments);
    154154    ExpressionNode* makeBitOrNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments);
     155    ExpressionNode* makeCoalesceNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right);
    155156    ExpressionNode* makeLeftShiftNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments);
    156157    ExpressionNode* makeRightShiftNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments);
     
    11601161            ASSERT(!optionalChain->expr()->isResolveNode());
    11611162            optionalChain->setExpr(makeDeleteNode(location, optionalChain->expr(), start, divot, end));
    1162             optionalChain->setIsDelete();
    11631163            return optionalChain;
    11641164        }
     
    13601360    }
    13611361    return new (m_parserArena) BitXOrNode(location, expr1, expr2, rightHasAssignments);
     1362}
     1363
     1364ExpressionNode* ASTBuilder::makeCoalesceNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2)
     1365{
     1366    // Optimization for `x?.y ?? z`.
     1367    if (expr1->isOptionalChain()) {
     1368        OptionalChainNode* optionalChain = static_cast<OptionalChainNode*>(expr1);
     1369        if (!optionalChain->expr()->isDeleteNode()) {
     1370            constexpr bool hasAbsorbedOptionalChain = true;
     1371            return new (m_parserArena) CoalesceNode(location, optionalChain->expr(), expr2, hasAbsorbedOptionalChain);
     1372        }
     1373    }
     1374    constexpr bool hasAbsorbedOptionalChain = false;
     1375    return new (m_parserArena) CoalesceNode(location, expr1, expr2, hasAbsorbedOptionalChain);
    13621376}
    13631377
     
    14251439    switch (token) {
    14261440    case COALESCE:
    1427         return new (m_parserArena) CoalesceNode(location, lhs.first, rhs.first);
     1441        return makeCoalesceNode(location, lhs.first, rhs.first);
    14281442
    14291443    case OR:
  • trunk/Source/JavaScriptCore/parser/NodeConstructors.h

    r248829 r249117  
    670670    }
    671671
    672     inline CoalesceNode::CoalesceNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2)
     672    inline CoalesceNode::CoalesceNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool hasAbsorbedOptionalChain)
    673673        : ExpressionNode(location, ResultType::forCoalesce(expr1->resultDescriptor(), expr2->resultDescriptor()))
    674674        , m_expr1(expr1)
    675675        , m_expr2(expr2)
     676        , m_hasAbsorbedOptionalChain(hasAbsorbedOptionalChain)
    676677    {
    677678    }
  • trunk/Source/JavaScriptCore/parser/Nodes.h

    r248829 r249117  
    206206        virtual bool isBinaryOpNode() const { return false; }
    207207        virtual bool isFunctionCall() const { return false; }
     208        virtual bool isDeleteNode() const { return false; }
    208209        virtual bool isOptionalChain() const { return false; }
    209210
     
    995996        RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
    996997
     998        bool isDeleteNode() const final { return true; }
     999
    9971000        const Identifier& m_ident;
    9981001    };
     
    10041007    private:
    10051008        RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
     1009
     1010        bool isDeleteNode() const final { return true; }
    10061011
    10071012        ExpressionNode* m_base;
     
    10161021        RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
    10171022
     1023        bool isDeleteNode() const final { return true; }
     1024
    10181025        ExpressionNode* m_base;
    10191026        const Identifier& m_ident;
     
    10261033    private:
    10271034        RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
     1035
     1036        bool isDeleteNode() const final { return true; }
    10281037
    10291038        ExpressionNode* m_expr;
     
    13131322    class CoalesceNode final : public ExpressionNode {
    13141323    public:
    1315         CoalesceNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2);
     1324        CoalesceNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool);
    13161325
    13171326    private:
     
    13201329        ExpressionNode* m_expr1;
    13211330        ExpressionNode* m_expr2;
     1331        bool m_hasAbsorbedOptionalChain;
    13221332    };
    13231333
     
    13291339        ExpressionNode* expr() const { return m_expr; }
    13301340
    1331         void setIsDelete() { m_isDelete = true; }
    1332 
    13331341    private:
    13341342        RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = nullptr) final;
     
    13381346        ExpressionNode* m_expr;
    13391347        bool m_isOutermost;
    1340         bool m_isDelete { false };
    13411348    };
    13421349
Note: See TracChangeset for help on using the changeset viewer.