2009-12-05 Maciej Stachowiak <mjs@apple.com>
authormjs@apple.com <mjs@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 6 Dec 2009 09:42:03 +0000 (09:42 +0000)
committermjs@apple.com <mjs@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 6 Dec 2009 09:42:03 +0000 (09:42 +0000)
        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  <mjs@apple.com>

        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.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@51735 268f45cc-cd09-0410-ab3c-d52691b4dbfc

17 files changed:
JavaScriptCore/ChangeLog
JavaScriptCore/bytecode/CodeBlock.cpp
JavaScriptCore/bytecode/Opcode.h
JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
JavaScriptCore/bytecompiler/BytecodeGenerator.h
JavaScriptCore/interpreter/Interpreter.cpp
JavaScriptCore/jit/JIT.cpp
JavaScriptCore/jit/JIT.h
JavaScriptCore/jit/JITArithmetic.cpp
JavaScriptCore/jit/JITOpcodes.cpp
JavaScriptCore/jit/JITStubs.cpp
JavaScriptCore/jit/JITStubs.h
JavaScriptCore/parser/Nodes.cpp
JavaScriptCore/parser/Nodes.h
LayoutTests/ChangeLog
LayoutTests/fast/js/codegen-loops-logical-nodes-expected.txt
LayoutTests/fast/js/script-tests/codegen-loops-logical-nodes.js

index 295eef3d3c911bab802833c7094c650fda813605..42dc4102c191febac2375ce7aeac378dae02380d 100644 (file)
@@ -1,3 +1,122 @@
+2009-12-05  Maciej Stachowiak  <mjs@apple.com>
+
+        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-04  Kent Hansen  <kent.hansen@nokia.com>
 
         Reviewed by Darin Adler.
index 27b72f0002b8f41d1a93830698c5d3fe2d4226fb..13bed8c29c2593053b19e4b8b3c924c4c40c0bb1 100644 (file)
@@ -876,6 +876,10 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator&
             printConditionalJump(exec, begin, it, location, "loop_if_true");
             break;
         }
+        case op_loop_if_false: {
+            printConditionalJump(exec, begin, it, location, "loop_if_false");
+            break;
+        }
         case op_jfalse: {
             printConditionalJump(exec, begin, it, location, "jfalse");
             break;
@@ -916,6 +920,13 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator&
             printf("[%4d] loop_if_less\t %s, %s, %d(->%d)\n", location, registerName(exec, r0).c_str(), registerName(exec, r1).c_str(), offset, location + offset);
             break;
         }
+        case op_jless: {
+            int r0 = (++it)->u.operand;
+            int r1 = (++it)->u.operand;
+            int offset = (++it)->u.operand;
+            printf("[%4d] jless\t\t %s, %s, %d(->%d)\n", location, registerName(exec, r0).c_str(), registerName(exec, r1).c_str(), offset, location + offset);
+            break;
+        }
         case op_loop_if_lesseq: {
             int r0 = (++it)->u.operand;
             int r1 = (++it)->u.operand;
index 4facbef5b25206df7f599a616b7b12bc0eaa86fa..e88f051796b0737da4b100399aa40846034d287d 100644 (file)
@@ -128,9 +128,11 @@ namespace JSC {
         macro(op_jneq_ptr, 4) \
         macro(op_jnless, 4) \
         macro(op_jnlesseq, 4) \
+        macro(op_jless, 4) \
         macro(op_jmp_scopes, 3) \
         macro(op_loop, 2) \
         macro(op_loop_if_true, 3) \
+        macro(op_loop_if_false, 3) \
         macro(op_loop_if_less, 4) \
         macro(op_loop_if_lesseq, 4) \
         macro(op_switch_imm, 4) \
index 04dae153749e9009e3effa340c85366adc16df3d..5e84ecefbfde59508d67a88d93a20ca2ff3c2dc8 100644 (file)
@@ -616,7 +616,7 @@ PassRefPtr<Label> BytecodeGenerator::emitJump(Label* target)
 
 PassRefPtr<Label> BytecodeGenerator::emitJumpIfTrue(RegisterID* cond, Label* target)
 {
-    if (m_lastOpcodeID == op_less && !target->isForward()) {
+    if (m_lastOpcodeID == op_less) {
         int dstIndex;
         int src1Index;
         int src2Index;
@@ -627,7 +627,7 @@ PassRefPtr<Label> BytecodeGenerator::emitJumpIfTrue(RegisterID* cond, Label* tar
             rewindBinaryOp();
 
             size_t begin = instructions().size();
-            emitOpcode(op_loop_if_less);
+            emitOpcode(target->isForward() ? op_jless : op_loop_if_less);
             instructions().append(src1Index);
             instructions().append(src2Index);
             instructions().append(target->bind(begin, instructions().size()));
@@ -692,9 +692,7 @@ PassRefPtr<Label> BytecodeGenerator::emitJumpIfTrue(RegisterID* cond, Label* tar
 
 PassRefPtr<Label> BytecodeGenerator::emitJumpIfFalse(RegisterID* cond, Label* target)
 {
-    ASSERT(target->isForward());
-
-    if (m_lastOpcodeID == op_less) {
+    if (m_lastOpcodeID == op_less && target->isForward()) {
         int dstIndex;
         int src1Index;
         int src2Index;
@@ -711,7 +709,7 @@ PassRefPtr<Label> BytecodeGenerator::emitJumpIfFalse(RegisterID* cond, Label* ta
             instructions().append(target->bind(begin, instructions().size()));
             return target;
         }
-    } else if (m_lastOpcodeID == op_lesseq) {
+    } else if (m_lastOpcodeID == op_lesseq && target->isForward()) {
         int dstIndex;
         int src1Index;
         int src2Index;
@@ -738,12 +736,12 @@ PassRefPtr<Label> BytecodeGenerator::emitJumpIfFalse(RegisterID* cond, Label* ta
             rewindUnaryOp();
 
             size_t begin = instructions().size();
-            emitOpcode(op_jtrue);
+            emitOpcode(target->isForward() ? op_jtrue : op_loop_if_true);
             instructions().append(srcIndex);
             instructions().append(target->bind(begin, instructions().size()));
             return target;
         }
-    } else if (m_lastOpcodeID == op_eq_null) {
+    } else if (m_lastOpcodeID == op_eq_null && target->isForward()) {
         int dstIndex;
         int srcIndex;
 
@@ -758,7 +756,7 @@ PassRefPtr<Label> BytecodeGenerator::emitJumpIfFalse(RegisterID* cond, Label* ta
             instructions().append(target->bind(begin, instructions().size()));
             return target;
         }
-    } else if (m_lastOpcodeID == op_neq_null) {
+    } else if (m_lastOpcodeID == op_neq_null && target->isForward()) {
         int dstIndex;
         int srcIndex;
 
@@ -776,7 +774,7 @@ PassRefPtr<Label> BytecodeGenerator::emitJumpIfFalse(RegisterID* cond, Label* ta
     }
 
     size_t begin = instructions().size();
-    emitOpcode(op_jfalse);
+    emitOpcode(target->isForward() ? op_jfalse : op_loop_if_false);
     instructions().append(cond->index());
     instructions().append(target->bind(begin, instructions().size()));
     return target;
index 4648fb57e60b2c87dfb6357b19b2cda0cf4fe9da..8b6a425194a3103dcbe81786d0efdc44b2273d2c 100644 (file)
@@ -192,6 +192,19 @@ namespace JSC {
             return emitNode(0, n);
         }
 
+        void emitNodeInConditionContext(ExpressionNode* n, Label* trueTarget, Label* falseTarget, bool fallThroughMeansTrue)
+        {
+            if (!m_codeBlock->numberOfLineInfos() || m_codeBlock->lastLineInfo().lineNumber != n->lineNo()) {
+                LineInfo info = { instructions().size(), n->lineNo() };
+                m_codeBlock->addLineInfo(info);
+            }
+            if (m_emitNodeDepth >= s_maxEmitNodeDepth)
+                emitThrowExpressionTooDeepException();
+            ++m_emitNodeDepth;
+            n->emitBytecodeInConditionContext(*this, trueTarget, falseTarget, fallThroughMeansTrue);
+            --m_emitNodeDepth;
+        }
+
         void emitExpressionInfo(unsigned divot, unsigned startOffset, unsigned endOffset)
         { 
             divot -= m_codeBlock->sourceOffset();
index ba3a7d92ba47bd5dbf5c063bdc03eb0f5d81dc23..1d69512806faf4bd9fd3a9da632ac97b85c04516 100644 (file)
@@ -2647,6 +2647,26 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi
         vPC += OPCODE_LENGTH(op_loop_if_true);
         NEXT_INSTRUCTION();
     }
+    DEFINE_OPCODE(op_loop_if_false) {
+        /* loop_if_true cond(r) target(offset)
+         
+           Jumps to offset target from the current instruction, if and
+           only if register cond converts to boolean as false.
+
+           Additionally this loop instruction may terminate JS execution is
+           the JS timeout is reached.
+         */
+        int cond = vPC[1].u.operand;
+        int target = vPC[2].u.operand;
+        if (!callFrame->r(cond).jsValue().toBoolean(callFrame)) {
+            vPC += target;
+            CHECK_FOR_TIMEOUT();
+            NEXT_INSTRUCTION();
+        }
+        
+        vPC += OPCODE_LENGTH(op_loop_if_true);
+        NEXT_INSTRUCTION();
+    }
     DEFINE_OPCODE(op_jtrue) {
         /* jtrue cond(r) target(offset)
 
@@ -2810,6 +2830,29 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi
         vPC += OPCODE_LENGTH(op_jnless);
         NEXT_INSTRUCTION();
     }
+    DEFINE_OPCODE(op_jless) {
+        /* jless src1(r) src2(r) target(offset)
+
+           Checks whether register src1 is less than register src2, as
+           with the ECMAScript '<' operator, and then jumps to offset
+           target from the current instruction, if and only if the 
+           result of the comparison is true.
+        */
+        JSValue src1 = callFrame->r(vPC[1].u.operand).jsValue();
+        JSValue src2 = callFrame->r(vPC[2].u.operand).jsValue();
+        int target = vPC[3].u.operand;
+
+        bool result = jsLess(callFrame, src1, src2);
+        CHECK_FOR_EXCEPTION();
+        
+        if (result) {
+            vPC += target;
+            NEXT_INSTRUCTION();
+        }
+
+        vPC += OPCODE_LENGTH(op_jless);
+        NEXT_INSTRUCTION();
+    }
     DEFINE_OPCODE(op_jnlesseq) {
         /* jnlesseq src1(r) src2(r) target(offset)
 
index c0d125830a973893b2916db8270126894ff11d75..585486f61343a6490542808026011dd224c5c92c 100644 (file)
@@ -250,6 +250,7 @@ void JIT::privateCompileMainPass()
         DEFINE_OP(op_jneq_null)
         DEFINE_OP(op_jneq_ptr)
         DEFINE_OP(op_jnless)
+        DEFINE_OP(op_jless)
         DEFINE_OP(op_jnlesseq)
         DEFINE_OP(op_jsr)
         DEFINE_OP(op_jtrue)
@@ -258,6 +259,7 @@ void JIT::privateCompileMainPass()
         DEFINE_OP(op_loop_if_less)
         DEFINE_OP(op_loop_if_lesseq)
         DEFINE_OP(op_loop_if_true)
+        DEFINE_OP(op_loop_if_false)
         DEFINE_OP(op_lshift)
         DEFINE_OP(op_method_check)
         DEFINE_OP(op_mod)
@@ -389,11 +391,13 @@ void JIT::privateCompileSlowCases()
         DEFINE_SLOWCASE_OP(op_instanceof)
         DEFINE_SLOWCASE_OP(op_jfalse)
         DEFINE_SLOWCASE_OP(op_jnless)
+        DEFINE_SLOWCASE_OP(op_jless)
         DEFINE_SLOWCASE_OP(op_jnlesseq)
         DEFINE_SLOWCASE_OP(op_jtrue)
         DEFINE_SLOWCASE_OP(op_loop_if_less)
         DEFINE_SLOWCASE_OP(op_loop_if_lesseq)
         DEFINE_SLOWCASE_OP(op_loop_if_true)
+        DEFINE_SLOWCASE_OP(op_loop_if_false)
         DEFINE_SLOWCASE_OP(op_lshift)
         DEFINE_SLOWCASE_OP(op_method_check)
         DEFINE_SLOWCASE_OP(op_mod)
index c59a2fa4393f847b2f4f9fbd13700cc8a390f0f4..0b902b9634999f8c12ddefb2e3e4444351946a6e 100644 (file)
@@ -736,6 +736,7 @@ namespace JSC {
         void emit_op_jneq_null(Instruction*);
         void emit_op_jneq_ptr(Instruction*);
         void emit_op_jnless(Instruction*);
+        void emit_op_jless(Instruction*);
         void emit_op_jnlesseq(Instruction*);
         void emit_op_jsr(Instruction*);
         void emit_op_jtrue(Instruction*);
@@ -744,6 +745,7 @@ namespace JSC {
         void emit_op_loop_if_less(Instruction*);
         void emit_op_loop_if_lesseq(Instruction*);
         void emit_op_loop_if_true(Instruction*);
+        void emit_op_loop_if_false(Instruction*);
         void emit_op_lshift(Instruction*);
         void emit_op_method_check(Instruction*);
         void emit_op_mod(Instruction*);
@@ -818,11 +820,13 @@ namespace JSC {
         void emitSlow_op_instanceof(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_jfalse(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_jnless(Instruction*, Vector<SlowCaseEntry>::iterator&);
+        void emitSlow_op_jless(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_jnlesseq(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_jtrue(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_loop_if_less(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_loop_if_lesseq(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_loop_if_true(Instruction*, Vector<SlowCaseEntry>::iterator&);
+        void emitSlow_op_loop_if_false(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_lshift(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_method_check(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_mod(Instruction*, Vector<SlowCaseEntry>::iterator&);
index 821ec0f0455fbdee48133552f08ffe8974ab19d2..cef518820b6ee63df763d4b9706e26d3b46080a4 100644 (file)
@@ -148,6 +148,69 @@ void JIT::emitSlow_op_jnless(Instruction* currentInstruction, Vector<SlowCaseEnt
     emitJumpSlowToHot(branchTest32(Zero, regT0), target);
 }
 
+void JIT::emit_op_jless(Instruction* currentInstruction)
+{
+    unsigned op1 = currentInstruction[1].u.operand;
+    unsigned op2 = currentInstruction[2].u.operand;
+    unsigned target = currentInstruction[3].u.operand;
+
+    JumpList notInt32Op1;
+    JumpList notInt32Op2;
+
+    // Int32 less.
+    if (isOperandConstantImmediateInt(op1)) {
+        emitLoad(op2, regT3, regT2);
+        notInt32Op2.append(branch32(NotEqual, regT3, Imm32(JSValue::Int32Tag)));
+        addJump(branch32(GreaterThan, regT2, Imm32(getConstantOperand(op1).asInt32())), target);
+    } else if (isOperandConstantImmediateInt(op2)) {
+        emitLoad(op1, regT1, regT0);
+        notInt32Op1.append(branch32(NotEqual, regT1, Imm32(JSValue::Int32Tag)));
+        addJump(branch32(LessThan, regT0, Imm32(getConstantOperand(op2).asInt32())), target);
+    } else {
+        emitLoad2(op1, regT1, regT0, op2, regT3, regT2);
+        notInt32Op1.append(branch32(NotEqual, regT1, Imm32(JSValue::Int32Tag)));
+        notInt32Op2.append(branch32(NotEqual, regT3, Imm32(JSValue::Int32Tag)));
+        addJump(branch32(LessThan, regT0, regT2), target);
+    }
+
+    if (!supportsFloatingPoint()) {
+        addSlowCase(notInt32Op1);
+        addSlowCase(notInt32Op2);
+        return;
+    }
+    Jump end = jump();
+
+    // Double less.
+    emitBinaryDoubleOp(op_jless, target, op1, op2, OperandTypes(), notInt32Op1, notInt32Op2, !isOperandConstantImmediateInt(op1), isOperandConstantImmediateInt(op1) || !isOperandConstantImmediateInt(op2));
+    end.link(this);
+}
+
+void JIT::emitSlow_op_jless(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+    unsigned op1 = currentInstruction[1].u.operand;
+    unsigned op2 = currentInstruction[2].u.operand;
+    unsigned target = currentInstruction[3].u.operand;
+
+    if (!supportsFloatingPoint()) {
+        if (!isOperandConstantImmediateInt(op1) && !isOperandConstantImmediateInt(op2))
+            linkSlowCase(iter); // int32 check
+        linkSlowCase(iter); // int32 check
+    } else {
+        if (!isOperandConstantImmediateInt(op1)) {
+            linkSlowCase(iter); // double check
+            linkSlowCase(iter); // int32 check
+        }
+        if (isOperandConstantImmediateInt(op1) || !isOperandConstantImmediateInt(op2))
+            linkSlowCase(iter); // double check
+    }
+
+    JITStubCall stubCall(this, cti_op_jless);
+    stubCall.addArgument(op1);
+    stubCall.addArgument(op2);
+    stubCall.call();
+    emitJumpSlowToHot(branchTest32(NonZero, regT0), target);
+}
+
 void JIT::emit_op_jnlesseq(Instruction* currentInstruction)
 {
     unsigned op1 = currentInstruction[1].u.operand;
@@ -831,6 +894,10 @@ void JIT::emitBinaryDoubleOp(OpcodeID opcodeID, unsigned dst, unsigned op1, unsi
                 emitLoadDouble(op1, fpRegT2);
                 addJump(branchDouble(DoubleLessThanOrEqualOrUnordered, fpRegT0, fpRegT2), dst);
                 break;
+            case op_jless:
+                emitLoadDouble(op1, fpRegT2);
+                addJump(branchDouble(DoubleLessThan, fpRegT2, fpRegT0), dst);
+                break;
             case op_jnlesseq:
                 emitLoadDouble(op1, fpRegT2);
                 addJump(branchDouble(DoubleLessThanOrUnordered, fpRegT0, fpRegT2), dst);
@@ -884,6 +951,10 @@ void JIT::emitBinaryDoubleOp(OpcodeID opcodeID, unsigned dst, unsigned op1, unsi
                 emitLoadDouble(op2, fpRegT1);
                 addJump(branchDouble(DoubleLessThanOrEqualOrUnordered, fpRegT1, fpRegT0), dst);
                 break;
+            case op_jless:
+                emitLoadDouble(op2, fpRegT1);
+                addJump(branchDouble(DoubleLessThan, fpRegT0, fpRegT1), dst);
+                break;
             case op_jnlesseq:
                 emitLoadDouble(op2, fpRegT1);
                 addJump(branchDouble(DoubleLessThanOrUnordered, fpRegT1, fpRegT0), dst);
@@ -1455,6 +1526,191 @@ void JIT::emitSlow_op_jnless(Instruction* currentInstruction, Vector<SlowCaseEnt
     }
 }
 
+void JIT::emit_op_jless(Instruction* currentInstruction)
+{
+    unsigned op1 = currentInstruction[1].u.operand;
+    unsigned op2 = currentInstruction[2].u.operand;
+    unsigned target = currentInstruction[3].u.operand;
+
+    // We generate inline code for the following cases in the fast path:
+    // - int immediate to constant int immediate
+    // - constant int immediate to int immediate
+    // - int immediate to int immediate
+
+    if (isOperandConstantImmediateInt(op2)) {
+        emitGetVirtualRegister(op1, regT0);
+        emitJumpSlowCaseIfNotImmediateInteger(regT0);
+#if USE(JSVALUE64)
+        int32_t op2imm = getConstantOperandImmediateInt(op2);
+#else
+        int32_t op2imm = static_cast<int32_t>(JSImmediate::rawValue(getConstantOperand(op2)));
+#endif
+        addJump(branch32(LessThan, regT0, Imm32(op2imm)), target);
+    } else if (isOperandConstantImmediateInt(op1)) {
+        emitGetVirtualRegister(op2, regT1);
+        emitJumpSlowCaseIfNotImmediateInteger(regT1);
+#if USE(JSVALUE64)
+        int32_t op1imm = getConstantOperandImmediateInt(op1);
+#else
+        int32_t op1imm = static_cast<int32_t>(JSImmediate::rawValue(getConstantOperand(op1)));
+#endif
+        addJump(branch32(GreaterThan, regT1, Imm32(op1imm)), target);
+    } else {
+        emitGetVirtualRegisters(op1, regT0, op2, regT1);
+        emitJumpSlowCaseIfNotImmediateInteger(regT0);
+        emitJumpSlowCaseIfNotImmediateInteger(regT1);
+
+        addJump(branch32(LessThan, regT0, regT1), target);
+    }
+}
+
+void JIT::emitSlow_op_jless(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+    unsigned op1 = currentInstruction[1].u.operand;
+    unsigned op2 = currentInstruction[2].u.operand;
+    unsigned target = currentInstruction[3].u.operand;
+
+    // We generate inline code for the following cases in the slow path:
+    // - floating-point number to constant int immediate
+    // - constant int immediate to floating-point number
+    // - floating-point number to floating-point number.
+
+    if (isOperandConstantImmediateInt(op2)) {
+        linkSlowCase(iter);
+
+        if (supportsFloatingPoint()) {
+#if USE(JSVALUE64)
+            Jump fail1 = emitJumpIfNotImmediateNumber(regT0);
+            addPtr(tagTypeNumberRegister, regT0);
+            movePtrToDouble(regT0, fpRegT0);
+#else
+            Jump fail1;
+            if (!m_codeBlock->isKnownNotImmediate(op1))
+                fail1 = emitJumpIfNotJSCell(regT0);
+
+            Jump fail2 = checkStructure(regT0, m_globalData->numberStructure.get());
+            loadDouble(Address(regT0, OBJECT_OFFSETOF(JSNumberCell, m_value)), fpRegT0);
+#endif
+            
+            int32_t op2imm = getConstantOperand(op2).asInt32();
+                    
+            move(Imm32(op2imm), regT1);
+            convertInt32ToDouble(regT1, fpRegT1);
+
+            emitJumpSlowToHot(branchDouble(DoubleLessThan, fpRegT0, fpRegT1), target);
+
+            emitJumpSlowToHot(jump(), OPCODE_LENGTH(op_jnless));
+
+#if USE(JSVALUE64)
+            fail1.link(this);
+#else
+            if (!m_codeBlock->isKnownNotImmediate(op1))
+                fail1.link(this);
+            fail2.link(this);
+#endif
+        }
+
+        JITStubCall stubCall(this, cti_op_jless);
+        stubCall.addArgument(regT0);
+        stubCall.addArgument(op2, regT2);
+        stubCall.call();
+        emitJumpSlowToHot(branchTest32(Zero, regT0), target);
+
+    } else if (isOperandConstantImmediateInt(op1)) {
+        linkSlowCase(iter);
+
+        if (supportsFloatingPoint()) {
+#if USE(JSVALUE64)
+            Jump fail1 = emitJumpIfNotImmediateNumber(regT1);
+            addPtr(tagTypeNumberRegister, regT1);
+            movePtrToDouble(regT1, fpRegT1);
+#else
+            Jump fail1;
+            if (!m_codeBlock->isKnownNotImmediate(op2))
+                fail1 = emitJumpIfNotJSCell(regT1);
+            
+            Jump fail2 = checkStructure(regT1, m_globalData->numberStructure.get());
+            loadDouble(Address(regT1, OBJECT_OFFSETOF(JSNumberCell, m_value)), fpRegT1);
+#endif
+            
+            int32_t op1imm = getConstantOperand(op1).asInt32();
+                    
+            move(Imm32(op1imm), regT0);
+            convertInt32ToDouble(regT0, fpRegT0);
+
+            emitJumpSlowToHot(branchDouble(DoubleLessThan, fpRegT0, fpRegT1), target);
+
+            emitJumpSlowToHot(jump(), OPCODE_LENGTH(op_jnless));
+
+#if USE(JSVALUE64)
+            fail1.link(this);
+#else
+            if (!m_codeBlock->isKnownNotImmediate(op2))
+                fail1.link(this);
+            fail2.link(this);
+#endif
+        }
+
+        JITStubCall stubCall(this, cti_op_jless);
+        stubCall.addArgument(op1, regT2);
+        stubCall.addArgument(regT1);
+        stubCall.call();
+        emitJumpSlowToHot(branchTest32(Zero, regT0), target);
+
+    } else {
+        linkSlowCase(iter);
+
+        if (supportsFloatingPoint()) {
+#if USE(JSVALUE64)
+            Jump fail1 = emitJumpIfNotImmediateNumber(regT0);
+            Jump fail2 = emitJumpIfNotImmediateNumber(regT1);
+            Jump fail3 = emitJumpIfImmediateInteger(regT1);
+            addPtr(tagTypeNumberRegister, regT0);
+            addPtr(tagTypeNumberRegister, regT1);
+            movePtrToDouble(regT0, fpRegT0);
+            movePtrToDouble(regT1, fpRegT1);
+#else
+            Jump fail1;
+            if (!m_codeBlock->isKnownNotImmediate(op1))
+                fail1 = emitJumpIfNotJSCell(regT0);
+
+            Jump fail2;
+            if (!m_codeBlock->isKnownNotImmediate(op2))
+                fail2 = emitJumpIfNotJSCell(regT1);
+
+            Jump fail3 = checkStructure(regT0, m_globalData->numberStructure.get());
+            Jump fail4 = checkStructure(regT1, m_globalData->numberStructure.get());
+            loadDouble(Address(regT0, OBJECT_OFFSETOF(JSNumberCell, m_value)), fpRegT0);
+            loadDouble(Address(regT1, OBJECT_OFFSETOF(JSNumberCell, m_value)), fpRegT1);
+#endif
+
+            emitJumpSlowToHot(branchDouble(DoubleLessThan, fpRegT0, fpRegT1), target);
+
+            emitJumpSlowToHot(jump(), OPCODE_LENGTH(op_jnless));
+
+#if USE(JSVALUE64)
+            fail1.link(this);
+            fail2.link(this);
+            fail3.link(this);
+#else
+            if (!m_codeBlock->isKnownNotImmediate(op1))
+                fail1.link(this);
+            if (!m_codeBlock->isKnownNotImmediate(op2))
+                fail2.link(this);
+            fail3.link(this);
+            fail4.link(this);
+#endif
+        }
+
+        linkSlowCase(iter);
+        JITStubCall stubCall(this, cti_op_jless);
+        stubCall.addArgument(regT0);
+        stubCall.addArgument(regT1);
+        stubCall.call();
+        emitJumpSlowToHot(branchTest32(NotZero, regT0), target);
+    }
+}
+
 void JIT::emit_op_jnlesseq(Instruction* currentInstruction)
 {
     unsigned op1 = currentInstruction[1].u.operand;
index 991d4ca75cd2b190d136dafa17b0241f6a292fa2..aea552d218ee1cdc90675f856499d26b56bc772e 100644 (file)
@@ -454,7 +454,7 @@ void JIT::emitSlow_op_loop_if_less(Instruction* currentInstruction, Vector<SlowC
         linkSlowCase(iter); // int32 check
     linkSlowCase(iter); // int32 check
 
-    JITStubCall stubCall(this, cti_op_loop_if_less);
+    JITStubCall stubCall(this, cti_op_jless);
     stubCall.addArgument(op1);
     stubCall.addArgument(op2);
     stubCall.call();
@@ -743,6 +743,50 @@ void JIT::emitSlow_op_loop_if_true(Instruction* currentInstruction, Vector<SlowC
     emitJumpSlowToHot(branchTest32(NonZero, regT0), target);
 }
 
+void JIT::emit_op_loop_if_false(Instruction* currentInstruction)
+{
+    unsigned cond = currentInstruction[1].u.operand;
+    unsigned target = currentInstruction[2].u.operand;
+
+    emitTimeoutCheck();
+
+    emitLoad(cond, regT1, regT0);
+
+    Jump isTrue = branch32(Equal, regT1, Imm32(JSValue::TrueTag));
+    addJump(branch32(Equal, regT1, Imm32(JSValue::FalseTag)), target);
+
+    Jump isNotInteger = branch32(NotEqual, regT1, Imm32(JSValue::Int32Tag));
+    Jump isTrue2 = branch32(NotEqual, regT0, Imm32(0));
+    addJump(jump(), target);
+
+    if (supportsFloatingPoint()) {
+        isNotInteger.link(this);
+
+        addSlowCase(branch32(Above, regT1, Imm32(JSValue::LowestTag)));
+
+        zeroDouble(fpRegT0);
+        emitLoadDouble(cond, fpRegT1);
+        addJump(branchDouble(DoubleEqualOrUnordered, fpRegT0, fpRegT1), target);
+    } else
+        addSlowCase(isNotInteger);
+
+    isTrue.link(this);
+    isTrue2.link(this);
+}
+
+void JIT::emitSlow_op_loop_if_false(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+    unsigned cond = currentInstruction[1].u.operand;
+    unsigned target = currentInstruction[2].u.operand;
+
+    linkSlowCase(iter);
+
+    JITStubCall stubCall(this, cti_op_jtrue);
+    stubCall.addArgument(cond);
+    stubCall.call();
+    emitJumpSlowToHot(branchTest32(Zero, regT0), target);
+}
+
 void JIT::emit_op_resolve_base(Instruction* currentInstruction)
 {
     JITStubCall stubCall(this, cti_op_resolve_base);
index 0316a86c5d789a608cdc2d7fe929a3f98e421a52..adedf5b1b1659d10c5b041707fd7528600036092 100644 (file)
@@ -1119,19 +1119,6 @@ DEFINE_STUB_FUNCTION(void, register_file_check)
     throwStackOverflowError(oldCallFrame, stackFrame.globalData, ReturnAddressPtr(oldCallFrame->returnPC()), STUB_RETURN_ADDRESS);
 }
 
-DEFINE_STUB_FUNCTION(int, op_loop_if_less)
-{
-    STUB_INIT_STACK_FRAME(stackFrame);
-
-    JSValue src1 = stackFrame.args[0].jsValue();
-    JSValue src2 = stackFrame.args[1].jsValue();
-    CallFrame* callFrame = stackFrame.callFrame;
-
-    bool result = jsLess(callFrame, src1, src2);
-    CHECK_FOR_EXCEPTION_AT_END();
-    return result;
-}
-
 DEFINE_STUB_FUNCTION(int, op_loop_if_lesseq)
 {
     STUB_INIT_STACK_FRAME(stackFrame);
@@ -2097,19 +2084,6 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_lesseq)
     return JSValue::encode(result);
 }
 
-DEFINE_STUB_FUNCTION(int, op_loop_if_true)
-{
-    STUB_INIT_STACK_FRAME(stackFrame);
-
-    JSValue src1 = stackFrame.args[0].jsValue();
-
-    CallFrame* callFrame = stackFrame.callFrame;
-
-    bool result = src1.toBoolean(callFrame);
-    CHECK_FOR_EXCEPTION_AT_END();
-    return result;
-}
-    
 DEFINE_STUB_FUNCTION(int, op_load_varargs)
 {
     STUB_INIT_STACK_FRAME(stackFrame);
index a8ea127919cd8dd221232a757c1ac5c5dd6f7e86..f71dc9a67704cce01e7e25102d1e34ecdb249e14 100644 (file)
@@ -335,9 +335,7 @@ extern "C" {
     int JIT_STUB cti_op_jlesseq(STUB_ARGS_DECLARATION);
     int JIT_STUB cti_op_jtrue(STUB_ARGS_DECLARATION);
     int JIT_STUB cti_op_load_varargs(STUB_ARGS_DECLARATION);
-    int JIT_STUB cti_op_loop_if_less(STUB_ARGS_DECLARATION);
     int JIT_STUB cti_op_loop_if_lesseq(STUB_ARGS_DECLARATION);
-    int JIT_STUB cti_op_loop_if_true(STUB_ARGS_DECLARATION);
     int JIT_STUB cti_timeout_check(STUB_ARGS_DECLARATION);
     int JIT_STUB cti_has_property(STUB_ARGS_DECLARATION);
     void JIT_STUB cti_op_create_arguments(STUB_ARGS_DECLARATION);
index 45009dce44d76a336e8432255db82b6c7a94adf8..bad6b0da3938be28da1a21f9e2525e4df9ee22a5 100644 (file)
@@ -811,6 +811,18 @@ RegisterID* UnaryOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID*
     return generator.emitUnaryOp(opcodeID(), generator.finalDestination(dst), src);
 }
 
+
+// ------------------------------ LogicalNotNode -----------------------------------
+
+void LogicalNotNode::emitBytecodeInConditionContext(BytecodeGenerator& generator, Label* trueTarget, Label* falseTarget, bool fallThroughMeansTrue)
+{
+    ASSERT(expr()->hasConditionContextCodegen());
+
+    // reverse the true and false targets
+    generator.emitNodeInConditionContext(expr(), falseTarget, trueTarget, !fallThroughMeansTrue);
+}
+
+
 // ------------------------------ Binary Operation Nodes -----------------------------------
 
 // BinaryOpNode::emitStrcat:
@@ -1017,6 +1029,34 @@ RegisterID* LogicalOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID
     return generator.moveToDestinationIfNeeded(dst, temp.get());
 }
 
+void LogicalOpNode::emitBytecodeInConditionContext(BytecodeGenerator& generator, Label* trueTarget, Label* falseTarget, bool fallThroughMeansTrue)
+{
+    if (m_expr1->hasConditionContextCodegen()) {
+        RefPtr<Label> afterExpr1 = generator.newLabel();
+        if (m_operator == OpLogicalAnd)
+            generator.emitNodeInConditionContext(m_expr1, afterExpr1.get(), falseTarget, true);
+        else 
+            generator.emitNodeInConditionContext(m_expr1, trueTarget, afterExpr1.get(), false);
+        generator.emitLabel(afterExpr1.get());
+    } else {
+        RegisterID* temp = generator.emitNode(m_expr1);
+        if (m_operator == OpLogicalAnd)
+            generator.emitJumpIfFalse(temp, falseTarget);
+        else
+            generator.emitJumpIfTrue(temp, trueTarget);
+    }
+
+    if (m_expr2->hasConditionContextCodegen())
+        generator.emitNodeInConditionContext(m_expr2, trueTarget, falseTarget, fallThroughMeansTrue);
+    else {
+        RegisterID* temp = generator.emitNode(m_expr2);
+        if (fallThroughMeansTrue)
+            generator.emitJumpIfFalse(temp, falseTarget);
+        else
+            generator.emitJumpIfTrue(temp, trueTarget);
+    }
+}
+
 // ------------------------------ ConditionalNode ------------------------------
 
 RegisterID* ConditionalNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
@@ -1025,8 +1065,14 @@ RegisterID* ConditionalNode::emitBytecode(BytecodeGenerator& generator, Register
     RefPtr<Label> beforeElse = generator.newLabel();
     RefPtr<Label> afterElse = generator.newLabel();
 
-    RegisterID* cond = generator.emitNode(m_logical);
-    generator.emitJumpIfFalse(cond, beforeElse.get());
+    if (m_logical->hasConditionContextCodegen()) {
+        RefPtr<Label> beforeThen = generator.newLabel();
+        generator.emitNodeInConditionContext(m_logical, beforeThen.get(), beforeElse.get(), true);
+        generator.emitLabel(beforeThen.get());
+    } else {
+        RegisterID* cond = generator.emitNode(m_logical);
+        generator.emitJumpIfFalse(cond, beforeElse.get());
+    }
 
     generator.emitNode(newDst.get(), m_expr1);
     generator.emitJump(afterElse.get());
@@ -1343,8 +1389,14 @@ RegisterID* IfNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
     
     RefPtr<Label> afterThen = generator.newLabel();
 
-    RegisterID* cond = generator.emitNode(m_condition);
-    generator.emitJumpIfFalse(cond, afterThen.get());
+    if (m_condition->hasConditionContextCodegen()) {
+        RefPtr<Label> beforeThen = generator.newLabel();
+        generator.emitNodeInConditionContext(m_condition, beforeThen.get(), afterThen.get(), true);
+        generator.emitLabel(beforeThen.get());
+    } else {
+        RegisterID* cond = generator.emitNode(m_condition);
+        generator.emitJumpIfFalse(cond, afterThen.get());
+    }
 
     generator.emitNode(dst, m_ifBlock);
     generator.emitLabel(afterThen.get());
@@ -1362,8 +1414,14 @@ RegisterID* IfElseNode::emitBytecode(BytecodeGenerator& generator, RegisterID* d
     RefPtr<Label> beforeElse = generator.newLabel();
     RefPtr<Label> afterElse = generator.newLabel();
 
-    RegisterID* cond = generator.emitNode(m_condition);
-    generator.emitJumpIfFalse(cond, beforeElse.get());
+    if (m_condition->hasConditionContextCodegen()) {
+        RefPtr<Label> beforeThen = generator.newLabel();
+        generator.emitNodeInConditionContext(m_condition, beforeThen.get(), beforeElse.get(), true);
+        generator.emitLabel(beforeThen.get());
+    } else {
+        RegisterID* cond = generator.emitNode(m_condition);
+        generator.emitJumpIfFalse(cond, beforeElse.get());
+    }
 
     generator.emitNode(dst, m_ifBlock);
     generator.emitJump(afterElse.get());
@@ -1393,8 +1451,12 @@ RegisterID* DoWhileNode::emitBytecode(BytecodeGenerator& generator, RegisterID*
 
     generator.emitLabel(scope->continueTarget());
     generator.emitDebugHook(WillExecuteStatement, m_expr->lineNo(), m_expr->lineNo());
-    RegisterID* cond = generator.emitNode(m_expr);
-    generator.emitJumpIfTrue(cond, topOfLoop.get());
+    if (m_expr->hasConditionContextCodegen())
+        generator.emitNodeInConditionContext(m_expr, topOfLoop.get(), scope->breakTarget(), false);
+    else {
+        RegisterID* cond = generator.emitNode(m_expr);
+        generator.emitJumpIfTrue(cond, topOfLoop.get());
+    }
 
     generator.emitLabel(scope->breakTarget());
     return result.get();
@@ -1415,8 +1477,13 @@ RegisterID* WhileNode::emitBytecode(BytecodeGenerator& generator, RegisterID* ds
 
     generator.emitLabel(scope->continueTarget());
     generator.emitDebugHook(WillExecuteStatement, m_expr->lineNo(), m_expr->lineNo());
-    RegisterID* cond = generator.emitNode(m_expr);
-    generator.emitJumpIfTrue(cond, topOfLoop.get());
+
+    if (m_expr->hasConditionContextCodegen())
+        generator.emitNodeInConditionContext(m_expr, topOfLoop.get(), scope->breakTarget(), false);
+    else {
+        RegisterID* cond = generator.emitNode(m_expr);
+        generator.emitJumpIfTrue(cond, topOfLoop.get());
+    }
 
     generator.emitLabel(scope->breakTarget());
     
@@ -1450,8 +1517,12 @@ RegisterID* ForNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
 
     generator.emitLabel(condition.get());
     if (m_expr2) {
-        RegisterID* cond = generator.emitNode(m_expr2);
-        generator.emitJumpIfTrue(cond, topOfLoop.get());
+        if (m_expr2->hasConditionContextCodegen())
+            generator.emitNodeInConditionContext(m_expr2, topOfLoop.get(), scope->breakTarget(), false);
+        else {
+            RegisterID* cond = generator.emitNode(m_expr2);
+            generator.emitJumpIfTrue(cond, topOfLoop.get());
+        }
     } else
         generator.emitJump(topOfLoop.get());
 
index be84c4a1b146ae869fef84038684389542dc7212..c216ea86327a1f098e66e0746136e859f7c4c4a3 100644 (file)
@@ -40,6 +40,7 @@ namespace JSC {
     class ArgumentListNode;
     class BytecodeGenerator;
     class FunctionBodyNode;
+    class Label;
     class PropertyListNode;
     class ReadModifyResolveNode;
     class RegisterID;
@@ -151,6 +152,9 @@ namespace JSC {
         virtual bool isCommaNode() const { return false; }
         virtual bool isSimpleArray() const { return false; }
         virtual bool isAdd() const { return false; }
+        virtual bool hasConditionContextCodegen() const { return false; }
+
+        virtual void emitBytecodeInConditionContext(BytecodeGenerator&, Label*, Label*, bool) { ASSERT_NOT_REACHED(); }
 
         virtual ExpressionNode* stripUnaryPlus() { return this; }
 
@@ -757,6 +761,7 @@ namespace JSC {
 
     protected:
         ExpressionNode* expr() { return m_expr; }
+        const ExpressionNode* expr() const { return m_expr; }
 
     private:
         virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0);
@@ -788,6 +793,9 @@ namespace JSC {
     class LogicalNotNode : public UnaryOpNode {
     public:
         LogicalNotNode(JSGlobalData*, ExpressionNode*);
+    private:
+        void emitBytecodeInConditionContext(BytecodeGenerator&, Label* trueTarget, Label* falseTarget, bool fallThroughMeansTrue);
+        virtual bool hasConditionContextCodegen() const { return expr()->hasConditionContextCodegen(); }
     };
 
     class BinaryOpNode : public ExpressionNode {
@@ -952,6 +960,8 @@ namespace JSC {
 
     private:
         virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0);
+        void emitBytecodeInConditionContext(BytecodeGenerator&, Label* trueTarget, Label* falseTarget, bool fallThroughMeansTrue);
+        virtual bool hasConditionContextCodegen() const { return true; }
 
         ExpressionNode* m_expr1;
         ExpressionNode* m_expr2;
index 8fb56dfb9264aa53b5f162a8b8a20b2aa8532200..ebb2ec0e43e617cf5d49de6af86e0bace0092ca8 100644 (file)
@@ -1,3 +1,14 @@
+2009-12-05  Maciej Stachowiak  <mjs@apple.com>
+
+        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.
+
 2009-12-05  Philippe Normand  <pnormand@igalia.com>
 
         Reviewed by Gustavo Noronha.
index f38497a4e985c2c26725036fba970b7a875f7fa9..641b77a0ce0143be404a5acc59ea02f690374d91 100644 (file)
@@ -27,6 +27,77 @@ PASS dowhile_and_eq() is true
 PASS dowhile_and_neq() is true
 PASS dowhile_and_less() is true
 PASS dowhile_and_lesseq() is true
+PASS while_not_or_eq() is false
+PASS while_not_or_neq() is false
+PASS while_not_or_less() is false
+PASS while_not_or_lesseq() is false
+PASS while_not_and_eq() is false
+PASS while_not_and_neq() is false
+PASS while_not_and_less() is false
+PASS while_not_and_lesseq() is false
+PASS for_not_or_eq() is false
+PASS for_not_or_neq() is false
+PASS for_not_or_less() is false
+PASS for_not_or_lesseq() is false
+PASS for_not_and_eq() is false
+PASS for_not_and_neq() is false
+PASS for_not_and_less() is false
+PASS for_not_and_lesseq() is false
+PASS dowhile_not_or_eq() is false
+PASS dowhile_not_or_neq() is false
+PASS dowhile_not_or_less() is false
+PASS dowhile_not_or_lesseq() is false
+PASS dowhile_not_and_eq() is false
+PASS dowhile_not_and_neq() is false
+PASS dowhile_not_and_less() is false
+PASS dowhile_not_and_lesseq() is false
+PASS float_while_or_eq() is true
+PASS float_while_or_neq() is true
+PASS float_while_or_less() is true
+PASS float_while_or_lesseq() is true
+PASS float_while_and_eq() is true
+PASS float_while_and_neq() is true
+PASS float_while_and_less() is true
+PASS float_while_and_lesseq() is true
+PASS float_for_or_eq() is true
+PASS float_for_or_neq() is true
+PASS float_for_or_less() is true
+PASS float_for_or_lesseq() is true
+PASS float_for_and_eq() is true
+PASS float_for_and_neq() is true
+PASS float_for_and_less() is true
+PASS float_for_and_lesseq() is true
+PASS float_dowhile_or_eq() is true
+PASS float_dowhile_or_neq() is true
+PASS float_dowhile_or_less() is true
+PASS float_dowhile_or_lesseq() is true
+PASS float_dowhile_and_eq() is true
+PASS float_dowhile_and_neq() is true
+PASS float_dowhile_and_less() is true
+PASS float_dowhile_and_lesseq() is true
+PASS float_while_not_or_eq() is false
+PASS float_while_not_or_neq() is false
+PASS float_while_not_or_less() is false
+PASS float_while_not_or_lesseq() is false
+PASS float_while_not_and_eq() is false
+PASS float_while_not_and_neq() is false
+PASS float_while_not_and_less() is false
+PASS float_while_not_and_lesseq() is false
+PASS float_for_not_or_eq() is false
+PASS float_for_not_or_neq() is false
+PASS float_for_not_or_less() is false
+PASS float_for_not_or_lesseq() is false
+PASS float_for_not_and_eq() is false
+PASS float_for_not_and_neq() is false
+PASS float_for_not_and_less() is false
+PASS float_for_not_and_lesseq() is false
+PASS float_dowhile_not_or_eq() is false
+PASS float_dowhile_not_or_neq() is false
+PASS float_dowhile_not_or_less() is false
+PASS float_dowhile_not_or_lesseq() is false
+PASS float_dowhile_not_and_eq() is false
+PASS float_dowhile_not_and_neq() is false
+PASS float_dowhile_not_and_less() is false
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 4d255e20b2cbd2afe121fec8045196b103464d7b..2f8a3e0da9e4f74d7c019d949551d57c054d8138 100644 (file)
@@ -266,4 +266,794 @@ function dowhile_and_lesseq()
 
 shouldBeTrue("dowhile_and_lesseq()");
 
+function while_not_or_eq()
+{
+    var a = 0;
+    while (!(a == 0 || a == 0))
+        return true;
+    return false;
+}
+
+shouldBeFalse("while_not_or_eq()");
+
+function while_not_or_neq()
+{
+    var a = 0;
+    while (!(a != 1 || a != 1))
+        return true;
+    return false;
+}
+
+shouldBeFalse("while_not_or_neq()");
+
+function while_not_or_less()
+{
+    var a = 0;
+    while (!(a < 1 || a < 1))
+        return true;
+    return false;
+}
+
+shouldBeFalse("while_not_or_less()");
+
+function while_not_or_lesseq()
+{
+    var a = 0;
+    while (!(a <= 1 || a <= 1))
+        return true;
+    return false;
+}
+
+shouldBeFalse("while_not_or_lesseq()");
+
+function while_not_and_eq()
+{
+    var a = 0;
+    while (!(a == 0 && a == 0))
+        return true;
+    return false;
+}
+
+shouldBeFalse("while_not_and_eq()");
+
+function while_not_and_neq()
+{
+    var a = 0;
+    while (!(a != 1 && a != 1))
+        return true;
+    return false;
+}
+
+shouldBeFalse("while_not_and_neq()");
+
+function while_not_and_less()
+{
+    var a = 0;
+    while (!(a < 1 && a < 1))
+        return true;
+    return false;
+}
+
+shouldBeFalse("while_not_and_less()");
+
+function while_not_and_lesseq()
+{
+    var a = 0;
+    while (!(a <= 1 && a <= 1))
+        return true;
+    return false;
+}
+
+shouldBeFalse("while_not_and_lesseq()");
+
+function for_not_or_eq()
+{
+    for (var a = 0; !(a == 0 || a == 0); )
+        return true;
+    return false;
+}
+
+shouldBeFalse("for_not_or_eq()");
+
+function for_not_or_neq()
+{
+    for (var a = 0; !(a != 1 || a != 1); )
+        return true;
+    return false;
+}
+
+shouldBeFalse("for_not_or_neq()");
+
+function for_not_or_less()
+{
+    for (var a = 0; !(a < 1 || a < 1); )
+        return true;
+    return false;
+}
+
+shouldBeFalse("for_not_or_less()");
+
+function for_not_or_lesseq()
+{
+    for (var a = 0; !(a <= 1 || a <= 1); )
+        return true;
+    return false;
+}
+
+shouldBeFalse("for_not_or_lesseq()");
+
+function for_not_and_eq()
+{
+    for (var a = 0; !(a == 0 && a == 0); )
+        return true;
+    return false;
+}
+
+shouldBeFalse("for_not_and_eq()");
+
+function for_not_and_neq()
+{
+    for (var a = 0; !(a != 1 && a != 1); )
+        return true;
+    return false;
+}
+
+shouldBeFalse("for_not_and_neq()");
+
+function for_not_and_less()
+{
+    for (var a = 0; !(a < 1 && a < 1); )
+        return true;
+    return false;
+}
+
+shouldBeFalse("for_not_and_less()");
+
+function for_not_and_lesseq()
+{
+    for (var a = 0; !(a <= 1 && a <= 1); )
+        return true;
+    return false;
+}
+
+shouldBeFalse("for_not_and_lesseq()");
+
+function dowhile_not_or_eq()
+{
+    var a = 0;
+    var i = 0;
+    do {
+        if (i > 0)
+            return true;
+        i++;
+    } while (!(a == 0 || a == 0))
+    return false;
+}
+
+shouldBeFalse("dowhile_not_or_eq()");
+
+function dowhile_not_or_neq()
+{
+    var a = 0;
+    var i = 0;
+    do {
+        if (i > 0)
+            return true;
+        i++;
+    } while (!(a != 1 || a != 1))
+    return false;
+}
+
+shouldBeFalse("dowhile_not_or_neq()");
+
+function dowhile_not_or_less()
+{
+    var a = 0;
+    var i = 0;
+    do {
+        if (i > 0)
+            return true;
+        i++;
+    } while (!(a < 1 || a < 1))
+    return false;
+}
+
+shouldBeFalse("dowhile_not_or_less()");
+
+function dowhile_not_or_lesseq()
+{
+    var a = 0;
+    var i = 0;
+    do {
+        if (i > 0)
+            return true;
+        i++;
+    } while (!(a <= 1 || a <= 1))
+    return false;
+}
+
+shouldBeFalse("dowhile_not_or_lesseq()");
+
+function dowhile_not_and_eq()
+{
+    var a = 0;
+    var i = 0;
+    do {
+        if (i > 0)
+            return true;
+        i++;
+    } while (!(a == 0 && a == 0))
+    return false;
+}
+
+shouldBeFalse("dowhile_not_and_eq()");
+
+function dowhile_not_and_neq()
+{
+    var a = 0;
+    var i = 0;
+    do {
+        if (i > 0)
+            return true;
+        i++;
+    } while (!(a != 1 && a != 1))
+    return false;
+}
+
+shouldBeFalse("dowhile_not_and_neq()");
+
+function dowhile_not_and_less()
+{
+    var a = 0;
+    var i = 0;
+    do {
+        if (i > 0)
+            return true;
+        i++;
+    } while (!(a < 1 && a < 1))
+    return false;
+}
+
+shouldBeFalse("dowhile_not_and_less()");
+
+function dowhile_not_and_lesseq()
+{
+    var a = 0;
+    var i = 0;
+    do {
+        if (i > 0)
+            return true;
+        i++;
+    } while (!(a <= 1 && a <= 1))
+    return false;
+}
+
+shouldBeFalse("dowhile_not_and_lesseq()");
+
+function float_while_or_eq()
+{
+    var a = 0.1;
+    while (a == 0.1 || a == 0.1)
+        return true;
+    return false;
+}
+
+shouldBeTrue("float_while_or_eq()");
+
+function float_while_or_neq()
+{
+    var a = 0.1;
+    while (a != 1.1 || a != 1.1)
+        return true;
+    return false;
+}
+
+shouldBeTrue("float_while_or_neq()");
+
+function float_while_or_less()
+{
+    var a = 0.1;
+    while (a < 1.1 || a < 1.1)
+        return true;
+    return false;
+}
+
+shouldBeTrue("float_while_or_less()");
+
+function float_while_or_lesseq()
+{
+    var a = 0.1;
+    while (a <= 1.1 || a <= 1.1)
+        return true;
+    return false;
+}
+
+shouldBeTrue("float_while_or_lesseq()");
+
+function float_while_and_eq()
+{
+    var a = 0.1;
+    while (a == 0.1 && a == 0.1)
+        return true;
+    return false;
+}
+
+shouldBeTrue("float_while_and_eq()");
+
+function float_while_and_neq()
+{
+    var a = 0.1;
+    while (a != 1.1 && a != 1.1)
+        return true;
+    return false;
+}
+
+shouldBeTrue("float_while_and_neq()");
+
+function float_while_and_less()
+{
+    var a = 0.1;
+    while (a < 1.1 && a < 1.1)
+        return true;
+    return false;
+}
+
+shouldBeTrue("float_while_and_less()");
+
+function float_while_and_lesseq()
+{
+    var a = 0.1;
+    while (a <= 1.1 && a <= 1.1)
+        return true;
+    return false;
+}
+
+shouldBeTrue("float_while_and_lesseq()");
+
+function float_for_or_eq()
+{
+    for (var a = 0.1; a == 0.1 || a == 0.1; )
+        return true;
+    return false;
+}
+
+shouldBeTrue("float_for_or_eq()");
+
+function float_for_or_neq()
+{
+    for (var a = 0.1; a != 1.1 || a != 1.1; )
+        return true;
+    return false;
+}
+
+shouldBeTrue("float_for_or_neq()");
+
+function float_for_or_less()
+{
+    for (var a = 0.1; a < 1.1 || a < 1.1; )
+        return true;
+    return false;
+}
+
+shouldBeTrue("float_for_or_less()");
+
+function float_for_or_lesseq()
+{
+    for (var a = 0.1; a <= 1.1 || a <= 1.1; )
+        return true;
+    return false;
+}
+
+shouldBeTrue("float_for_or_lesseq()");
+
+function float_for_and_eq()
+{
+    for (var a = 0.1; a == 0.1 && a == 0.1; )
+        return true;
+    return false;
+}
+
+shouldBeTrue("float_for_and_eq()");
+
+function float_for_and_neq()
+{
+    for (var a = 0.1; a != 1.1 && a != 1.1; )
+        return true;
+    return false;
+}
+
+shouldBeTrue("float_for_and_neq()");
+
+function float_for_and_less()
+{
+    for (var a = 0.1; a < 1.1 && a < 1.1; )
+        return true;
+    return false;
+}
+
+shouldBeTrue("float_for_and_less()");
+
+function float_for_and_lesseq()
+{
+    for (var a = 0.1; a <= 1.1 && a <= 1.1; )
+        return true;
+    return false;
+}
+
+shouldBeTrue("float_for_and_lesseq()");
+
+function float_dowhile_or_eq()
+{
+    var a = 0.1;
+    var i = 0.1;
+    do {
+        if (i > 0.1)
+            return true;
+        i++;
+    } while (a == 0.1 || a == 0.1)
+    return false;
+}
+
+shouldBeTrue("float_dowhile_or_eq()");
+
+function float_dowhile_or_neq()
+{
+    var a = 0.1;
+    var i = 0.1;
+    do {
+        if (i > 0.1)
+            return true;
+        i++;
+    } while (a != 1.1 || a != 1.1)
+    return false;
+}
+
+shouldBeTrue("float_dowhile_or_neq()");
+
+function float_dowhile_or_less()
+{
+    var a = 0.1;
+    var i = 0.1;
+    do {
+        if (i > 0.1)
+            return true;
+        i++;
+    } while (a < 1.1 || a < 1.1)
+    return false;
+}
+
+shouldBeTrue("float_dowhile_or_less()");
+
+function float_dowhile_or_lesseq()
+{
+    var a = 0.1;
+    var i = 0.1;
+    do {
+        if (i > 0.1)
+            return true;
+        i++;
+    } while (a <= 1.1 || a <= 1.1)
+    return false;
+}
+
+shouldBeTrue("float_dowhile_or_lesseq()");
+
+function float_dowhile_and_eq()
+{
+    var a = 0.1;
+    var i = 0.1;
+    do {
+        if (i > 0.1)
+            return true;
+        i++;
+    } while (a == 0.1 && a == 0.1)
+    return false;
+}
+
+shouldBeTrue("float_dowhile_and_eq()");
+
+function float_dowhile_and_neq()
+{
+    var a = 0.1;
+    var i = 0.1;
+    do {
+        if (i > 0.1)
+            return true;
+        i++;
+    } while (a != 1.1 && a != 1.1)
+    return false;
+}
+
+shouldBeTrue("float_dowhile_and_neq()");
+
+function float_dowhile_and_less()
+{
+    var a = 0.1;
+    var i = 0.1;
+    do {
+        if (i > 0.1)
+            return true;
+        i++;
+    } while (a < 1.1 && a < 1.1)
+    return false;
+}
+
+shouldBeTrue("float_dowhile_and_less()");
+
+function float_dowhile_and_lesseq()
+{
+    var a = 0.1;
+    var i = 0.1;
+    do {
+        if (i > 0.1)
+            return true;
+        i++;
+    } while (a <= 1.1 && a <= 1.1)
+    return false;
+}
+
+shouldBeTrue("float_dowhile_and_lesseq()");
+
+function float_while_not_or_eq()
+{
+    var a = 0.1;
+    while (!(a == 0.1 || a == 0.1))
+        return true;
+    return false;
+}
+
+shouldBeFalse("float_while_not_or_eq()");
+
+function float_while_not_or_neq()
+{
+    var a = 0.1;
+    while (!(a != 1.1 || a != 1.1))
+        return true;
+    return false;
+}
+
+shouldBeFalse("float_while_not_or_neq()");
+
+function float_while_not_or_less()
+{
+    var a = 0.1;
+    while (!(a < 1.1 || a < 1.1))
+        return true;
+    return false;
+}
+
+shouldBeFalse("float_while_not_or_less()");
+
+function float_while_not_or_lesseq()
+{
+    var a = 0.1;
+    while (!(a <= 1.1 || a <= 1.1))
+        return true;
+    return false;
+}
+
+shouldBeFalse("float_while_not_or_lesseq()");
+
+function float_while_not_and_eq()
+{
+    var a = 0.1;
+    while (!(a == 0.1 && a == 0.1))
+        return true;
+    return false;
+}
+
+shouldBeFalse("float_while_not_and_eq()");
+
+function float_while_not_and_neq()
+{
+    var a = 0.1;
+    while (!(a != 1.1 && a != 1.1))
+        return true;
+    return false;
+}
+
+shouldBeFalse("float_while_not_and_neq()");
+
+function float_while_not_and_less()
+{
+    var a = 0.1;
+    while (!(a < 1.1 && a < 1.1))
+        return true;
+    return false;
+}
+
+shouldBeFalse("float_while_not_and_less()");
+
+function float_while_not_and_lesseq()
+{
+    var a = 0.1;
+    while (!(a <= 1.1 && a <= 1.1))
+        return true;
+    return false;
+}
+
+shouldBeFalse("float_while_not_and_lesseq()");
+
+function float_for_not_or_eq()
+{
+    for (var a = 0.1; !(a == 0.1 || a == 0.1); )
+        return true;
+    return false;
+}
+
+shouldBeFalse("float_for_not_or_eq()");
+
+function float_for_not_or_neq()
+{
+    for (var a = 0.1; !(a != 1.1 || a != 1.1); )
+        return true;
+    return false;
+}
+
+shouldBeFalse("float_for_not_or_neq()");
+
+function float_for_not_or_less()
+{
+    for (var a = 0.1; !(a < 1.1 || a < 1.1); )
+        return true;
+    return false;
+}
+
+shouldBeFalse("float_for_not_or_less()");
+
+function float_for_not_or_lesseq()
+{
+    for (var a = 0.1; !(a <= 1.1 || a <= 1.1); )
+        return true;
+    return false;
+}
+
+shouldBeFalse("float_for_not_or_lesseq()");
+
+function float_for_not_and_eq()
+{
+    for (var a = 0.1; !(a == 0.1 && a == 0.1); )
+        return true;
+    return false;
+}
+
+shouldBeFalse("float_for_not_and_eq()");
+
+function float_for_not_and_neq()
+{
+    for (var a = 0.1; !(a != 1.1 && a != 1.1); )
+        return true;
+    return false;
+}
+
+shouldBeFalse("float_for_not_and_neq()");
+
+function float_for_not_and_less()
+{
+    for (var a = 0.1; !(a < 1.1 && a < 1.1); )
+        return true;
+    return false;
+}
+
+shouldBeFalse("float_for_not_and_less()");
+
+function float_for_not_and_lesseq()
+{
+    for (var a = 0.1; !(a <= 1.1 && a <= 1.1); )
+        return true;
+    return false;
+}
+
+shouldBeFalse("float_for_not_and_lesseq()");
+
+function float_dowhile_not_or_eq()
+{
+    var a = 0.1;
+    var i = 0.1;
+    do {
+        if (i > 0.1)
+            return true;
+        i++;
+    } while (!(a == 0.1 || a == 0.1))
+    return false;
+}
+
+shouldBeFalse("float_dowhile_not_or_eq()");
+
+function float_dowhile_not_or_neq()
+{
+    var a = 0.1;
+    var i = 0.1;
+    do {
+        if (i > 0.1)
+            return true;
+        i++;
+    } while (!(a != 1.1 || a != 1.1))
+    return false;
+}
+
+shouldBeFalse("float_dowhile_not_or_neq()");
+
+function float_dowhile_not_or_less()
+{
+    var a = 0.1;
+    var i = 0.1;
+    do {
+        if (i > 0.1)
+            return true;
+        i++;
+    } while (!(a < 1.1 || a < 1.1))
+    return false;
+}
+
+shouldBeFalse("float_dowhile_not_or_less()");
+
+function float_dowhile_not_or_lesseq()
+{
+    var a = 0.1;
+    var i = 0.1;
+    do {
+        if (i > 0.1)
+            return true;
+        i++;
+    } while (!(a <= 1.1 || a <= 1.1))
+    return false;
+}
+
+shouldBeFalse("float_dowhile_not_or_lesseq()");
+
+function float_dowhile_not_and_eq()
+{
+    var a = 0.1;
+    var i = 0.1;
+    do {
+        if (i > 0.1)
+            return true;
+        i++;
+    } while (!(a == 0.1 && a == 0.1))
+    return false;
+}
+
+shouldBeFalse("float_dowhile_not_and_eq()");
+
+function float_dowhile_not_and_neq()
+{
+    var a = 0.1;
+    var i = 0.1;
+    do {
+        if (i > 0.1)
+            return true;
+        i++;
+    } while (!(a != 1.1 && a != 1.1))
+    return false;
+}
+
+shouldBeFalse("float_dowhile_not_and_neq()");
+
+function float_dowhile_not_and_less()
+{
+    var a = 0.1;
+    var i = 0.1;
+    do {
+        if (i > 0.1)
+            return true;
+        i++;
+    } while (!(a < 1.1 && a < 1.1))
+    return false;
+}
+
+shouldBeFalse("float_dowhile_not_and_less()");
+
+function float_dowhile_not_and_lesseq()
+{
+    var a = 0.1;
+    var i = 0.1;
+    do {
+        if (i > 0.1)
+            return true;
+        i++;
+    } while (!(a <= 1.1 && a <= 1.1))
+    return false;
+}
+
 var successfullyParsed = true;