Ignore:
Timestamp:
Feb 27, 2015, 7:21:37 PM (10 years ago)
Author:
[email protected]
Message:

[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 <[email protected]> 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.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/parser/ASTBuilder.h

    r180518 r180813  
    193193        return new (m_parserArena) ArrayNode(location, elisions, elems);
    194194    }
    195     ExpressionNode* createNumberExpr(const JSTokenLocation& location, double d)
     195    ExpressionNode* createDoubleExpr(const JSTokenLocation& location, double d)
    196196    {
    197197        incConstants();
    198         return new (m_parserArena) NumberNode(location, d);
     198        return new (m_parserArena) DoubleNode(location, d);
     199    }
     200    ExpressionNode* createIntegerExpr(const JSTokenLocation& location, double d)
     201    {
     202        incConstants();
     203        return new (m_parserArena) IntegerNode(location, d);
    199204    }
    200205
     
    749754        m_scope.m_features |= EvalFeature;
    750755    }
    751     ExpressionNode* createNumber(const JSTokenLocation& location, double d)
    752     {
    753         return new (m_parserArena) NumberNode(location, d);
    754     }
    755    
     756    ExpressionNode* createIntegerLikeNumber(const JSTokenLocation& location, double d)
     757    {
     758        return new (m_parserArena) IntegerNode(location, d);
     759    }
     760    ExpressionNode* createDoubleLikeNumber(const JSTokenLocation& location, double d)
     761    {
     762        return new (m_parserArena) DoubleNode(location, d);
     763    }
     764    ExpressionNode* createNumberFromBinaryOperation(const JSTokenLocation& location, double value, const NumberNode& originalNodeA, const NumberNode& originalNodeB)
     765    {
     766        if (originalNodeA.isIntegerNode() && originalNodeB.isIntegerNode())
     767            return createIntegerLikeNumber(location, value);
     768        return createDoubleLikeNumber(location, value);
     769    }
     770    ExpressionNode* createNumberFromUnaryOperation(const JSTokenLocation& location, double value, const NumberNode& originalNode)
     771    {
     772        if (originalNode.isIntegerNode())
     773            return createIntegerLikeNumber(location, value);
     774        return createDoubleLikeNumber(location, value);
     775    }
     776
    756777    VM* m_vm;
    757778    ParserArena& m_parserArena;
     
    794815{
    795816    if (n->isNumber()) {
    796         NumberNode* numberNode = static_cast<NumberNode*>(n);
    797         numberNode->setValue(-numberNode->value());
    798         return numberNode;
     817        const NumberNode& numberNode = static_cast<const NumberNode&>(*n);
     818        return createNumberFromUnaryOperation(location, -numberNode.value(), numberNode);
    799819    }
    800820
     
    805825{
    806826    if (expr->isNumber())
    807         return createNumber(location, ~toInt32(static_cast<NumberNode*>(expr)->value()));
     827        return createIntegerLikeNumber(location, ~toInt32(static_cast<NumberNode*>(expr)->value()));
    808828    return new (m_parserArena) BitwiseNotNode(location, expr);
    809829}
     
    814834    expr2 = expr2->stripUnaryPlus();
    815835
    816     if (expr1->isNumber() && expr2->isNumber())
    817         return createNumber(location, static_cast<NumberNode*>(expr1)->value() * static_cast<NumberNode*>(expr2)->value());
     836    if (expr1->isNumber() && expr2->isNumber()) {
     837        const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1);
     838        const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2);
     839        return createNumberFromBinaryOperation(location, numberExpr1.value() * numberExpr2.value(), numberExpr1, numberExpr2);
     840    }
    818841
    819842    if (expr1->isNumber() && static_cast<NumberNode*>(expr1)->value() == 1)
     
    831854    expr2 = expr2->stripUnaryPlus();
    832855
    833     if (expr1->isNumber() && expr2->isNumber())
    834         return createNumber(location, static_cast<NumberNode*>(expr1)->value() / static_cast<NumberNode*>(expr2)->value());
     856    if (expr1->isNumber() && expr2->isNumber()) {
     857        const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1);
     858        const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2);
     859        double result = numberExpr1.value() / numberExpr2.value();
     860        if (static_cast<int64_t>(result) == result)
     861            return createNumberFromBinaryOperation(location, result, numberExpr1, numberExpr2);
     862        return createDoubleLikeNumber(location, result);
     863    }
    835864    return new (m_parserArena) DivNode(location, expr1, expr2, rightHasAssignments);
    836865}
     
    840869    expr1 = expr1->stripUnaryPlus();
    841870    expr2 = expr2->stripUnaryPlus();
    842    
    843     if (expr1->isNumber() && expr2->isNumber())
    844         return createNumber(location, fmod(static_cast<NumberNode*>(expr1)->value(), static_cast<NumberNode*>(expr2)->value()));
     871
     872    if (expr1->isNumber() && expr2->isNumber()) {
     873        const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1);
     874        const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2);
     875        return createIntegerLikeNumber(location, fmod(numberExpr1.value(), numberExpr2.value()));
     876    }
    845877    return new (m_parserArena) ModNode(location, expr1, expr2, rightHasAssignments);
    846878}
     
    848880ExpressionNode* ASTBuilder::makeAddNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
    849881{
    850     if (expr1->isNumber() && expr2->isNumber())
    851         return createNumber(location, static_cast<NumberNode*>(expr1)->value() + static_cast<NumberNode*>(expr2)->value());
     882
     883    if (expr1->isNumber() && expr2->isNumber()) {
     884        const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1);
     885        const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2);
     886        return createNumberFromBinaryOperation(location, numberExpr1.value() + numberExpr2.value(), numberExpr1, numberExpr2);
     887    }
    852888    return new (m_parserArena) AddNode(location, expr1, expr2, rightHasAssignments);
    853889}
     
    858894    expr2 = expr2->stripUnaryPlus();
    859895
    860     if (expr1->isNumber() && expr2->isNumber())
    861         return createNumber(location, static_cast<NumberNode*>(expr1)->value() - static_cast<NumberNode*>(expr2)->value());
     896    if (expr1->isNumber() && expr2->isNumber()) {
     897        const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1);
     898        const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2);
     899        return createNumberFromBinaryOperation(location, numberExpr1.value() - numberExpr2.value(), numberExpr1, numberExpr2);
     900    }
    862901    return new (m_parserArena) SubNode(location, expr1, expr2, rightHasAssignments);
    863902}
     
    865904ExpressionNode* ASTBuilder::makeLeftShiftNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
    866905{
    867     if (expr1->isNumber() && expr2->isNumber())
    868         return createNumber(location, toInt32(static_cast<NumberNode*>(expr1)->value()) << (toUInt32(static_cast<NumberNode*>(expr2)->value()) & 0x1f));
     906    if (expr1->isNumber() && expr2->isNumber()) {
     907        const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1);
     908        const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2);
     909        return createIntegerLikeNumber(location, toInt32(numberExpr1.value()) << (toUInt32(numberExpr2.value()) & 0x1f));
     910    }
    869911    return new (m_parserArena) LeftShiftNode(location, expr1, expr2, rightHasAssignments);
    870912}
     
    872914ExpressionNode* ASTBuilder::makeRightShiftNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
    873915{
    874     if (expr1->isNumber() && expr2->isNumber())
    875         return createNumber(location, toInt32(static_cast<NumberNode*>(expr1)->value()) >> (toUInt32(static_cast<NumberNode*>(expr2)->value()) & 0x1f));
     916    if (expr1->isNumber() && expr2->isNumber()) {
     917        const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1);
     918        const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2);
     919        return createIntegerLikeNumber(location, toInt32(numberExpr1.value()) >> (toUInt32(numberExpr2.value()) & 0x1f));
     920    }
    876921    return new (m_parserArena) RightShiftNode(location, expr1, expr2, rightHasAssignments);
    877922}
     
    879924ExpressionNode* ASTBuilder::makeURightShiftNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
    880925{
    881     if (expr1->isNumber() && expr2->isNumber())
    882         return createNumber(location, toUInt32(static_cast<NumberNode*>(expr1)->value()) >> (toUInt32(static_cast<NumberNode*>(expr2)->value()) & 0x1f));
     926    if (expr1->isNumber() && expr2->isNumber()) {
     927        const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1);
     928        const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2);
     929        return createIntegerLikeNumber(location, toUInt32(numberExpr1.value()) >> (toUInt32(numberExpr2.value()) & 0x1f));
     930    }
    883931    return new (m_parserArena) UnsignedRightShiftNode(location, expr1, expr2, rightHasAssignments);
    884932}
     
    886934ExpressionNode* ASTBuilder::makeBitOrNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
    887935{
    888     if (expr1->isNumber() && expr2->isNumber())
    889         return createNumber(location, toInt32(static_cast<NumberNode*>(expr1)->value()) | toInt32(static_cast<NumberNode*>(expr2)->value()));
     936    if (expr1->isNumber() && expr2->isNumber()) {
     937        const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1);
     938        const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2);
     939        return createIntegerLikeNumber(location, toInt32(numberExpr1.value()) | toInt32(numberExpr2.value()));
     940    }
    890941    return new (m_parserArena) BitOrNode(location, expr1, expr2, rightHasAssignments);
    891942}
     
    893944ExpressionNode* ASTBuilder::makeBitAndNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
    894945{
    895     if (expr1->isNumber() && expr2->isNumber())
    896         return createNumber(location, toInt32(static_cast<NumberNode*>(expr1)->value()) & toInt32(static_cast<NumberNode*>(expr2)->value()));
     946    if (expr1->isNumber() && expr2->isNumber()) {
     947        const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1);
     948        const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2);
     949        return createIntegerLikeNumber(location, toInt32(numberExpr1.value()) & toInt32(numberExpr2.value()));
     950    }
    897951    return new (m_parserArena) BitAndNode(location, expr1, expr2, rightHasAssignments);
    898952}
     
    900954ExpressionNode* ASTBuilder::makeBitXOrNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
    901955{
    902     if (expr1->isNumber() && expr2->isNumber())
    903         return createNumber(location, toInt32(static_cast<NumberNode*>(expr1)->value()) ^ toInt32(static_cast<NumberNode*>(expr2)->value()));
     956    if (expr1->isNumber() && expr2->isNumber()) {
     957        const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1);
     958        const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2);
     959        return createIntegerLikeNumber(location, toInt32(numberExpr1.value()) ^ toInt32(numberExpr2.value()));
     960    }
    904961    return new (m_parserArena) BitXOrNode(location, expr1, expr2, rightHasAssignments);
    905962}
Note: See TracChangeset for help on using the changeset viewer.