[JSC] Use the way number constants are written to help type speculation
https://bugs.webkit.org/show_bug.cgi?id=142072
Patch by Benjamin Poulain <bpoulain@apple.com> on 2015-02-27
Reviewed by Filip Pizlo.
This patch changes how we interpret numeric constant based on how they appear
in the source.
Constants that are integers but written with a decimal point now carry that information
to the optimizating tiers. From there, we use that to be more aggressive about typing
math operations toward double operations.
For example, in:
var a = x + 1.0;
var b = y + 1;
The Add for a would be biased toward doubles, the Add for b would speculate
integer as usual.
The gains are tiny but this is a prerequisite to make my next patch useful:
-SunSpider's access-fannkuch: definitely 1.0661x faster
-SunSpider's math-cordic: definitely 1.0266x slower
overal: might be 1.0066x slower.
-Kraken's imaging-darkroom: definitely 1.0333x faster.
* parser/Lexer.cpp:
(JSC::tokenTypeForIntegerLikeToken):
(JSC::Lexer<T>::lex):
The lexer now create two types of tokens for number: INTEGER and DOUBLE.
Those token types only carry information about how the values were
entered, an INTEGER does not have to be an integer, it is only written like one.
Large integer still end up represented as double in memory.
One trap I fell into was typing numbers like 12e3 as double. This kind of literal
is frequently used in integer-typed code, while 12.e3 would appear in double-typed
code.
Because of that, the only signals for double are: decimal point, negative zero,
and ridiculously large values.
* parser/NodeConstructors.h:
(JSC::DoubleNode::DoubleNode):
(JSC::IntegerNode::IntegerNode):
* parser/Nodes.h:
(JSC::NumberNode::value):
(JSC::NumberNode::setValue): Deleted.
Number get specialized in two new kind of nodes in the AST: IntegerNode and DoubleNode.
* bytecompiler/NodesCodegen.cpp:
(JSC::NumberNode::emitBytecode):
* parser/ASTBuilder.h:
(JSC::ASTBuilder::createDoubleExpr):
(JSC::ASTBuilder::createIntegerExpr):
(JSC::ASTBuilder::createIntegerLikeNumber):
(JSC::ASTBuilder::createDoubleLikeNumber):
(JSC::ASTBuilder::createNumberFromBinaryOperation):
(JSC::ASTBuilder::createNumberFromUnaryOperation):
(JSC::ASTBuilder::makeNegateNode):
(JSC::ASTBuilder::makeBitwiseNotNode):
(JSC::ASTBuilder::makeMultNode):
(JSC::ASTBuilder::makeDivNode):
(JSC::ASTBuilder::makeModNode):
(JSC::ASTBuilder::makeAddNode):
(JSC::ASTBuilder::makeSubNode):
(JSC::ASTBuilder::makeLeftShiftNode):
(JSC::ASTBuilder::makeRightShiftNode):
(JSC::ASTBuilder::makeURightShiftNode):
(JSC::ASTBuilder::makeBitOrNode):
(JSC::ASTBuilder::makeBitAndNode):
(JSC::ASTBuilder::makeBitXOrNode):
(JSC::ASTBuilder::createNumberExpr): Deleted.
(JSC::ASTBuilder::createNumber): Deleted.
The AST has some optimization to resolve constants before emitting bytecode.
In the new code, the intger representation is kept if both operands where
also represented as integers.
* parser/Parser.cpp:
(JSC::Parser<LexerType>::parseDeconstructionPattern):
(JSC::Parser<LexerType>::parseProperty):
(JSC::Parser<LexerType>::parseGetterSetter):
(JSC::Parser<LexerType>::parsePrimaryExpression):
(JSC::Parser<LexerType>::printUnexpectedTokenText):
* parser/ParserTokens.h:
* parser/SyntaxChecker.h:
(JSC::SyntaxChecker::createDoubleExpr):
(JSC::SyntaxChecker::createIntegerExpr):
(JSC::SyntaxChecker::createNumberExpr): Deleted.
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::registerName):
(JSC::CodeBlock::constantName):
Change constantName(r, getConstant(r)) -> constantName(r) to simplify
the dump code.
(JSC::CodeBlock::dumpBytecode):
Dump thre soure representation information we have with each constant.
(JSC::CodeBlock::CodeBlock):
(JSC::CodeBlock::shrinkToFit):
(JSC::constantName): Deleted.
* bytecode/CodeBlock.h:
(JSC::CodeBlock::constantsSourceCodeRepresentation):
(JSC::CodeBlock::addConstant):
(JSC::CodeBlock::addConstantLazily):
(JSC::CodeBlock::constantSourceCodeRepresentation):
(JSC::CodeBlock::setConstantRegisters):
* bytecode/UnlinkedCodeBlock.h:
(JSC::UnlinkedCodeBlock::addConstant):
(JSC::UnlinkedCodeBlock::constantsSourceCodeRepresentation):
(JSC::UnlinkedCodeBlock::shrinkToFit):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::addConstantValue):
(JSC::BytecodeGenerator::emitLoad):
* bytecompiler/BytecodeGenerator.h:
We have to differentiate between constants that have the same values but are
represented differently in the source. Values like 1.0 and 1 now end up
as different constants.
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::get):
(JSC::DFG::ByteCodeParser::addConstantToGraph):
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::registerFrozenValues):
* dfg/DFGGraph.h:
(JSC::DFG::Graph::addSpeculationMode):
(JSC::DFG::Graph::addImmediateShouldSpeculateInt32):
ArithAdd is very aggressive toward using Int52, which is quite useful
in many benchmarks.
Here we need to specialize to make sure we don't force our literals
to Int52 if there were represented as double.
There is one exception to that rule: when the other operand is guaranteed
to come from a NodeResultInt32. This is because there is some weird code
doing stuff like:
var b = a|0;
var c = b*2.0;
* dfg/DFGNode.h:
(JSC::DFG::Node::Node):
(JSC::DFG::Node::setOpAndDefaultFlags):
(JSC::DFG::Node::sourceCodeRepresentation):
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* runtime/JSCJSValue.h:
(JSC::EncodedJSValueWithRepresentationHashTraits::emptyValue):
(JSC::EncodedJSValueWithRepresentationHashTraits::constructDeletedValue):
(JSC::EncodedJSValueWithRepresentationHashTraits::isDeletedValue):
(JSC::EncodedJSValueWithRepresentationHash::hash):
(JSC::EncodedJSValueWithRepresentationHash::equal):
* tests/stress/arith-add-with-constants.js: Added.
* tests/stress/arith-mul-with-constants.js: Added.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@180813 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
index ef0ad4e..c1a20cb 100644
--- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp
+++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
@@ -172,11 +172,6 @@
dumpAssumingJITType(out, jitType());
}
-static CString constantName(int k, JSValue value)
-{
- return toCString(value, "(", VirtualRegister(k), ")");
-}
-
static CString idName(int id0, const Identifier& ident)
{
return toCString(ident.impl(), "(@id", id0, ")");
@@ -185,11 +180,17 @@
CString CodeBlock::registerName(int r) const
{
if (isConstantRegisterIndex(r))
- return constantName(r, getConstant(r));
+ return constantName(r);
return toCString(VirtualRegister(r));
}
+CString CodeBlock::constantName(int index) const
+{
+ JSValue value = getConstant(index);
+ return toCString(value, "(", VirtualRegister(index), ")");
+}
+
static CString regexpToSourceString(RegExp* regExp)
{
char postfix[5] = { '/', 0, 0, 0, 0 };
@@ -606,7 +607,19 @@
out.printf("\nConstants:\n");
size_t i = 0;
do {
- out.printf(" k%u = %s\n", static_cast<unsigned>(i), toCString(m_constantRegisters[i].get()).data());
+ const char* sourceCodeRepresentationDescription;
+ switch (m_constantsSourceCodeRepresentation[i]) {
+ case SourceCodeRepresentation::Double:
+ sourceCodeRepresentationDescription = ": in source as double";
+ break;
+ case SourceCodeRepresentation::Integer:
+ sourceCodeRepresentationDescription = ": in source as integer";
+ break;
+ case SourceCodeRepresentation::Other:
+ sourceCodeRepresentationDescription = "";
+ break;
+ }
+ out.printf(" k%u = %s%s\n", static_cast<unsigned>(i), toCString(m_constantRegisters[i].get()).data(), sourceCodeRepresentationDescription);
++i;
} while (i < m_constantRegisters.size());
}
@@ -1449,7 +1462,7 @@
int k0 = (++it)->u.operand;
JSNameScope::Type scopeType = (JSNameScope::Type)(++it)->u.operand;
printLocationAndOp(out, exec, location, it, "push_name_scope");
- out.printf("%s, %s, %s, %s", registerName(dst).data(), registerName(r1).data(), constantName(k0, getConstant(k0)).data(), (scopeType == JSNameScope::FunctionNameScope) ? "functionScope" : ((scopeType == JSNameScope::CatchScope) ? "catchScope" : "unknownScopeType"));
+ out.printf("%s, %s, %s, %s", registerName(dst).data(), registerName(r1).data(), constantName(k0).data(), (scopeType == JSNameScope::FunctionNameScope) ? "functionScope" : ((scopeType == JSNameScope::CatchScope) ? "catchScope" : "unknownScopeType"));
break;
}
case op_catch: {
@@ -1466,7 +1479,7 @@
int k0 = (++it)->u.operand;
int k1 = (++it)->u.operand;
printLocationAndOp(out, exec, location, it, "throw_static_error");
- out.printf("%s, %s", constantName(k0, getConstant(k0)).data(), k1 ? "true" : "false");
+ out.printf("%s, %s", constantName(k0).data(), k1 ? "true" : "false");
break;
}
case op_debug: {
@@ -1639,6 +1652,7 @@
, m_firstLineColumnOffset(other.m_firstLineColumnOffset)
, m_codeType(other.m_codeType)
, m_constantRegisters(other.m_constantRegisters)
+ , m_constantsSourceCodeRepresentation(other.m_constantsSourceCodeRepresentation)
, m_functionDecls(other.m_functionDecls)
, m_functionExprs(other.m_functionExprs)
, m_osrExitCounter(0)
@@ -1730,7 +1744,7 @@
if (vm()->typeProfiler() || vm()->controlFlowProfiler())
vm()->functionHasExecutedCache()->removeUnexecutedRange(m_ownerExecutable->sourceID(), m_ownerExecutable->typeProfilingStartOffset(), m_ownerExecutable->typeProfilingEndOffset());
- setConstantRegisters(unlinkedCodeBlock->constantRegisters());
+ setConstantRegisters(unlinkedCodeBlock->constantRegisters(), unlinkedCodeBlock->constantsSourceCodeRepresentation());
if (unlinkedCodeBlock->usesGlobalObject())
m_constantRegisters[unlinkedCodeBlock->globalObjectRegister().toConstantIndex()].set(*m_vm, ownerExecutable, m_globalObject.get());
m_functionDecls.resizeToFit(unlinkedCodeBlock->numberOfFunctionDecls());
@@ -2980,6 +2994,7 @@
if (shrinkMode == EarlyShrink) {
m_constantRegisters.shrinkToFit();
+ m_constantsSourceCodeRepresentation.shrinkToFit();
if (m_rareData) {
m_rareData->m_switchJumpTables.shrinkToFit();