Ignore:
Timestamp:
Dec 6, 2009, 1:42:03 AM (15 years ago)
Author:
[email protected]
Message:

2009-12-05 Maciej Stachowiak <[email protected]>

Reviewed by Oliver Hunt.

conway benchmark spends half it's time in op_less (jump fusion fails)
https://bugs.webkit.org/show_bug.cgi?id=32190

<1% speedup on SunSpider and V8
2x speedup on "conway" benchmark


Two optimizations:

1) Improve codegen for logical operators &&,
and ! in a condition context


When generating code for combinations of &&,
and !, in a

condition context (i.e. in an if statement or loop condition), we
used to produce a value, and then separately jump based on its
truthiness. Now we pass the false and true targets in, and let the
logical operators generate jumps directly. This helps in four
ways:

a) Individual clauses of a short-circuit logical operator can now
jump directly to the then or else clause of an if statement (or to
the top or exit of a loop) instead of jumping to a jump.


b) It used to be that jump fusion with the condition of the first
clause of a logical operator was inhibited, because the register
was ref'd to be used later, in the actual condition jump; this no
longer happens since a jump straight to the final target is
generated directly.

c) It used to be that jump fusion with the condition of the second
clause of a logical operator was inhibited, because there was a
jump target right after the second clause and before the actual
condition jump. But now it's no longer necessary for the first
clause to jump there so jump fusion is not blocked.

d) We avoid generating excess mov statements in some cases.


As a concrete example this source:


if (!((x < q && y < q)
(t < q && z < q))) {

...

}


Used to generate this bytecode:


[ 34] less r1, r-15, r-19
[ 38] jfalse r1, 7(->45)
[ 41] less r1, r-16, r-19
[ 45] jtrue r1, 14(->59)
[ 48] less r1, r-17, r-19
[ 52] jfalse r1, 7(->59)
[ 55] less r1, r-18, r-19
[ 59] jtrue r1, 17(->76)


And now generates this bytecode (also taking advantage of the second optimization below):


[ 34] jnless r-15, r-19, 8(->42)
[ 38] jless r-16, r-19, 26(->64)
[ 42] jnless r-17, r-19, 8(->50)
[ 46] jless r-18, r-19, 18(->64)


Note the jump fusion and the fact that there's less jump
indirection - three of the four jumps go straight to the target
clause instead of indirecting through another jump.


2) Implement jless opcode to take advantage of the above, since we'll now often generate
a less followed by a jtrue where fusion is not forbidden.


  • parser/Nodes.h: (JSC::ExpressionNode::hasConditionContextCodegen): Helper function to determine whether a node supports special conditional codegen. Return false as this is the default. (JSC::ExpressionNode::emitBytecodeInConditionContext): Assert not reached - only really defined for nodes that do have conditional codegen. (JSC::UnaryOpNode::expr): Add const version. (JSC::LogicalNotNode::hasConditionContextCodegen): Returne true only if subexpression supports it. (JSC::LogicalOpNode::hasConditionContextCodegen): Return true.
  • parser/Nodes.cpp: (JSC::LogicalNotNode::emitBytecodeInConditionContext): Implemented - just swap the true and false targets for the child node. (JSC::LogicalOpNode::emitBytecodeInConditionContext): Implemented - handle jumps directly, improving codegen quality. Also handles further nested conditional codegen. (JSC::ConditionalNode::emitBytecode): Use condition context codegen when available. (JSC::IfNode::emitBytecode): ditto (JSC::IfElseNode::emitBytecode): ditto (JSC::DoWhileNode::emitBytecode): ditto (JSC::WhileNode::emitBytecode): ditto (JSC::ForNode::emitBytecode): ditto
  • bytecode/Opcode.h:
  • Added loop_if_false opcode - needed now that falsey jumps can be backwards.
  • Added jless opcode to take advantage of new fusion opportunities.
  • bytecode/CodeBlock.cpp: (JSC::CodeBlock::dump): Handle above.
  • bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::emitJumpIfTrue): Add peephole for less + jtrue ==> jless. (JSC::BytecodeGenerator::emitJumpIfFalse): Add handling of backwrds falsey jumps.
  • bytecompiler/BytecodeGenerator.h: (JSC::BytecodeGenerator::emitNodeInConditionContext): Wrapper to handle tracking of overly deep expressions etc.
  • interpreter/Interpreter.cpp: (JSC::Interpreter::privateExecute): Implement the two new opcodes (loop_if_false, jless).
  • jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): Implement JIT support for the two new opcodes. (JSC::JIT::privateCompileSlowCases): ditto
  • jit/JIT.h:
  • jit/JITArithmetic.cpp: (JSC::JIT::emit_op_jless): (JSC::JIT::emitSlow_op_jless): ditto (JSC::JIT::emitBinaryDoubleOp): ditto
  • jit/JITOpcodes.cpp: (JSC::JIT::emitSlow_op_loop_if_less): ditto (JSC::JIT::emit_op_loop_if_false): ditto (JSC::JIT::emitSlow_op_loop_if_false): ditto
  • jit/JITStubs.cpp:
  • jit/JITStubs.h: (JSC::):

2009-12-05 Maciej Stachowiak <[email protected]>

Reviewed by Oliver Hunt.

conway benchmark spends half it's time in op_less (jump fusion fails)
https://bugs.webkit.org/show_bug.cgi?id=32190

  • fast/js/codegen-loops-logical-nodes-expected.txt:
  • fast/js/script-tests/codegen-loops-logical-nodes.js: Update to test some newly sensitive cases of codegen that were not already covered.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/parser/Nodes.cpp

    r50916 r51735  
    812812}
    813813
     814
     815// ------------------------------ LogicalNotNode -----------------------------------
     816
     817void LogicalNotNode::emitBytecodeInConditionContext(BytecodeGenerator& generator, Label* trueTarget, Label* falseTarget, bool fallThroughMeansTrue)
     818{
     819    ASSERT(expr()->hasConditionContextCodegen());
     820
     821    // reverse the true and false targets
     822    generator.emitNodeInConditionContext(expr(), falseTarget, trueTarget, !fallThroughMeansTrue);
     823}
     824
     825
    814826// ------------------------------ Binary Operation Nodes -----------------------------------
    815827
     
    10181030}
    10191031
     1032void LogicalOpNode::emitBytecodeInConditionContext(BytecodeGenerator& generator, Label* trueTarget, Label* falseTarget, bool fallThroughMeansTrue)
     1033{
     1034    if (m_expr1->hasConditionContextCodegen()) {
     1035        RefPtr<Label> afterExpr1 = generator.newLabel();
     1036        if (m_operator == OpLogicalAnd)
     1037            generator.emitNodeInConditionContext(m_expr1, afterExpr1.get(), falseTarget, true);
     1038        else
     1039            generator.emitNodeInConditionContext(m_expr1, trueTarget, afterExpr1.get(), false);
     1040        generator.emitLabel(afterExpr1.get());
     1041    } else {
     1042        RegisterID* temp = generator.emitNode(m_expr1);
     1043        if (m_operator == OpLogicalAnd)
     1044            generator.emitJumpIfFalse(temp, falseTarget);
     1045        else
     1046            generator.emitJumpIfTrue(temp, trueTarget);
     1047    }
     1048
     1049    if (m_expr2->hasConditionContextCodegen())
     1050        generator.emitNodeInConditionContext(m_expr2, trueTarget, falseTarget, fallThroughMeansTrue);
     1051    else {
     1052        RegisterID* temp = generator.emitNode(m_expr2);
     1053        if (fallThroughMeansTrue)
     1054            generator.emitJumpIfFalse(temp, falseTarget);
     1055        else
     1056            generator.emitJumpIfTrue(temp, trueTarget);
     1057    }
     1058}
     1059
    10201060// ------------------------------ ConditionalNode ------------------------------
    10211061
     
    10261066    RefPtr<Label> afterElse = generator.newLabel();
    10271067
    1028     RegisterID* cond = generator.emitNode(m_logical);
    1029     generator.emitJumpIfFalse(cond, beforeElse.get());
     1068    if (m_logical->hasConditionContextCodegen()) {
     1069        RefPtr<Label> beforeThen = generator.newLabel();
     1070        generator.emitNodeInConditionContext(m_logical, beforeThen.get(), beforeElse.get(), true);
     1071        generator.emitLabel(beforeThen.get());
     1072    } else {
     1073        RegisterID* cond = generator.emitNode(m_logical);
     1074        generator.emitJumpIfFalse(cond, beforeElse.get());
     1075    }
    10301076
    10311077    generator.emitNode(newDst.get(), m_expr1);
     
    13441390    RefPtr<Label> afterThen = generator.newLabel();
    13451391
    1346     RegisterID* cond = generator.emitNode(m_condition);
    1347     generator.emitJumpIfFalse(cond, afterThen.get());
     1392    if (m_condition->hasConditionContextCodegen()) {
     1393        RefPtr<Label> beforeThen = generator.newLabel();
     1394        generator.emitNodeInConditionContext(m_condition, beforeThen.get(), afterThen.get(), true);
     1395        generator.emitLabel(beforeThen.get());
     1396    } else {
     1397        RegisterID* cond = generator.emitNode(m_condition);
     1398        generator.emitJumpIfFalse(cond, afterThen.get());
     1399    }
    13481400
    13491401    generator.emitNode(dst, m_ifBlock);
     
    13631415    RefPtr<Label> afterElse = generator.newLabel();
    13641416
    1365     RegisterID* cond = generator.emitNode(m_condition);
    1366     generator.emitJumpIfFalse(cond, beforeElse.get());
     1417    if (m_condition->hasConditionContextCodegen()) {
     1418        RefPtr<Label> beforeThen = generator.newLabel();
     1419        generator.emitNodeInConditionContext(m_condition, beforeThen.get(), beforeElse.get(), true);
     1420        generator.emitLabel(beforeThen.get());
     1421    } else {
     1422        RegisterID* cond = generator.emitNode(m_condition);
     1423        generator.emitJumpIfFalse(cond, beforeElse.get());
     1424    }
    13671425
    13681426    generator.emitNode(dst, m_ifBlock);
     
    13941452    generator.emitLabel(scope->continueTarget());
    13951453    generator.emitDebugHook(WillExecuteStatement, m_expr->lineNo(), m_expr->lineNo());
    1396     RegisterID* cond = generator.emitNode(m_expr);
    1397     generator.emitJumpIfTrue(cond, topOfLoop.get());
     1454    if (m_expr->hasConditionContextCodegen())
     1455        generator.emitNodeInConditionContext(m_expr, topOfLoop.get(), scope->breakTarget(), false);
     1456    else {
     1457        RegisterID* cond = generator.emitNode(m_expr);
     1458        generator.emitJumpIfTrue(cond, topOfLoop.get());
     1459    }
    13981460
    13991461    generator.emitLabel(scope->breakTarget());
     
    14161478    generator.emitLabel(scope->continueTarget());
    14171479    generator.emitDebugHook(WillExecuteStatement, m_expr->lineNo(), m_expr->lineNo());
    1418     RegisterID* cond = generator.emitNode(m_expr);
    1419     generator.emitJumpIfTrue(cond, topOfLoop.get());
     1480
     1481    if (m_expr->hasConditionContextCodegen())
     1482        generator.emitNodeInConditionContext(m_expr, topOfLoop.get(), scope->breakTarget(), false);
     1483    else {
     1484        RegisterID* cond = generator.emitNode(m_expr);
     1485        generator.emitJumpIfTrue(cond, topOfLoop.get());
     1486    }
    14201487
    14211488    generator.emitLabel(scope->breakTarget());
     
    14511518    generator.emitLabel(condition.get());
    14521519    if (m_expr2) {
    1453         RegisterID* cond = generator.emitNode(m_expr2);
    1454         generator.emitJumpIfTrue(cond, topOfLoop.get());
     1520        if (m_expr2->hasConditionContextCodegen())
     1521            generator.emitNodeInConditionContext(m_expr2, topOfLoop.get(), scope->breakTarget(), false);
     1522        else {
     1523            RegisterID* cond = generator.emitNode(m_expr2);
     1524            generator.emitJumpIfTrue(cond, topOfLoop.get());
     1525        }
    14551526    } else
    14561527        generator.emitJump(topOfLoop.get());
Note: See TracChangeset for help on using the changeset viewer.