[JSC] Use the way number constants are written to help type speculation
authorbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 28 Feb 2015 03:21:37 +0000 (03:21 +0000)
committerbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 28 Feb 2015 03:21:37 +0000 (03:21 +0000)
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

25 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecode/CodeBlock.h
Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGCommon.h
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGGraph.cpp
Source/JavaScriptCore/dfg/DFGGraph.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGValidate.cpp
Source/JavaScriptCore/parser/ASTBuilder.h
Source/JavaScriptCore/parser/Lexer.cpp
Source/JavaScriptCore/parser/NodeConstructors.h
Source/JavaScriptCore/parser/Nodes.h
Source/JavaScriptCore/parser/Parser.cpp
Source/JavaScriptCore/parser/ParserTokens.h
Source/JavaScriptCore/parser/SyntaxChecker.h
Source/JavaScriptCore/runtime/JSCJSValue.h
Source/JavaScriptCore/tests/stress/arith-add-with-constants.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/arith-mul-with-constants.js [new file with mode: 0644]

index 97e21ac..72a64d1 100644 (file)
@@ -1,3 +1,160 @@
+2015-02-27  Benjamin Poulain  <bpoulain@apple.com>
+
+        [JSC] Use the way number constants are written to help type speculation
+        https://bugs.webkit.org/show_bug.cgi?id=142072
+
+        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.
+
 2015-02-26  Filip Pizlo  <fpizlo@apple.com>
 
         Unreviewed, roll out r180723. It broke a bunch of tests.
index ef0ad4e..c1a20cb 100644 (file)
@@ -172,11 +172,6 @@ void CodeBlock::dump(PrintStream& out) const
     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 @@ static CString idName(int id0, const Identifier& ident)
 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 @@ void CodeBlock::dumpBytecode(PrintStream& out)
         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 @@ void CodeBlock::dumpBytecode(
             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 @@ void CodeBlock::dumpBytecode(
             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 @@ CodeBlock::CodeBlock(CopyParsedBlockTag, CodeBlock& other)
     , 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 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin
     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 @@ void CodeBlock::shrinkToFit(ShrinkMode shrinkMode)
     
     if (shrinkMode == EarlyShrink) {
         m_constantRegisters.shrinkToFit();
+        m_constantsSourceCodeRepresentation.shrinkToFit();
         
         if (m_rareData) {
             m_rareData->m_switchJumpTables.shrinkToFit();
index 07293e0..61ab3bd 100644 (file)
@@ -628,12 +628,14 @@ public:
 #endif
 
     Vector<WriteBarrier<Unknown>>& constants() { return m_constantRegisters; }
+    Vector<SourceCodeRepresentation>& constantsSourceCodeRepresentation() { return m_constantsSourceCodeRepresentation; }
     size_t numberOfConstantRegisters() const { return m_constantRegisters.size(); }
     unsigned addConstant(JSValue v)
     {
         unsigned result = m_constantRegisters.size();
         m_constantRegisters.append(WriteBarrier<Unknown>());
         m_constantRegisters.last().set(m_globalObject->vm(), m_ownerExecutable.get(), v);
+        m_constantsSourceCodeRepresentation.append(SourceCodeRepresentation::Other);
         return result;
     }
 
@@ -641,6 +643,7 @@ public:
     {
         unsigned result = m_constantRegisters.size();
         m_constantRegisters.append(WriteBarrier<Unknown>());
+        m_constantsSourceCodeRepresentation.append(SourceCodeRepresentation::Other);
         return result;
     }
 
@@ -649,6 +652,7 @@ public:
     WriteBarrier<Unknown>& constantRegister(int index) { return m_constantRegisters[index - FirstConstantRegisterIndex]; }
     ALWAYS_INLINE bool isConstantRegisterIndex(int index) const { return index >= FirstConstantRegisterIndex; }
     ALWAYS_INLINE JSValue getConstant(int index) const { return m_constantRegisters[index - FirstConstantRegisterIndex].get(); }
+    ALWAYS_INLINE SourceCodeRepresentation constantSourceCodeRepresentation(int index) const { return m_constantsSourceCodeRepresentation[index - FirstConstantRegisterIndex]; }
 
     FunctionExecutable* functionDecl(int index) { return m_functionDecls[index].get(); }
     int numberOfFunctionDecls() { return m_functionDecls.size(); }
@@ -983,12 +987,14 @@ private:
 
     void updateAllPredictionsAndCountLiveness(unsigned& numberOfLiveNonArgumentValueProfiles, unsigned& numberOfSamplesInProfiles);
 
-    void setConstantRegisters(const Vector<WriteBarrier<Unknown>>& constants)
+    void setConstantRegisters(const Vector<WriteBarrier<Unknown>>& constants, const Vector<SourceCodeRepresentation>& constantsSourceCodeRepresentation)
     {
+        ASSERT(constants.size() == constantsSourceCodeRepresentation.size());
         size_t count = constants.size();
         m_constantRegisters.resize(count);
         for (size_t i = 0; i < count; i++)
             m_constantRegisters[i].set(*m_vm, ownerExecutable(), constants[i].get());
+        m_constantsSourceCodeRepresentation = constantsSourceCodeRepresentation;
     }
 
     void dumpBytecode(
@@ -996,6 +1002,7 @@ private:
         const StubInfoMap& = StubInfoMap(), const CallLinkInfoMap& = CallLinkInfoMap());
 
     CString registerName(int r) const;
+    CString constantName(int index) const;
     void printUnaryOp(PrintStream&, ExecState*, int location, const Instruction*&, const char* op);
     void printBinaryOp(PrintStream&, ExecState*, int location, const Instruction*&, const char* op);
     void printConditionalJump(PrintStream&, ExecState*, const Instruction*, const Instruction*&, int location, const char* op);
@@ -1093,6 +1100,7 @@ private:
     // TODO: This could just be a pointer to m_unlinkedCodeBlock's data, but the DFG mutates
     // it, so we're stuck with it for now.
     Vector<WriteBarrier<Unknown>> m_constantRegisters;
+    Vector<SourceCodeRepresentation> m_constantsSourceCodeRepresentation;
     Vector<WriteBarrier<FunctionExecutable>> m_functionDecls;
     Vector<WriteBarrier<FunctionExecutable>> m_functionExprs;
 
index 08c7297..c7d2da5 100644 (file)
@@ -318,11 +318,12 @@ public:
     const Vector<Identifier>& identifiers() const { return m_identifiers; }
 
     size_t numberOfConstantRegisters() const { return m_constantRegisters.size(); }
-    unsigned addConstant(JSValue v)
+    unsigned addConstant(JSValue v, SourceCodeRepresentation sourceCodeRepresentation = SourceCodeRepresentation::Other)
     {
         unsigned result = m_constantRegisters.size();
         m_constantRegisters.append(WriteBarrier<Unknown>());
         m_constantRegisters.last().set(*m_vm, this, v);
+        m_constantsSourceCodeRepresentation.append(sourceCodeRepresentation);
         return result;
     }
     unsigned addOrFindConstant(JSValue);
@@ -330,6 +331,7 @@ public:
     const WriteBarrier<Unknown>& constantRegister(int index) const { return m_constantRegisters[index - FirstConstantRegisterIndex]; }
     ALWAYS_INLINE bool isConstantRegisterIndex(int index) const { return index >= FirstConstantRegisterIndex; }
     ALWAYS_INLINE JSValue getConstant(int index) const { return m_constantRegisters[index - FirstConstantRegisterIndex].get(); }
+    const Vector<SourceCodeRepresentation>& constantsSourceCodeRepresentation() { return m_constantsSourceCodeRepresentation; }
 
     // Jumps
     size_t numberOfJumpTargets() const { return m_jumpTargets.size(); }
@@ -347,6 +349,7 @@ public:
         m_jumpTargets.shrinkToFit();
         m_identifiers.shrinkToFit();
         m_constantRegisters.shrinkToFit();
+        m_constantsSourceCodeRepresentation.shrinkToFit();
         m_functionDecls.shrinkToFit();
         m_functionExprs.shrinkToFit();
         m_propertyAccessInstructions.shrinkToFit();
@@ -544,6 +547,7 @@ private:
     // Constant Pools
     Vector<Identifier> m_identifiers;
     Vector<WriteBarrier<Unknown>> m_constantRegisters;
+    Vector<SourceCodeRepresentation> m_constantsSourceCodeRepresentation;
     typedef Vector<WriteBarrier<UnlinkedFunctionExecutable>> FunctionExpressionVector;
     FunctionExpressionVector m_functionDecls;
     FunctionExpressionVector m_functionExprs;
index b7f4368..7b947cd 100644 (file)
@@ -979,17 +979,19 @@ RegisterID* BytecodeGenerator::addConstantEmptyValue()
     return m_emptyValueRegister;
 }
 
-RegisterID* BytecodeGenerator::addConstantValue(JSValue v)
+RegisterID* BytecodeGenerator::addConstantValue(JSValue v, SourceCodeRepresentation sourceCodeRepresentation)
 {
     if (!v)
         return addConstantEmptyValue();
 
     int index = m_nextConstantOffset;
-    JSValueMap::AddResult result = m_jsValueMap.add(JSValue::encode(v), m_nextConstantOffset);
+
+    EncodedJSValueWithRepresentation valueMapKey { JSValue::encode(v), sourceCodeRepresentation };
+    JSValueMap::AddResult result = m_jsValueMap.add(valueMapKey, m_nextConstantOffset);
     if (result.isNewEntry) {
         m_constantPoolRegisters.append(FirstConstantRegisterIndex + m_nextConstantOffset);
         ++m_nextConstantOffset;
-        m_codeBlock->addConstant(v);
+        m_codeBlock->addConstant(v, sourceCodeRepresentation);
     } else
         index = result.iterator->value;
     return &m_constantPoolRegisters[index];
@@ -1162,9 +1164,9 @@ RegisterID* BytecodeGenerator::emitLoad(RegisterID* dst, const Identifier& ident
     return emitLoad(dst, JSValue(stringInMap));
 }
 
-RegisterID* BytecodeGenerator::emitLoad(RegisterID* dst, JSValue v)
+RegisterID* BytecodeGenerator::emitLoad(RegisterID* dst, JSValue v, SourceCodeRepresentation sourceCodeRepresentation)
 {
-    RegisterID* constantID = addConstantValue(v);
+    RegisterID* constantID = addConstantValue(v, sourceCodeRepresentation);
     if (dst)
         return emitMove(dst, constantID);
     return constantID;
index d6a4a09..39a4983 100644 (file)
@@ -443,7 +443,7 @@ namespace JSC {
 
         RegisterID* emitLoad(RegisterID* dst, bool);
         RegisterID* emitLoad(RegisterID* dst, const Identifier&);
-        RegisterID* emitLoad(RegisterID* dst, JSValue);
+        RegisterID* emitLoad(RegisterID* dst, JSValue, SourceCodeRepresentation = SourceCodeRepresentation::Other);
         RegisterID* emitLoadGlobalObject(RegisterID* dst);
 
         RegisterID* emitUnaryOp(OpcodeID, RegisterID* dst, RegisterID* src);
@@ -646,7 +646,7 @@ namespace JSC {
 
         bool hasConstant(const Identifier&) const;
         unsigned addConstant(const Identifier&);
-        RegisterID* addConstantValue(JSValue);
+        RegisterID* addConstantValue(JSValue, SourceCodeRepresentation = SourceCodeRepresentation::Other);
         RegisterID* addConstantEmptyValue();
         unsigned addRegExp(RegExp*);
 
@@ -792,6 +792,8 @@ namespace JSC {
         
         // Constant pool
         IdentifierMap m_identifierMap;
+
+        typedef HashMap<EncodedJSValueWithRepresentation, unsigned, EncodedJSValueWithRepresentationHash, EncodedJSValueWithRepresentationHashTraits> JSValueMap;
         JSValueMap m_jsValueMap;
         IdentifierStringMap m_stringMap;
 
index 00accf9..b42ecb7 100644 (file)
@@ -122,6 +122,15 @@ JSValue StringNode::jsValue(BytecodeGenerator& generator) const
     return generator.addStringConstant(m_value);
 }
 
+// ------------------------------ NumberNode ----------------------------------
+
+RegisterID* NumberNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
+{
+    if (dst == generator.ignoredResult())
+        return nullptr;
+    return generator.emitLoad(dst, jsValue(generator), isIntegerNode() ? SourceCodeRepresentation::Integer : SourceCodeRepresentation::Double);
+}
+
 // ------------------------------ RegExpNode -----------------------------------
 
 RegisterID* RegExpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
index 85859d2..47d4a5c 100644 (file)
@@ -95,7 +95,7 @@ private:
     template<int power>
     bool isWithinPowerOfTwoNonRecursive(Node* node)
     {
-        if (node->op() != JSConstant)
+        if (!node->isNumberConstant())
             return false;
         return isWithinPowerOfTwoForConstant<power>(node);
     }
@@ -104,7 +104,9 @@ private:
     bool isWithinPowerOfTwo(Node* node)
     {
         switch (node->op()) {
-        case JSConstant: {
+        case DoubleConstant:
+        case JSConstant:
+        case Int52Constant: {
             return isWithinPowerOfTwoForConstant<power>(node);
         }
             
@@ -128,7 +130,7 @@ private:
                 return true;
             
             Node* shiftAmount = node->child2().node();
-            if (shiftAmount->op() != JSConstant)
+            if (!node->isNumberConstant())
                 return false;
             JSValue immediateValue = shiftAmount->asJSValue();
             if (!immediateValue.isInt32())
index 2c85a86..a513d7a 100644 (file)
@@ -252,14 +252,21 @@ private:
             unsigned constantIndex = operand.toConstantIndex();
             unsigned oldSize = m_constants.size();
             if (constantIndex >= oldSize || !m_constants[constantIndex]) {
-                JSValue value = m_inlineStackTop->m_codeBlock->getConstant(operand.offset());
+                const CodeBlock& codeBlock = *m_inlineStackTop->m_codeBlock;
+                JSValue value = codeBlock.getConstant(operand.offset());
+                SourceCodeRepresentation sourceCodeRepresentation = codeBlock.constantSourceCodeRepresentation(operand.offset());
                 if (constantIndex >= oldSize) {
                     m_constants.grow(constantIndex + 1);
                     for (unsigned i = oldSize; i < m_constants.size(); ++i)
                         m_constants[i] = nullptr;
                 }
-                m_constants[constantIndex] =
-                    addToGraph(JSConstant, OpInfo(m_graph.freezeStrong(value)));
+
+                Node* constantNode = nullptr;
+                if (sourceCodeRepresentation == SourceCodeRepresentation::Double)
+                    constantNode = addToGraph(DoubleConstant, OpInfo(m_graph.freezeStrong(jsDoubleNumber(value.asNumber()))));
+                else
+                    constantNode = addToGraph(JSConstant, OpInfo(m_graph.freezeStrong(value)));
+                m_constants[constantIndex] = constantNode;
             }
             ASSERT(m_constants[constantIndex]);
             return m_constants[constantIndex];
index e91274d..887c939 100644 (file)
@@ -256,6 +256,11 @@ inline KillStatus killStatusForDoesKill(bool doesKill)
     return doesKill ? DoesKill : DoesNotKill;
 }
 
+enum class PlanStage {
+    Initial,
+    AfterFixup
+};
+
 template<typename T, typename U>
 bool checkAndSet(T& left, U right)
 {
index d7809dd..625e9ea 100644 (file)
@@ -67,7 +67,9 @@ public:
         
         for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex)
             injectTypeConversionsInBlock(m_graph.block(blockIndex));
-        
+
+        m_graph.m_planStage = PlanStage::AfterFixup;
+
         return true;
     }
 
@@ -1056,7 +1058,6 @@ private:
         case DoubleRep:
         case ValueRep:
         case Int52Rep:
-        case DoubleConstant:
         case Int52Constant:
         case Identity: // This should have been cleaned up.
         case BooleanToNumber:
@@ -1219,6 +1220,7 @@ private:
         // Have these no-op cases here to ensure that nobody forgets to add handlers for new opcodes.
         case SetArgument:
         case JSConstant:
+        case DoubleConstant:
         case GetLocal:
         case GetCallee:
         case Flush:
index e7901d8..a5e9e5e 100644 (file)
@@ -1053,6 +1053,7 @@ JSArrayBufferView* Graph::tryGetFoldableViewForChild1(Node* node)
 void Graph::registerFrozenValues()
 {
     m_codeBlock->constants().resize(0);
+    m_codeBlock->constantsSourceCodeRepresentation().resize(0);
     for (FrozenValue* value : m_frozenValues) {
         if (value->structure())
             ASSERT(m_plan.weakReferences.contains(value->structure()));
@@ -1078,6 +1079,7 @@ void Graph::registerFrozenValues()
         } }
     }
     m_codeBlock->constants().shrinkToFit();
+    m_codeBlock->constantsSourceCodeRepresentation().shrinkToFit();
 }
 
 void Graph::visitChildren(SlotVisitor& visitor)
index 01d1aa6..aece1d5 100644 (file)
@@ -217,9 +217,9 @@ public:
         Node* right = add->child2().node();
         
         if (left->hasConstant())
-            return addImmediateShouldSpeculateInt32(add, rightShouldSpeculateInt32, left, source);
+            return addImmediateShouldSpeculateInt32(add, rightShouldSpeculateInt32, right, left, source);
         if (right->hasConstant())
-            return addImmediateShouldSpeculateInt32(add, leftShouldSpeculateInt32, right, source);
+            return addImmediateShouldSpeculateInt32(add, leftShouldSpeculateInt32, left, right, source);
         
         return (leftShouldSpeculateInt32 && rightShouldSpeculateInt32 && add->canSpeculateInt32(source)) ? SpeculateInt32 : DontSpeculateInt32;
     }
@@ -846,12 +846,13 @@ public:
     StructureRegistrationState m_structureRegistrationState;
     GraphForm m_form;
     UnificationState m_unificationState;
+    PlanStage m_planStage { PlanStage::Initial };
     RefCountState m_refCountState;
 private:
     
     void handleSuccessor(Vector<BasicBlock*, 16>& worklist, BasicBlock*, BasicBlock* successor);
     
-    AddSpeculationMode addImmediateShouldSpeculateInt32(Node* add, bool variableShouldSpeculateInt32, Node* immediate, RareCaseProfilingSource source)
+    AddSpeculationMode addImmediateShouldSpeculateInt32(Node* add, bool variableShouldSpeculateInt32, Node* operand, Node*immediate, RareCaseProfilingSource source)
     {
         ASSERT(immediate->hasConstant());
         
@@ -861,8 +862,14 @@ private:
         
         if (!variableShouldSpeculateInt32)
             return DontSpeculateInt32;
+
+        // Integer constants can be typed Double if they are written like a double in the source code (e.g. 42.0).
+        // In that case, we stay conservative unless the other operand was explicitly typed as integer.
+        NodeFlags operandResultType = operand->result();
+        if (operandResultType != NodeResultInt32 && immediateValue.isDouble())
+            return DontSpeculateInt32;
         
-        if (immediateValue.isInt32() || immediateValue.isBoolean())
+        if (jsNumber(immediateValue.asNumber()).isInt32() || immediateValue.isBoolean())
             return add->canSpeculateInt32(source) ? SpeculateInt32 : DontSpeculateInt32;
         
         double doubleImmediate = immediateValue.asDouble();
index f5c9ec4..68aafea 100644 (file)
@@ -148,6 +148,11 @@ private:
             changed |= setPrediction(type);
             break;
         }
+        case DoubleConstant: {
+            SpeculatedType type = speculationFromValue(node->asJSValue());
+            changed |= setPrediction(type);
+            break;
+        }
             
         case GetLocal: {
             VariableAccessData* variable = node->variableAccessData();
@@ -532,7 +537,6 @@ private:
         case DoubleRep:
         case ValueRep:
         case Int52Rep:
-        case DoubleConstant:
         case Int52Constant:
         case Identity:
         case BooleanToNumber:
index 329a076..fb1b929 100644 (file)
@@ -118,8 +118,8 @@ public:
                         continue;
                     
                     m_myRefCounts.find(edge.node())->value++;
-                    
-                    VALIDATE((node, edge), edge->hasDoubleResult() == (edge.useKind() == DoubleRepUse || edge.useKind() == DoubleRepRealUse || edge.useKind() == DoubleRepMachineIntUse));
+
+                    validateEdgeWithDoubleResultIfNecessary(node, edge);
                     VALIDATE((node, edge), edge->hasInt52Result() == (edge.useKind() == Int52RepUse));
                     
                     if (m_graph.m_form == SSA) {
@@ -543,7 +543,18 @@ private:
             }
         }
     }
-    
+
+    void validateEdgeWithDoubleResultIfNecessary(Node* node, Edge edge)
+    {
+        if (!edge->hasDoubleResult())
+            return;
+
+        if (m_graph.m_planStage < PlanStage::AfterFixup)
+            VALIDATE((node, edge), edge.useKind() == UntypedUse);
+        else
+            VALIDATE((node, edge), edge.useKind() == DoubleRepUse || edge.useKind() == DoubleRepRealUse || edge.useKind() == DoubleRepMachineIntUse);
+    }
+
     void checkOperand(
         BasicBlock* block, Operands<size_t>& getLocalPositions,
         Operands<size_t>& setLocalPositions, VirtualRegister operand)
index 88418f1..fa64951 100644 (file)
@@ -192,10 +192,15 @@ public:
             incConstants();
         return new (m_parserArena) ArrayNode(location, elisions, elems);
     }
-    ExpressionNode* createNumberExpr(const JSTokenLocation& location, double d)
+    ExpressionNode* createDoubleExpr(const JSTokenLocation& location, double d)
     {
         incConstants();
-        return new (m_parserArena) NumberNode(location, d);
+        return new (m_parserArena) DoubleNode(location, d);
+    }
+    ExpressionNode* createIntegerExpr(const JSTokenLocation& location, double d)
+    {
+        incConstants();
+        return new (m_parserArena) IntegerNode(location, d);
     }
 
     ExpressionNode* createString(const JSTokenLocation& location, const Identifier* string)
@@ -748,11 +753,27 @@ private:
         m_evalCount++;
         m_scope.m_features |= EvalFeature;
     }
-    ExpressionNode* createNumber(const JSTokenLocation& location, double d)
+    ExpressionNode* createIntegerLikeNumber(const JSTokenLocation& location, double d)
     {
-        return new (m_parserArena) NumberNode(location, d);
+        return new (m_parserArena) IntegerNode(location, d);
     }
-    
+    ExpressionNode* createDoubleLikeNumber(const JSTokenLocation& location, double d)
+    {
+        return new (m_parserArena) DoubleNode(location, d);
+    }
+    ExpressionNode* createNumberFromBinaryOperation(const JSTokenLocation& location, double value, const NumberNode& originalNodeA, const NumberNode& originalNodeB)
+    {
+        if (originalNodeA.isIntegerNode() && originalNodeB.isIntegerNode())
+            return createIntegerLikeNumber(location, value);
+        return createDoubleLikeNumber(location, value);
+    }
+    ExpressionNode* createNumberFromUnaryOperation(const JSTokenLocation& location, double value, const NumberNode& originalNode)
+    {
+        if (originalNode.isIntegerNode())
+            return createIntegerLikeNumber(location, value);
+        return createDoubleLikeNumber(location, value);
+    }
+
     VM* m_vm;
     ParserArena& m_parserArena;
     SourceCode* m_sourceCode;
@@ -793,9 +814,8 @@ ExpressionNode* ASTBuilder::makeDeleteNode(const JSTokenLocation& location, Expr
 ExpressionNode* ASTBuilder::makeNegateNode(const JSTokenLocation& location, ExpressionNode* n)
 {
     if (n->isNumber()) {
-        NumberNode* numberNode = static_cast<NumberNode*>(n);
-        numberNode->setValue(-numberNode->value());
-        return numberNode;
+        const NumberNode& numberNode = static_cast<const NumberNode&>(*n);
+        return createNumberFromUnaryOperation(location, -numberNode.value(), numberNode);
     }
 
     return new (m_parserArena) NegateNode(location, n);
@@ -804,7 +824,7 @@ ExpressionNode* ASTBuilder::makeNegateNode(const JSTokenLocation& location, Expr
 ExpressionNode* ASTBuilder::makeBitwiseNotNode(const JSTokenLocation& location, ExpressionNode* expr)
 {
     if (expr->isNumber())
-        return createNumber(location, ~toInt32(static_cast<NumberNode*>(expr)->value()));
+        return createIntegerLikeNumber(location, ~toInt32(static_cast<NumberNode*>(expr)->value()));
     return new (m_parserArena) BitwiseNotNode(location, expr);
 }
 
@@ -813,8 +833,11 @@ ExpressionNode* ASTBuilder::makeMultNode(const JSTokenLocation& location, Expres
     expr1 = expr1->stripUnaryPlus();
     expr2 = expr2->stripUnaryPlus();
 
-    if (expr1->isNumber() && expr2->isNumber())
-        return createNumber(location, static_cast<NumberNode*>(expr1)->value() * static_cast<NumberNode*>(expr2)->value());
+    if (expr1->isNumber() && expr2->isNumber()) {
+        const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1);
+        const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2);
+        return createNumberFromBinaryOperation(location, numberExpr1.value() * numberExpr2.value(), numberExpr1, numberExpr2);
+    }
 
     if (expr1->isNumber() && static_cast<NumberNode*>(expr1)->value() == 1)
         return new (m_parserArena) UnaryPlusNode(location, expr2);
@@ -830,8 +853,14 @@ ExpressionNode* ASTBuilder::makeDivNode(const JSTokenLocation& location, Express
     expr1 = expr1->stripUnaryPlus();
     expr2 = expr2->stripUnaryPlus();
 
-    if (expr1->isNumber() && expr2->isNumber())
-        return createNumber(location, static_cast<NumberNode*>(expr1)->value() / static_cast<NumberNode*>(expr2)->value());
+    if (expr1->isNumber() && expr2->isNumber()) {
+        const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1);
+        const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2);
+        double result = numberExpr1.value() / numberExpr2.value();
+        if (static_cast<int64_t>(result) == result)
+            return createNumberFromBinaryOperation(location, result, numberExpr1, numberExpr2);
+        return createDoubleLikeNumber(location, result);
+    }
     return new (m_parserArena) DivNode(location, expr1, expr2, rightHasAssignments);
 }
 
@@ -839,16 +868,23 @@ ExpressionNode* ASTBuilder::makeModNode(const JSTokenLocation& location, Express
 {
     expr1 = expr1->stripUnaryPlus();
     expr2 = expr2->stripUnaryPlus();
-    
-    if (expr1->isNumber() && expr2->isNumber())
-        return createNumber(location, fmod(static_cast<NumberNode*>(expr1)->value(), static_cast<NumberNode*>(expr2)->value()));
+
+    if (expr1->isNumber() && expr2->isNumber()) {
+        const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1);
+        const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2);
+        return createIntegerLikeNumber(location, fmod(numberExpr1.value(), numberExpr2.value()));
+    }
     return new (m_parserArena) ModNode(location, expr1, expr2, rightHasAssignments);
 }
 
 ExpressionNode* ASTBuilder::makeAddNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
 {
-    if (expr1->isNumber() && expr2->isNumber())
-        return createNumber(location, static_cast<NumberNode*>(expr1)->value() + static_cast<NumberNode*>(expr2)->value());
+
+    if (expr1->isNumber() && expr2->isNumber()) {
+        const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1);
+        const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2);
+        return createNumberFromBinaryOperation(location, numberExpr1.value() + numberExpr2.value(), numberExpr1, numberExpr2);
+    }
     return new (m_parserArena) AddNode(location, expr1, expr2, rightHasAssignments);
 }
 
@@ -857,50 +893,71 @@ ExpressionNode* ASTBuilder::makeSubNode(const JSTokenLocation& location, Express
     expr1 = expr1->stripUnaryPlus();
     expr2 = expr2->stripUnaryPlus();
 
-    if (expr1->isNumber() && expr2->isNumber())
-        return createNumber(location, static_cast<NumberNode*>(expr1)->value() - static_cast<NumberNode*>(expr2)->value());
+    if (expr1->isNumber() && expr2->isNumber()) {
+        const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1);
+        const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2);
+        return createNumberFromBinaryOperation(location, numberExpr1.value() - numberExpr2.value(), numberExpr1, numberExpr2);
+    }
     return new (m_parserArena) SubNode(location, expr1, expr2, rightHasAssignments);
 }
 
 ExpressionNode* ASTBuilder::makeLeftShiftNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
 {
-    if (expr1->isNumber() && expr2->isNumber())
-        return createNumber(location, toInt32(static_cast<NumberNode*>(expr1)->value()) << (toUInt32(static_cast<NumberNode*>(expr2)->value()) & 0x1f));
+    if (expr1->isNumber() && expr2->isNumber()) {
+        const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1);
+        const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2);
+        return createIntegerLikeNumber(location, toInt32(numberExpr1.value()) << (toUInt32(numberExpr2.value()) & 0x1f));
+    }
     return new (m_parserArena) LeftShiftNode(location, expr1, expr2, rightHasAssignments);
 }
 
 ExpressionNode* ASTBuilder::makeRightShiftNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
 {
-    if (expr1->isNumber() && expr2->isNumber())
-        return createNumber(location, toInt32(static_cast<NumberNode*>(expr1)->value()) >> (toUInt32(static_cast<NumberNode*>(expr2)->value()) & 0x1f));
+    if (expr1->isNumber() && expr2->isNumber()) {
+        const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1);
+        const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2);
+        return createIntegerLikeNumber(location, toInt32(numberExpr1.value()) >> (toUInt32(numberExpr2.value()) & 0x1f));
+    }
     return new (m_parserArena) RightShiftNode(location, expr1, expr2, rightHasAssignments);
 }
 
 ExpressionNode* ASTBuilder::makeURightShiftNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
 {
-    if (expr1->isNumber() && expr2->isNumber())
-        return createNumber(location, toUInt32(static_cast<NumberNode*>(expr1)->value()) >> (toUInt32(static_cast<NumberNode*>(expr2)->value()) & 0x1f));
+    if (expr1->isNumber() && expr2->isNumber()) {
+        const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1);
+        const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2);
+        return createIntegerLikeNumber(location, toUInt32(numberExpr1.value()) >> (toUInt32(numberExpr2.value()) & 0x1f));
+    }
     return new (m_parserArena) UnsignedRightShiftNode(location, expr1, expr2, rightHasAssignments);
 }
 
 ExpressionNode* ASTBuilder::makeBitOrNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
 {
-    if (expr1->isNumber() && expr2->isNumber())
-        return createNumber(location, toInt32(static_cast<NumberNode*>(expr1)->value()) | toInt32(static_cast<NumberNode*>(expr2)->value()));
+    if (expr1->isNumber() && expr2->isNumber()) {
+        const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1);
+        const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2);
+        return createIntegerLikeNumber(location, toInt32(numberExpr1.value()) | toInt32(numberExpr2.value()));
+    }
     return new (m_parserArena) BitOrNode(location, expr1, expr2, rightHasAssignments);
 }
 
 ExpressionNode* ASTBuilder::makeBitAndNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
 {
-    if (expr1->isNumber() && expr2->isNumber())
-        return createNumber(location, toInt32(static_cast<NumberNode*>(expr1)->value()) & toInt32(static_cast<NumberNode*>(expr2)->value()));
+    if (expr1->isNumber() && expr2->isNumber()) {
+        const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1);
+        const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2);
+        return createIntegerLikeNumber(location, toInt32(numberExpr1.value()) & toInt32(numberExpr2.value()));
+    }
     return new (m_parserArena) BitAndNode(location, expr1, expr2, rightHasAssignments);
 }
 
 ExpressionNode* ASTBuilder::makeBitXOrNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
 {
-    if (expr1->isNumber() && expr2->isNumber())
-        return createNumber(location, toInt32(static_cast<NumberNode*>(expr1)->value()) ^ toInt32(static_cast<NumberNode*>(expr2)->value()));
+    if (expr1->isNumber() && expr2->isNumber()) {
+        const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1);
+        const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2);
+        return createIntegerLikeNumber(location, toInt32(numberExpr1.value()) ^ toInt32(numberExpr2.value()));
+    }
     return new (m_parserArena) BitXOrNode(location, expr1, expr2, rightHasAssignments);
 }
 
index f13fe4d..1ec67df 100644 (file)
@@ -495,6 +495,13 @@ Lexer<T>::Lexer(VM* vm, JSParserStrictness strictness)
 {
 }
 
+static inline JSTokenType tokenTypeForIntegerLikeToken(double doubleValue)
+{
+    if ((doubleValue || !std::signbit(doubleValue)) && static_cast<int64_t>(doubleValue) == doubleValue)
+        return INTEGER;
+    return DOUBLE;
+}
+
 template <typename T>
 Lexer<T>::~Lexer()
 {
@@ -1687,7 +1694,7 @@ start:
                 token = INVALID_HEX_NUMBER_ERRORTOK;
                 goto returnError;
             }
-            token = NUMBER;
+            token = tokenTypeForIntegerLikeToken(tokenData->doubleValue);
             m_buffer8.resize(0);
             break;
         }
@@ -1700,17 +1707,19 @@ start:
         }
         if (isASCIIOctalDigit(m_current)) {
             if (parseOctal(tokenData->doubleValue)) {
-                token = NUMBER;
+                token = tokenTypeForIntegerLikeToken(tokenData->doubleValue);
             }
         }
         FALLTHROUGH;
     case CharacterNumber:
-        if (LIKELY(token != NUMBER)) {
+        if (LIKELY(token != INTEGER && token != DOUBLE)) {
             if (!parseDecimal(tokenData->doubleValue)) {
+                token = INTEGER;
                 if (m_current == '.') {
                     shift();
 inNumberAfterDecimalPoint:
                     parseNumberAfterDecimalPoint();
+                    token = DOUBLE;
                 }
                 if ((m_current | 0x20) == 'e') {
                     if (!parseNumberAfterExponentIndicator()) {
@@ -1721,8 +1730,10 @@ inNumberAfterDecimalPoint:
                 }
                 size_t parsedLength;
                 tokenData->doubleValue = parseDouble(m_buffer8.data(), m_buffer8.size(), parsedLength);
-            }
-            token = NUMBER;
+                if (token == INTEGER)
+                    token = tokenTypeForIntegerLikeToken(tokenData->doubleValue);
+            } else
+                token = tokenTypeForIntegerLikeToken(tokenData->doubleValue);
         }
 
         // No identifiers allowed directly after numeric literal, e.g. "3in" is bad.
index 950409a..12e3d4d 100644 (file)
@@ -84,6 +84,16 @@ namespace JSC {
     {
     }
 
+    inline DoubleNode::DoubleNode(const JSTokenLocation& location, double value)
+        : NumberNode(location, value)
+    {
+    }
+
+    inline IntegerNode::IntegerNode(const JSTokenLocation& location, double value)
+        : DoubleNode(location, value)
+    {
+    }
+
     inline StringNode::StringNode(const JSTokenLocation& location, const Identifier& value)
         : ConstantNode(location, ResultType::stringType())
         , m_value(value)
index 27bc3f8..30edb0a 100644 (file)
@@ -236,16 +236,32 @@ namespace JSC {
     class NumberNode : public ConstantNode {
     public:
         NumberNode(const JSTokenLocation&, double value);
-        double value() { return m_value; }
-        void setValue(double value) { m_value = value; }
+        double value() const { return m_value; }
+        virtual bool isIntegerNode() const = 0;
+        virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override final;
 
     private:
-        virtual bool isNumber() const override { return true; }
+        virtual bool isNumber() const override final { return true; }
         virtual JSValue jsValue(BytecodeGenerator&) const override { return jsNumber(m_value); }
 
         double m_value;
     };
 
+    class DoubleNode : public NumberNode {
+    public:
+        DoubleNode(const JSTokenLocation&, double value);
+
+    private:
+        virtual bool isIntegerNode() const override { return false; }
+    };
+
+    // An integer node represent a number represented as an integer (e.g. 42 instead of 42., 42.0, 42e0)
+    class IntegerNode : public DoubleNode {
+    public:
+        IntegerNode(const JSTokenLocation&, double value);
+        virtual bool isIntegerNode() const override final { return true; }
+    };
+
     class StringNode : public ConstantNode {
     public:
         StringNode(const JSTokenLocation&, const Identifier&);
index f4c4bc5..6318fd6 100644 (file)
@@ -629,7 +629,8 @@ template <class TreeBuilder> TreeDeconstructionPattern Parser<LexerType>::parseD
             } else {
                 JSTokenType tokenType = m_token.m_type;
                 switch (m_token.m_type) {
-                case NUMBER:
+                case DOUBLE:
+                case INTEGER:
                     propertyName = Identifier::from(m_vm, m_token.m_data.doubleValue);
                     break;
                 case STRING:
@@ -1945,7 +1946,8 @@ template <class TreeBuilder> TreeProperty Parser<LexerType>::parseProperty(TreeB
             failWithMessage("Expected a ':' following the property name '", ident->impl(), "'");
         return parseGetterSetter(context, complete, type, getterOrSetterStartOffset);
     }
-    case NUMBER: {
+    case DOUBLE:
+    case INTEGER: {
         double propertyName = m_token.m_data.doubleValue;
         next();
         consumeOrFail(COLON, "Expected ':' after property name");
@@ -1979,7 +1981,7 @@ template <class TreeBuilder> TreeProperty Parser<LexerType>::parseGetterSetter(T
     double numericPropertyName = 0;
     if (m_token.m_type == IDENT || m_token.m_type == STRING)
         stringPropertyName = m_token.m_data.ident;
-    else if (m_token.m_type == NUMBER)
+    else if (m_token.m_type == DOUBLE || m_token.m_type == INTEGER)
         numericPropertyName = m_token.m_data.doubleValue;
     else
         failDueToUnexpectedToken();
@@ -2221,11 +2223,17 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePrimaryExpre
         next();
         return context.createString(location, ident);
     }
-    case NUMBER: {
+    case DOUBLE: {
         double d = m_token.m_data.doubleValue;
         JSTokenLocation location(tokenLocation());
         next();
-        return context.createNumberExpr(location, d);
+        return context.createDoubleExpr(location, d);
+    }
+    case INTEGER: {
+        double d = m_token.m_data.doubleValue;
+        JSTokenLocation location(tokenLocation());
+        next();
+        return context.createIntegerExpr(location, d);
     }
     case NULLTOKEN: {
         JSTokenLocation location(tokenLocation());
@@ -2577,7 +2585,8 @@ template <typename LexerType> void Parser<LexerType>::printUnexpectedTokenText(W
     case STRING:
         out.print("Unexpected string literal ", getToken());
         return;
-    case NUMBER:
+    case INTEGER:
+    case DOUBLE:
         out.print("Unexpected number '", getToken(), "'");
         return;
     
index 652055a..3ff2798 100644 (file)
@@ -94,7 +94,8 @@ enum JSTokenType {
     CLOSEBRACKET,
     COMMA,
     QUESTION,
-    NUMBER,
+    INTEGER,
+    DOUBLE,
     IDENT,
     STRING,
     SEMICOLON,
index 63b1348..5884fb2 100644 (file)
@@ -71,7 +71,7 @@ public:
 
     typedef SyntaxChecker FunctionBodyBuilder;
     enum { NoneExpr = 0,
-        ResolveEvalExpr, ResolveExpr, NumberExpr, StringExpr,
+        ResolveEvalExpr, ResolveExpr, IntegerExpr, DoubleExpr, StringExpr,
         ThisExpr, NullExpr, BoolExpr, RegExpExpr, ObjectLiteralExpr,
         FunctionExpr, ClassExpr, BracketExpr, DotExpr, CallExpr,
         NewExpr, PreExpr, PostExpr, UnaryExpr, BinaryExpr,
@@ -151,7 +151,8 @@ public:
     ExpressionType createObjectLiteral(const JSTokenLocation&, int) { return ObjectLiteralExpr; }
     ExpressionType createArray(const JSTokenLocation&, int) { return ArrayLiteralExpr; }
     ExpressionType createArray(const JSTokenLocation&, int, int) { return ArrayLiteralExpr; }
-    ExpressionType createNumberExpr(const JSTokenLocation&, double) { return NumberExpr; }
+    ExpressionType createDoubleExpr(const JSTokenLocation&, double) { return DoubleExpr; }
+    ExpressionType createIntegerExpr(const JSTokenLocation&, double) { return IntegerExpr; }
     ExpressionType createString(const JSTokenLocation&, const Identifier*) { return StringExpr; }
     ExpressionType createBoolean(const JSTokenLocation&, bool) { return BoolExpr; }
     ExpressionType createNull(const JSTokenLocation&) { return NullExpr; }
index acab3fa..8a261e3 100644 (file)
@@ -124,8 +124,15 @@ inline uint32_t toUInt32(double number)
 int64_t tryConvertToInt52(double);
 bool isInt52(double);
 
+enum class SourceCodeRepresentation {
+    Other,
+    Integer,
+    Double
+};
+
 class JSValue {
     friend struct EncodedJSValueHashTraits;
+    friend struct EncodedJSValueWithRepresentationHashTraits;
     friend class AssemblyHelpers;
     friend class JIT;
     friend class JITSlowPathCall;
@@ -448,7 +455,26 @@ struct EncodedJSValueHashTraits : HashTraits<EncodedJSValue> {
 };
 #endif
 
-typedef HashMap<EncodedJSValue, unsigned, EncodedJSValueHash, EncodedJSValueHashTraits> JSValueMap;
+typedef std::pair<EncodedJSValue, SourceCodeRepresentation> EncodedJSValueWithRepresentation;
+
+struct EncodedJSValueWithRepresentationHashTraits : HashTraits<EncodedJSValueWithRepresentation> {
+    static const bool emptyValueIsZero = false;
+    static EncodedJSValueWithRepresentation emptyValue() { return std::make_pair(JSValue::encode(JSValue()), SourceCodeRepresentation::Other); }
+    static void constructDeletedValue(EncodedJSValueWithRepresentation& slot) { slot = std::make_pair(JSValue::encode(JSValue(JSValue::HashTableDeletedValue)), SourceCodeRepresentation::Other); }
+    static bool isDeletedValue(EncodedJSValueWithRepresentation value) { return value == std::make_pair(JSValue::encode(JSValue(JSValue::HashTableDeletedValue)), SourceCodeRepresentation::Other); }
+};
+
+struct EncodedJSValueWithRepresentationHash {
+    static unsigned hash(const EncodedJSValueWithRepresentation& value)
+    {
+        return WTF::pairIntHash(EncodedJSValueHash::hash(value.first), IntHash<SourceCodeRepresentation>::hash(value.second));
+    }
+    static bool equal(const EncodedJSValueWithRepresentation& a, const EncodedJSValueWithRepresentation& b)
+    {
+        return a == b;
+    }
+    static const bool safeToCompareToEmptyOrDeleted = true;
+};
 
 // Stand-alone helper functions.
 inline JSValue jsNull()
diff --git a/Source/JavaScriptCore/tests/stress/arith-add-with-constants.js b/Source/JavaScriptCore/tests/stress/arith-add-with-constants.js
new file mode 100644 (file)
index 0000000..27be428
--- /dev/null
@@ -0,0 +1,222 @@
+// Test value + 0.
+function arithAddIdentityWrittenAsInteger(x) {
+    var a = x + 0;
+    var b = 0 + x;
+    if (!(isNaN(x) && isNaN(a) && isNaN(b)) && a !== b)
+        throw "Internal error on arithAddIdentityWrittenAsInteger, a = " + a + " b = " + b;
+    return a;
+}
+noInline(arithAddIdentityWrittenAsInteger);
+
+function testArithAddIdentityWrittenAsInteger() {
+    for (var i = 0; i < 1e4; ++i) {
+        var result = arithAddIdentityWrittenAsInteger(i);
+        if (result !== i) {
+            throw "arithAddIdentityWrittenAsInteger(i) = " + result + ", expected " + i;
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {
+        var result = arithAddIdentityWrittenAsInteger(-0);
+        if (result !== -0) {
+            throw "arithAddIdentityWrittenAsInteger(-0) = " + result + ", expected -0";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {
+        var testValue = i + .5;
+        var result = arithAddIdentityWrittenAsInteger(testValue);
+        if (result !== testValue) {
+            throw "arithAddIdentityWrittenAsInteger(i) = " + result + ", expected " + testValue;
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {;
+        var result = arithAddIdentityWrittenAsInteger(NaN);
+        if (!isNaN(result)) {
+            throw "arithAddIdentityWrittenAsInteger(NaN) = " + result + ", expected NaN";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {;
+        var result = arithAddIdentityWrittenAsInteger(Infinity);
+        if (isFinite(result)) {
+            throw "arithAddIdentityWrittenAsInteger(Infinity) = " + result + ", expected Infinity";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {;
+        var result = arithAddIdentityWrittenAsInteger(-Infinity);
+        if (isFinite(result) || result >= 0) {
+            throw "arithAddIdentityWrittenAsInteger(-Infinity) = " + result + ", expected -Infinity";
+        }
+    }
+}
+testArithAddIdentityWrittenAsInteger();
+
+
+function arithAddIdentityWrittenAsDouble(x) {
+    var a = x + 0.0;
+    var b = 0. + x;
+    if (!(isNaN(x) && isNaN(a) && isNaN(b)) && a !== b)
+        throw "Internal error on arithAddIdentityWrittenAsDouble, a = " + a + " b = " + b;
+    return a;
+}
+noInline(arithAddIdentityWrittenAsDouble);
+
+function testArithAddIdentityWrittenAsDouble() {
+    for (var i = 0; i < 1e4; ++i) {
+        var result = arithAddIdentityWrittenAsDouble(i);
+        if (result !== i) {
+            throw "arithAddIdentityWrittenAsDouble(i) = " + result + ", expected " + i;
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {
+        var result = arithAddIdentityWrittenAsDouble(-0);
+        if (result !== -0) {
+            throw "arithAddIdentityWrittenAsDouble(-0) = " + result + ", expected -0 ";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {
+        var testValue = i + .5;
+        var result = arithAddIdentityWrittenAsDouble(testValue);
+        if (result !== testValue) {
+            throw "arithAddIdentityWrittenAsDouble(i) = " + result + ", expected " + testValue;
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {;
+        var result = arithAddIdentityWrittenAsDouble(NaN);
+        if (!isNaN(result)) {
+            throw "arithAddIdentityWrittenAsDouble(NaN) = " + result + ", expected NaN";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {;
+        var result = arithAddIdentityWrittenAsDouble(Infinity);
+        if (isFinite(result)) {
+            throw "arithAddIdentityWrittenAsDouble(Infinity) = " + result + ", expected Infinity";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {;
+        var result = arithAddIdentityWrittenAsDouble(-Infinity);
+        if (isFinite(result) || result >= 0) {
+            throw "arithAddIdentityWrittenAsDouble(-Infinity) = " + result + ", expected -Infinity";
+        }
+    }
+}
+testArithAddIdentityWrittenAsDouble();
+
+
+// Test "value + 42".
+function arithAdd42WrittenAsInteger(x) {
+    var a = x + 42;
+    var b = 42 + x;
+    if (!(isNaN(x) && isNaN(a) && isNaN(b)) && a !== b)
+        throw "Internal error on arithAdd42WrittenAsInteger, a = " + a + " b = " + b;
+    return a;
+}
+noInline(arithAdd42WrittenAsInteger);
+
+function testArithAdd42WrittenAsInteger() {
+    for (var i = 0; i < 1e4; ++i) {
+        var result = arithAdd42WrittenAsInteger(13);
+        if (result !== 55) {
+            throw "arithAdd42WrittenAsInteger(13) = " + result + ", expected 55";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {
+        var result = arithAdd42WrittenAsInteger(-0);
+        if (result !== 42) {
+            throw "arithAdd42WrittenAsInteger(-0) = " + result + ", expected 42";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {
+        var result = arithAdd42WrittenAsInteger(13.3);
+        if (result !== 55.3) {
+            throw "arithAdd42WrittenAsInteger(13.3) = " + result + ", expected 55.3";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {;
+        var result = arithAdd42WrittenAsInteger(NaN);
+        if (!isNaN(result)) {
+            throw "arithAdd42WrittenAsInteger(NaN) = " + result + ", expected NaN";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {;
+        var result = arithAdd42WrittenAsInteger(Infinity);
+        if (isFinite(result)) {
+            throw "arithAdd42WrittenAsInteger(Infinity) = " + result + ", expected Infinity";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {;
+        var result = arithAdd42WrittenAsInteger(-Infinity);
+        if (isFinite(result) || result >= 0) {
+            throw "arithAdd42WrittenAsInteger(-Infinity) = " + result + ", expected -Infinity";
+        }
+    }
+}
+testArithAdd42WrittenAsInteger();
+
+
+function arithAdd42WrittenAsDouble(x) {
+    var a = x + 42.0;
+    var b = 42. + x;
+    if (!(isNaN(x) && isNaN(a) && isNaN(b)) && a !== b)
+        throw "Internal error on arithAdd42WrittenAsDouble, a = " + a + " b = " + b;
+    return a;
+}
+noInline(arithAdd42WrittenAsDouble);
+
+function testArithAdd42WrittenAsDouble() {
+    for (var i = 0; i < 1e4; ++i) {
+        var result = arithAdd42WrittenAsDouble(13);
+        if (result !== 55) {
+            throw "arithAdd42WrittenAsDouble(i) = " + result + ", expected 55";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {
+        var result = arithAdd42WrittenAsDouble(-0);
+        if (result !== 42) {
+            throw "arithAdd42WrittenAsDouble(-0) = " + result + ", expected 42";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {
+        var result = arithAdd42WrittenAsDouble(13.3);
+        if (result !== 55.3) {
+            throw "arithAdd42WrittenAsDouble(13.3) = " + result + ", expected 55.3";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {;
+        var result = arithAdd42WrittenAsDouble(NaN);
+        if (!isNaN(result)) {
+            throw "arithAdd42WrittenAsDouble(NaN) = " + result + ", expected NaN";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {;
+        var result = arithAdd42WrittenAsDouble(Infinity);
+        if (isFinite(result)) {
+            throw "arithAdd42WrittenAsDouble(Infinity) = " + result + ", expected Infinity";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {;
+        var result = arithAdd42WrittenAsDouble(-Infinity);
+        if (isFinite(result) || result >= 0) {
+            throw "arithAdd42WrittenAsDouble(-Infinity) = " + result + ", expected -Infinity";
+        }
+    }
+}
+testArithAdd42WrittenAsDouble();
\ No newline at end of file
diff --git a/Source/JavaScriptCore/tests/stress/arith-mul-with-constants.js b/Source/JavaScriptCore/tests/stress/arith-mul-with-constants.js
new file mode 100644 (file)
index 0000000..156ab79
--- /dev/null
@@ -0,0 +1,222 @@
+// Test value * 1.
+function arithMulIdentityWrittenAsInteger(x) {
+    var a = x * 1;
+    var b = 1 * x;
+    if (!(isNaN(x) && isNaN(a) && isNaN(b)) && a !== b)
+        throw "Internal error on arithMulIdentityWrittenAsInteger, a = " + a + " b = " + b;
+    return a;
+}
+noInline(arithMulIdentityWrittenAsInteger);
+
+function testArithMulIdentityWrittenAsInteger() {
+    for (var i = 0; i < 1e4; ++i) {
+        var result = arithMulIdentityWrittenAsInteger(i);
+        if (result !== i) {
+            throw "arithMulIdentityWrittenAsInteger(i) = " + result + ", expected " + i;
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {
+        var result = arithMulIdentityWrittenAsInteger(-0);
+        if (result !== -0) {
+            throw "arithMulIdentityWrittenAsInteger(-0) = " + result + ", expected -0";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {
+        var testValue = i + .5;
+        var result = arithMulIdentityWrittenAsInteger(testValue);
+        if (result !== testValue) {
+            throw "arithMulIdentityWrittenAsInteger(i) = " + result + ", expected " + testValue;
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {;
+        var result = arithMulIdentityWrittenAsInteger(NaN);
+        if (!isNaN(result)) {
+            throw "arithMulIdentityWrittenAsInteger(NaN) = " + result + ", expected NaN";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {;
+        var result = arithMulIdentityWrittenAsInteger(Infinity);
+        if (isFinite(result)) {
+            throw "arithMulIdentityWrittenAsInteger(Infinity) = " + result + ", expected Infinity";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {;
+        var result = arithMulIdentityWrittenAsInteger(-Infinity);
+        if (isFinite(result) || result >= 0) {
+            throw "arithMulIdentityWrittenAsInteger(-Infinity) = " + result + ", expected -Infinity";
+        }
+    }
+}
+testArithMulIdentityWrittenAsInteger();
+
+
+function arithMulIdentityWrittenAsDouble(x) {
+    var a = x * 1.0;
+    var b = 1. * x;
+    if (!(isNaN(x) && isNaN(a) && isNaN(b)) && a !== b)
+        throw "Internal error on arithMulIdentityWrittenAsDouble, a = " + a + " b = " + b;
+    return a;
+}
+noInline(arithMulIdentityWrittenAsDouble);
+
+function testArithMulIdentityWrittenAsDouble() {
+    for (var i = 0; i < 1e4; ++i) {
+        var result = arithMulIdentityWrittenAsDouble(i);
+        if (result !== i) {
+            throw "arithMulIdentityWrittenAsDouble(i) = " + result + ", expected " + i;
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {
+        var result = arithMulIdentityWrittenAsDouble(-0);
+        if (result !== -0) {
+            throw "arithMulIdentityWrittenAsDouble(-0) = " + result + ", expected -0 ";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {
+        var testValue = i + .5;
+        var result = arithMulIdentityWrittenAsDouble(testValue);
+        if (result !== testValue) {
+            throw "arithMulIdentityWrittenAsDouble(i) = " + result + ", expected " + testValue;
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {;
+        var result = arithMulIdentityWrittenAsDouble(NaN);
+        if (!isNaN(result)) {
+            throw "arithMulIdentityWrittenAsDouble(NaN) = " + result + ", expected NaN";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {;
+        var result = arithMulIdentityWrittenAsDouble(Infinity);
+        if (isFinite(result)) {
+            throw "arithMulIdentityWrittenAsDouble(Infinity) = " + result + ", expected Infinity";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {;
+        var result = arithMulIdentityWrittenAsDouble(-Infinity);
+        if (isFinite(result) || result >= 0) {
+            throw "arithMulIdentityWrittenAsDouble(-Infinity) = " + result + ", expected -Infinity";
+        }
+    }
+}
+testArithMulIdentityWrittenAsDouble();
+
+
+// Test "value * 42".
+function arithMul42WrittenAsInteger(x) {
+    var a = x * 42;
+    var b = 42 * x;
+    if (!(isNaN(x) && isNaN(a) && isNaN(b)) && a !== b)
+        throw "Internal error on arithMul42WrittenAsInteger, a = " + a + " b = " + b;
+    return a;
+}
+noInline(arithMul42WrittenAsInteger);
+
+function testArithMul42WrittenAsInteger() {
+    for (var i = 0; i < 1e4; ++i) {
+        var result = arithMul42WrittenAsInteger(13);
+        if (result !== 546) {
+            throw "arithMul42WrittenAsInteger(13) = " + result + ", expected 546";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {
+        var result = arithMul42WrittenAsInteger(-0);
+        if (result !== -0) {
+            throw "arithMul42WrittenAsInteger(-0) = " + result + ", expected -0";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {
+        var result = arithMul42WrittenAsInteger(13.3);
+        if (result !== 558.6) {
+            throw "arithMul42WrittenAsInteger(13.3) = " + result + ", expected 558.6";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {;
+        var result = arithMul42WrittenAsInteger(NaN);
+        if (!isNaN(result)) {
+            throw "arithMul42WrittenAsInteger(NaN) = " + result + ", expected NaN";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {;
+        var result = arithMul42WrittenAsInteger(Infinity);
+        if (isFinite(result)) {
+            throw "arithMul42WrittenAsInteger(Infinity) = " + result + ", expected Infinity";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {;
+        var result = arithMul42WrittenAsInteger(-Infinity);
+        if (isFinite(result) || result >= 0) {
+            throw "arithMul42WrittenAsInteger(-Infinity) = " + result + ", expected -Infinity";
+        }
+    }
+}
+testArithMul42WrittenAsInteger();
+
+
+function arithMul42WrittenAsDouble(x) {
+    var a = x * 42.0;
+    var b = 42. * x;
+    if (!(isNaN(x) && isNaN(a) && isNaN(b)) && a !== b)
+        throw "Internal error on arithMul42WrittenAsDouble, a = " + a + " b = " + b;
+    return a;
+}
+noInline(arithMul42WrittenAsDouble);
+
+function testArithMul42WrittenAsDouble() {
+    for (var i = 0; i < 1e4; ++i) {
+        var result = arithMul42WrittenAsDouble(13);
+        if (result !== 546) {
+            throw "arithMul42WrittenAsDouble(i) = " + result + ", expected 546";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {
+        var result = arithMul42WrittenAsDouble(-0);
+        if (result !== -0) {
+            throw "arithMul42WrittenAsDouble(-0) = " + result + ", expected -0";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {
+        var result = arithMul42WrittenAsDouble(13.3);
+        if (result !== 558.6) {
+            throw "arithMul42WrittenAsDouble(13.3) = " + result + ", expected 558.6";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {;
+        var result = arithMul42WrittenAsDouble(NaN);
+        if (!isNaN(result)) {
+            throw "arithMul42WrittenAsDouble(NaN) = " + result + ", expected NaN";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {;
+        var result = arithMul42WrittenAsDouble(Infinity);
+        if (isFinite(result)) {
+            throw "arithMul42WrittenAsDouble(Infinity) = " + result + ", expected Infinity";
+        }
+    }
+
+    for (var i = 0; i < 1e4; ++i) {;
+        var result = arithMul42WrittenAsDouble(-Infinity);
+        if (isFinite(result) || result >= 0) {
+            throw "arithMul42WrittenAsDouble(-Infinity) = " + result + ", expected -Infinity";
+        }
+    }
+}
+testArithMul42WrittenAsDouble();
\ No newline at end of file