Diff
Modified: trunk/JSTests/ChangeLog (249116 => 249117)
--- trunk/JSTests/ChangeLog 2019-08-26 20:42:48 UTC (rev 249116)
+++ trunk/JSTests/ChangeLog 2019-08-26 21:09:35 UTC (rev 249117)
@@ -1,3 +1,12 @@
+2019-08-26 Ross Kirsling <[email protected]>
+
+ [JSC] Ensure x?.y ?? z is fast
+ https://bugs.webkit.org/show_bug.cgi?id=200875
+
+ Reviewed by Yusuke Suzuki.
+
+ * stress/nullish-coalescing.js:
+
2019-08-23 Tadeu Zagallo <[email protected]>
Remove MaximalFlushInsertionPhase
Modified: trunk/JSTests/stress/nullish-coalescing.js (249116 => 249117)
--- trunk/JSTests/stress/nullish-coalescing.js 2019-08-26 20:42:48 UTC (rev 249116)
+++ trunk/JSTests/stress/nullish-coalescing.js 2019-08-26 21:09:35 UTC (rev 249117)
@@ -89,3 +89,11 @@
shouldNotThrow('0 || 1 && 2 | 3 ^ 4 & 5 == 6 != 7 === 8 !== 9 < 0 > 1 <= 2 >= 3 << 4 >> 5 >>> 6 + 7 - 8 * 9 / 0 % 1 ** 2');
shouldThrowSyntaxError('0 || 1 && 2 | 3 ^ 4 & 5 == 6 != 7 === 8 !== 9 < 0 > 1 <= 2 >= 3 << 4 >> 5 >>> 6 + 7 - 8 * 9 / 0 % 1 ** 2 ?? 3');
shouldThrowSyntaxError('3 ?? 2 ** 1 % 0 / 9 * 8 - 7 + 6 >>> 5 >> 4 << 3 >= 2 <= 1 > 0 < 9 !== 8 === 7 != 6 == 5 & 4 ^ 3 | 2 && 1 || 0');
+
+shouldBe(null?.x ?? 3, 3);
+shouldBe(({})?.x ?? 3, 3);
+shouldBe(({ x: 0 })?.x ?? 3, 0);
+shouldBe(null?.() ?? 3, 3);
+shouldBe((() => 0)?.() ?? 3, 0);
+shouldBe(({ x: 0 })?.[null?.a ?? 'x'] ?? 3, 0);
+shouldBe((() => 0)?.(null?.a ?? 'x') ?? 3, 0);
Modified: trunk/Source/_javascript_Core/ChangeLog (249116 => 249117)
--- trunk/Source/_javascript_Core/ChangeLog 2019-08-26 20:42:48 UTC (rev 249116)
+++ trunk/Source/_javascript_Core/ChangeLog 2019-08-26 21:09:35 UTC (rev 249117)
@@ -1,3 +1,46 @@
+2019-08-26 Ross Kirsling <[email protected]>
+
+ [JSC] Ensure x?.y ?? z is fast
+ https://bugs.webkit.org/show_bug.cgi?id=200875
+
+ Reviewed by Yusuke Suzuki.
+
+ 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.)
+
2019-08-26 Carlos Alberto Lopez Perez <[email protected]>
Missing media controls when WebKit is built with Python3
Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp (249116 => 249117)
--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp 2019-08-26 20:42:48 UTC (rev 249116)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp 2019-08-26 21:09:35 UTC (rev 249117)
@@ -4969,14 +4969,18 @@
m_optionalChainTargetStack.append(newLabel());
}
-void BytecodeGenerator::popOptionalChainTarget(RegisterID* dst, bool isDelete)
+void BytecodeGenerator::popOptionalChainTarget()
{
ASSERT(m_optionalChainTargetStack.size());
+ emitLabel(m_optionalChainTargetStack.takeLast().get());
+}
+void BytecodeGenerator::popOptionalChainTarget(RegisterID* dst, bool isDelete)
+{
Ref<Label> endLabel = newLabel();
emitJump(endLabel.get());
- emitLabel(m_optionalChainTargetStack.takeLast().get());
+ popOptionalChainTarget();
emitLoad(dst, isDelete ? jsBoolean(true) : jsUndefined());
emitLabel(endLabel.get());
Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h (249116 => 249117)
--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h 2019-08-26 20:42:48 UTC (rev 249116)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h 2019-08-26 21:09:35 UTC (rev 249117)
@@ -960,7 +960,8 @@
void popFinallyControlFlowScope();
void pushOptionalChainTarget();
- void popOptionalChainTarget(RegisterID* dst, bool isDelete = false);
+ void popOptionalChainTarget();
+ void popOptionalChainTarget(RegisterID* dst, bool isDelete);
void emitOptionalCheck(RegisterID* src);
void pushIndexedForInScope(RegisterID* local, RegisterID* index);
Modified: trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp (249116 => 249117)
--- trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp 2019-08-26 20:42:48 UTC (rev 249116)
+++ trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp 2019-08-26 21:09:35 UTC (rev 249117)
@@ -2408,13 +2408,18 @@
RegisterID* CoalesceNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
{
RefPtr<RegisterID> temp = generator.tempDestination(dst);
- Ref<Label> target = generator.newLabel();
+ Ref<Label> endLabel = generator.newLabel();
+ if (m_hasAbsorbedOptionalChain)
+ generator.pushOptionalChainTarget();
generator.emitNode(temp.get(), m_expr1);
- generator.emitJumpIfFalse(generator.emitIsUndefinedOrNull(generator.newTemporary(), temp.get()), target.get());
+ generator.emitJumpIfFalse(generator.emitIsUndefinedOrNull(generator.newTemporary(), temp.get()), endLabel.get());
+
+ if (m_hasAbsorbedOptionalChain)
+ generator.popOptionalChainTarget();
generator.emitNodeInTailPosition(temp.get(), m_expr2);
- generator.emitLabel(target.get());
+ generator.emitLabel(endLabel.get());
return generator.move(dst, temp.get());
}
@@ -2428,7 +2433,7 @@
generator.pushOptionalChainTarget();
generator.emitNodeInTailPosition(finalDest.get(), m_expr);
if (m_isOutermost)
- generator.popOptionalChainTarget(finalDest.get(), m_isDelete);
+ generator.popOptionalChainTarget(finalDest.get(), m_expr->isDeleteNode());
return finalDest.get();
}
Modified: trunk/Source/_javascript_Core/parser/ASTBuilder.h (249116 => 249117)
--- trunk/Source/_javascript_Core/parser/ASTBuilder.h 2019-08-26 20:42:48 UTC (rev 249116)
+++ trunk/Source/_javascript_Core/parser/ASTBuilder.h 2019-08-26 21:09:35 UTC (rev 249117)
@@ -152,6 +152,7 @@
ExpressionNode* makeBitXOrNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments);
ExpressionNode* makeBitAndNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments);
ExpressionNode* makeBitOrNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments);
+ ExpressionNode* makeCoalesceNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right);
ExpressionNode* makeLeftShiftNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments);
ExpressionNode* makeRightShiftNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments);
ExpressionNode* makeURightShiftNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments);
@@ -1159,7 +1160,6 @@
if (optionalChain->expr()->isLocation()) {
ASSERT(!optionalChain->expr()->isResolveNode());
optionalChain->setExpr(makeDeleteNode(location, optionalChain->expr(), start, divot, end));
- optionalChain->setIsDelete();
return optionalChain;
}
}
@@ -1361,6 +1361,20 @@
return new (m_parserArena) BitXOrNode(location, expr1, expr2, rightHasAssignments);
}
+ExpressionNode* ASTBuilder::makeCoalesceNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2)
+{
+ // Optimization for `x?.y ?? z`.
+ if (expr1->isOptionalChain()) {
+ OptionalChainNode* optionalChain = static_cast<OptionalChainNode*>(expr1);
+ if (!optionalChain->expr()->isDeleteNode()) {
+ constexpr bool hasAbsorbedOptionalChain = true;
+ return new (m_parserArena) CoalesceNode(location, optionalChain->expr(), expr2, hasAbsorbedOptionalChain);
+ }
+ }
+ constexpr bool hasAbsorbedOptionalChain = false;
+ return new (m_parserArena) CoalesceNode(location, expr1, expr2, hasAbsorbedOptionalChain);
+}
+
ExpressionNode* ASTBuilder::makeFunctionCallNode(const JSTokenLocation& location, ExpressionNode* func, bool previousBaseWasSuper, ArgumentsNode* args, const JSTextPosition& divotStart, const JSTextPosition& divot, const JSTextPosition& divotEnd, size_t callOrApplyChildDepth, bool isOptionalCall)
{
ASSERT(divot.offset >= divot.lineStartOffset);
@@ -1424,7 +1438,7 @@
{
switch (token) {
case COALESCE:
- return new (m_parserArena) CoalesceNode(location, lhs.first, rhs.first);
+ return makeCoalesceNode(location, lhs.first, rhs.first);
case OR:
return new (m_parserArena) LogicalOpNode(location, lhs.first, rhs.first, OpLogicalOr);
Modified: trunk/Source/_javascript_Core/parser/NodeConstructors.h (249116 => 249117)
--- trunk/Source/_javascript_Core/parser/NodeConstructors.h 2019-08-26 20:42:48 UTC (rev 249116)
+++ trunk/Source/_javascript_Core/parser/NodeConstructors.h 2019-08-26 21:09:35 UTC (rev 249117)
@@ -669,10 +669,11 @@
{
}
- inline CoalesceNode::CoalesceNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2)
+ inline CoalesceNode::CoalesceNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool hasAbsorbedOptionalChain)
: ExpressionNode(location, ResultType::forCoalesce(expr1->resultDescriptor(), expr2->resultDescriptor()))
, m_expr1(expr1)
, m_expr2(expr2)
+ , m_hasAbsorbedOptionalChain(hasAbsorbedOptionalChain)
{
}
Modified: trunk/Source/_javascript_Core/parser/Nodes.h (249116 => 249117)
--- trunk/Source/_javascript_Core/parser/Nodes.h 2019-08-26 20:42:48 UTC (rev 249116)
+++ trunk/Source/_javascript_Core/parser/Nodes.h 2019-08-26 21:09:35 UTC (rev 249117)
@@ -205,6 +205,7 @@
virtual bool isBytecodeIntrinsicNode() const { return false; }
virtual bool isBinaryOpNode() const { return false; }
virtual bool isFunctionCall() const { return false; }
+ virtual bool isDeleteNode() const { return false; }
virtual bool isOptionalChain() const { return false; }
virtual void emitBytecodeInConditionContext(BytecodeGenerator&, Label&, Label&, FallThroughMode);
@@ -994,6 +995,8 @@
private:
RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
+ bool isDeleteNode() const final { return true; }
+
const Identifier& m_ident;
};
@@ -1004,6 +1007,8 @@
private:
RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
+ bool isDeleteNode() const final { return true; }
+
ExpressionNode* m_base;
ExpressionNode* m_subscript;
};
@@ -1015,6 +1020,8 @@
private:
RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
+ bool isDeleteNode() const final { return true; }
+
ExpressionNode* m_base;
const Identifier& m_ident;
};
@@ -1026,6 +1033,8 @@
private:
RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
+ bool isDeleteNode() const final { return true; }
+
ExpressionNode* m_expr;
};
@@ -1312,7 +1321,7 @@
class CoalesceNode final : public ExpressionNode {
public:
- CoalesceNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2);
+ CoalesceNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool);
private:
RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = nullptr) final;
@@ -1319,6 +1328,7 @@
ExpressionNode* m_expr1;
ExpressionNode* m_expr2;
+ bool m_hasAbsorbedOptionalChain;
};
class OptionalChainNode final : public ExpressionNode {
@@ -1328,8 +1338,6 @@
void setExpr(ExpressionNode* expr) { m_expr = expr; }
ExpressionNode* expr() const { return m_expr; }
- void setIsDelete() { m_isDelete = true; }
-
private:
RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = nullptr) final;
@@ -1337,7 +1345,6 @@
ExpressionNode* m_expr;
bool m_isOutermost;
- bool m_isDelete { false };
};
// The ternary operator, "m_logical ? m_expr1 : m_expr2"