Reland "Add Above/Below comparisons for UInt32 patterns"
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 14 Oct 2017 15:35:48 +0000 (15:35 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 14 Oct 2017 15:35:48 +0000 (15:35 +0000)
https://bugs.webkit.org/show_bug.cgi?id=177281

Reviewed by Saam Barati.

JSTests:

* stress/uint32-comparison-jump.js: Added.
(shouldBe):
(above):
(aboveOrEqual):
(below):
(belowOrEqual):
(notAbove):
(notAboveOrEqual):
(notBelow):
(notBelowOrEqual):
* stress/uint32-comparison.js: Added.
(shouldBe):
(above):
(aboveOrEqual):
(below):
(belowOrEqual):
(aboveTest):
(aboveOrEqualTest):
(belowTest):
(belowOrEqualTest):

Source/JavaScriptCore:

We reland this patch without DFGStrengthReduction change to see what causes
regression in the iOS bot.

Sometimes, we would like to have UInt32 operations in JS. While VM does
not support UInt32 nicely, VM supports efficient Int32 operations. As long
as signedness does not matter, we can just perform Int32 operations instead
and recognize its bit pattern as UInt32.

But of course, some operations respect signedness. The most frequently
used one is comparison. Octane/zlib performs UInt32 comparison by performing
`val >>> 0`. It emits op_urshift and op_unsigned. op_urshift produces
UInt32 in Int32 form. And op_unsigned will generate Double value if
the generated Int32 is < 0 (which should be UInt32).

There is a chance for optimization. The given code pattern is the following.

    op_unsigned(op_urshift(@1)) lessThan:< op_unsigned(op_urshift(@2))

This can be converted to the following.

    op_urshift(@1) below:< op_urshift(@2)

The above conversion is nice since

1. We can avoid op_unsigned. This could be unsignedness check in DFG. Since
this check depends on the value of Int32, dropping this check is not as easy as
removing Int32 edge filters.

2. We can perform unsigned comparison in Int32 form. We do not need to convert
them to DoubleRep.

Since the above comparison exists in Octane/zlib's *super* hot path, dropping
op_unsigned offers huge win.

At first, my patch attempts to convert the above thing in DFG pipeline.
However it poses several problems.

1. MovHint is not well removed. It makes UInt32ToNumber (which is for op_unsigned) live.
2. UInt32ToNumber could cause an OSR exit. So if we have the following nodes,

    2: UInt32ToNumber(@0)
    3: MovHint(@2, xxx)
    4: UInt32ToNumber(@1)
    5: MovHint(@1, xxx)

we could drop @5's MovHint. But @3 is difficult since @4 can exit.

So, instead, we start introducing a simple optimization in the bytecode compiler.
It performs pattern matching for op_urshift and comparison to drop op_unsigned.
We adds op_below and op_above families to bytecodes. They only accept Int32 and
perform unsigned comparison.

This offers 4% performance improvement in Octane/zlib.

                            baseline                  patched

zlib           x2     431.07483+-16.28434       414.33407+-9.38375         might be 1.0404x faster

* bytecode/BytecodeDumper.cpp:
(JSC::BytecodeDumper<Block>::printCompareJump):
(JSC::BytecodeDumper<Block>::dumpBytecode):
* bytecode/BytecodeDumper.h:
* bytecode/BytecodeList.json:
* bytecode/BytecodeUseDef.h:
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/Opcode.h:
(JSC::isBranch):
* bytecode/PreciseJumpTargetsInlines.h:
(JSC::extractStoredJumpTargetsForBytecodeOffset):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitJumpIfTrue):
(JSC::BytecodeGenerator::emitJumpIfFalse):
* bytecompiler/NodesCodegen.cpp:
(JSC::BinaryOpNode::emitBytecode):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGIntegerRangeOptimizationPhase.cpp:
* dfg/DFGNodeType.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileCompareUnsigned):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGValidate.cpp:
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileCompareBelow):
(JSC::FTL::DFG::LowerDFGToB3::compileCompareBelowEq):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
* jit/JIT.h:
* jit/JITArithmetic.cpp:
(JSC::JIT::emit_op_below):
(JSC::JIT::emit_op_beloweq):
(JSC::JIT::emit_op_jbelow):
(JSC::JIT::emit_op_jbeloweq):
(JSC::JIT::emit_compareUnsignedAndJump):
(JSC::JIT::emit_compareUnsigned):
* jit/JITArithmetic32_64.cpp:
(JSC::JIT::emit_compareUnsignedAndJump):
(JSC::JIT::emit_compareUnsigned):
* llint/LowLevelInterpreter.asm:
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* parser/Nodes.h:
(JSC::ExpressionNode::isBinaryOpNode const):

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

37 files changed:
JSTests/ChangeLog
JSTests/stress/uint32-comparison-jump.js [new file with mode: 0644]
JSTests/stress/uint32-comparison.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecode/BytecodeDumper.cpp
Source/JavaScriptCore/bytecode/BytecodeDumper.h
Source/JavaScriptCore/bytecode/BytecodeList.json
Source/JavaScriptCore/bytecode/BytecodeUseDef.h
Source/JavaScriptCore/bytecode/Opcode.h
Source/JavaScriptCore/bytecode/PreciseJumpTargetsInlines.h
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGCapabilities.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGIntegerRangeOptimizationPhase.cpp
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/dfg/DFGValidate.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/jit/JIT.cpp
Source/JavaScriptCore/jit/JIT.h
Source/JavaScriptCore/jit/JITArithmetic.cpp
Source/JavaScriptCore/jit/JITArithmetic32_64.cpp
Source/JavaScriptCore/llint/LowLevelInterpreter.asm
Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
Source/JavaScriptCore/parser/Nodes.h

index 6da4d8e..27c02fc 100644 (file)
@@ -1,3 +1,31 @@
+2017-10-14  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        Reland "Add Above/Below comparisons for UInt32 patterns"
+        https://bugs.webkit.org/show_bug.cgi?id=177281
+
+        Reviewed by Saam Barati.
+
+        * stress/uint32-comparison-jump.js: Added.
+        (shouldBe):
+        (above):
+        (aboveOrEqual):
+        (below):
+        (belowOrEqual):
+        (notAbove):
+        (notAboveOrEqual):
+        (notBelow):
+        (notBelowOrEqual):
+        * stress/uint32-comparison.js: Added.
+        (shouldBe):
+        (above):
+        (aboveOrEqual):
+        (below):
+        (belowOrEqual):
+        (aboveTest):
+        (aboveOrEqualTest):
+        (belowTest):
+        (belowOrEqualTest):
+
 2017-10-12  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         WebAssembly: Wasm functions should have either JSFunctionType or TypeOfShouldCallGetCallData
diff --git a/JSTests/stress/uint32-comparison-jump.js b/JSTests/stress/uint32-comparison-jump.js
new file mode 100644 (file)
index 0000000..8a6be9f
--- /dev/null
@@ -0,0 +1,141 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function above(a, trap) {
+    let result = 0;
+    for (let i = 0; (a >>> 0) > (i >>> 0); ++i) {
+        result += i;
+        if (i === trap)
+            break;
+    }
+    return result;
+}
+noInline(above);
+
+function aboveOrEqual(a, trap) {
+    let result = 0;
+    for (let i = 0; (a >>> 0) >= (i >>> 0); ++i) {
+        result += i;
+        if (i === trap)
+            break;
+    }
+    return result;
+}
+noInline(aboveOrEqual);
+
+function below(a, trap) {
+    let result = 0;
+    for (let i = 0; (i >>> 0) < (a >>> 0); ++i) {
+        result += i;
+        if (i === trap)
+            break;
+    }
+    return result;
+}
+noInline(below);
+
+function belowOrEqual(a, trap) {
+    let result = 0;
+    for (let i = 0; (i >>> 0) <= (a >>> 0); ++i) {
+        result += i;
+        if (i === trap)
+            break;
+    }
+    return result;
+}
+noInline(belowOrEqual);
+
+function notAbove(a, trap) {
+    let result = 0;
+    for (let i = 0; (a >>> 0) > (i >>> 0) && a; ++i) {
+        result += i;
+        if (i === trap)
+            break;
+    }
+    return result;
+}
+noInline(notAbove);
+
+function notAboveOrEqual(a, trap) {
+    let result = 0;
+    for (let i = 0; (a >>> 0) >= (i >>> 0) && a; ++i) {
+        result += i;
+        if (i === trap)
+            break;
+    }
+    return result;
+}
+noInline(notAboveOrEqual);
+
+function notBelow(a, trap) {
+    let result = 0;
+    for (let i = 0; (i >>> 0) < (a >>> 0) && a; ++i) {
+        result += i;
+        if (i === trap)
+            break;
+    }
+    return result;
+}
+noInline(notBelow);
+
+function notBelowOrEqual(a, trap) {
+    let result = 0;
+    for (let i = 0; (i >>> 0) <= (a >>> 0) && a; ++i) {
+        result += i;
+        if (i === trap)
+            break;
+    }
+    return result;
+}
+noInline(notBelowOrEqual);
+
+for (var i = 0; i < 1e2; ++i) {
+    shouldBe(above(0, -1), 0);
+    shouldBe(above(20000, -1), 199990000);
+    shouldBe(above(-1, 10000), 50005000);
+}
+
+for (var i = 0; i < 1e2; ++i) {
+    shouldBe(aboveOrEqual(0, -1), 0);
+    shouldBe(aboveOrEqual(20000, -1), 200010000);
+    shouldBe(aboveOrEqual(-1, 10000), 50005000);
+}
+
+for (var i = 0; i < 1e2; ++i) {
+    shouldBe(below(0, -1), 0);
+    shouldBe(below(20000, -1), 199990000);
+    shouldBe(below(-1, 10000), 50005000);
+}
+
+for (var i = 0; i < 1e2; ++i) {
+    shouldBe(belowOrEqual(0, -1), 0);
+    shouldBe(belowOrEqual(20000, -1), 200010000);
+    shouldBe(belowOrEqual(-1, 10000), 50005000);
+}
+
+for (var i = 0; i < 1e2; ++i) {
+    shouldBe(notAbove(0, -1), 0);
+    shouldBe(notAbove(20000, -1), 199990000);
+    shouldBe(notAbove(-1, 10000), 50005000);
+}
+
+for (var i = 0; i < 1e2; ++i) {
+    shouldBe(notAboveOrEqual(0, -1), 0);
+    shouldBe(notAboveOrEqual(20000, -1), 200010000);
+    shouldBe(notAboveOrEqual(-1, 10000), 50005000);
+}
+
+for (var i = 0; i < 1e2; ++i) {
+    shouldBe(notBelow(0, -1), 0);
+    shouldBe(notBelow(20000, -1), 199990000);
+    shouldBe(notBelow(-1, 10000), 50005000);
+}
+
+for (var i = 0; i < 1e2; ++i) {
+    shouldBe(notBelowOrEqual(0, -1), 0);
+    shouldBe(notBelowOrEqual(20000, -1), 200010000);
+    shouldBe(notBelowOrEqual(-1, 10000), 50005000);
+}
+
diff --git a/JSTests/stress/uint32-comparison.js b/JSTests/stress/uint32-comparison.js
new file mode 100644 (file)
index 0000000..07bec6a
--- /dev/null
@@ -0,0 +1,88 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function above(a, b) {
+    return (a >>> 0) > (b >>> 0);
+}
+noInline(above);
+
+function aboveOrEqual(a, b) {
+    return (a >>> 0) >= (b >>> 0);
+}
+noInline(aboveOrEqual);
+
+function below(a, b) {
+    return (a >>> 0) < (b >>> 0);
+}
+noInline(below);
+
+function belowOrEqual(a, b) {
+    return (a >>> 0) <= (b >>> 0);
+}
+noInline(belowOrEqual);
+
+(function aboveTest() {
+    for (let i = 0; i < 1e5; ++i) {
+        shouldBe(above(0, 20), false);
+        shouldBe(above(0, 0), false);
+        shouldBe(above(0, -0), false);
+        shouldBe(above(-1, 0), true);
+        shouldBe(above(-1, -1), false);
+        shouldBe(above(-1, 1), true);
+        shouldBe(above(1, -1), false);
+        shouldBe(above(1, 0xffffffff), false);
+        shouldBe(above(0xffffffff, 0xffffffff), false);
+        shouldBe(above(-1, 0xffffffff), false);
+        shouldBe(above(-1, 0xfffffffff), false);
+    }
+}());
+
+(function aboveOrEqualTest() {
+    for (let i = 0; i < 1e5; ++i) {
+        shouldBe(aboveOrEqual(0, 20), false);
+        shouldBe(aboveOrEqual(0, 0), true);
+        shouldBe(aboveOrEqual(0, -0), true);
+        shouldBe(aboveOrEqual(-1, 0), true);
+        shouldBe(aboveOrEqual(-1, -1), true);
+        shouldBe(aboveOrEqual(-1, 1), true);
+        shouldBe(aboveOrEqual(1, -1), false);
+        shouldBe(aboveOrEqual(1, 0xffffffff), false);
+        shouldBe(aboveOrEqual(0xffffffff, 0xffffffff), true);
+        shouldBe(aboveOrEqual(-1, 0xffffffff), true);
+        shouldBe(aboveOrEqual(-1, 0xfffffffff), true);
+    }
+}());
+
+(function belowTest() {
+    for (let i = 0; i < 1e5; ++i) {
+        shouldBe(below(0, 20), true);
+        shouldBe(below(0, 0), false);
+        shouldBe(below(0, -0), false);
+        shouldBe(below(-1, 0), false);
+        shouldBe(below(-1, -1), false);
+        shouldBe(below(-1, 1), false);
+        shouldBe(below(1, -1), true);
+        shouldBe(below(1, 0xffffffff), true);
+        shouldBe(below(0xffffffff, 0xffffffff), false);
+        shouldBe(below(-1, 0xffffffff), false);
+        shouldBe(below(-1, 0xfffffffff), false);
+    }
+}());
+
+(function belowOrEqualTest() {
+    for (let i = 0; i < 1e5; ++i) {
+        shouldBe(belowOrEqual(0, 20), true);
+        shouldBe(belowOrEqual(0, 0), true);
+        shouldBe(belowOrEqual(0, -0), true);
+        shouldBe(belowOrEqual(-1, 0), false);
+        shouldBe(belowOrEqual(-1, -1), true);
+        shouldBe(belowOrEqual(-1, 1), false);
+        shouldBe(belowOrEqual(1, -1), true);
+        shouldBe(belowOrEqual(1, 0xffffffff), true);
+        shouldBe(belowOrEqual(0xffffffff, 0xffffffff), true);
+        shouldBe(belowOrEqual(-1, 0xffffffff), true);
+        shouldBe(belowOrEqual(-1, 0xfffffffff), true);
+    }
+}());
index 57bee9a..969a8ca 100644 (file)
@@ -1,3 +1,135 @@
+2017-10-14  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        Reland "Add Above/Below comparisons for UInt32 patterns"
+        https://bugs.webkit.org/show_bug.cgi?id=177281
+
+        Reviewed by Saam Barati.
+
+        We reland this patch without DFGStrengthReduction change to see what causes
+        regression in the iOS bot.
+
+        Sometimes, we would like to have UInt32 operations in JS. While VM does
+        not support UInt32 nicely, VM supports efficient Int32 operations. As long
+        as signedness does not matter, we can just perform Int32 operations instead
+        and recognize its bit pattern as UInt32.
+
+        But of course, some operations respect signedness. The most frequently
+        used one is comparison. Octane/zlib performs UInt32 comparison by performing
+        `val >>> 0`. It emits op_urshift and op_unsigned. op_urshift produces
+        UInt32 in Int32 form. And op_unsigned will generate Double value if
+        the generated Int32 is < 0 (which should be UInt32).
+
+        There is a chance for optimization. The given code pattern is the following.
+
+            op_unsigned(op_urshift(@1)) lessThan:< op_unsigned(op_urshift(@2))
+
+        This can be converted to the following.
+
+            op_urshift(@1) below:< op_urshift(@2)
+
+        The above conversion is nice since
+
+        1. We can avoid op_unsigned. This could be unsignedness check in DFG. Since
+        this check depends on the value of Int32, dropping this check is not as easy as
+        removing Int32 edge filters.
+
+        2. We can perform unsigned comparison in Int32 form. We do not need to convert
+        them to DoubleRep.
+
+        Since the above comparison exists in Octane/zlib's *super* hot path, dropping
+        op_unsigned offers huge win.
+
+        At first, my patch attempts to convert the above thing in DFG pipeline.
+        However it poses several problems.
+
+        1. MovHint is not well removed. It makes UInt32ToNumber (which is for op_unsigned) live.
+        2. UInt32ToNumber could cause an OSR exit. So if we have the following nodes,
+
+            2: UInt32ToNumber(@0)
+            3: MovHint(@2, xxx)
+            4: UInt32ToNumber(@1)
+            5: MovHint(@1, xxx)
+
+        we could drop @5's MovHint. But @3 is difficult since @4 can exit.
+
+        So, instead, we start introducing a simple optimization in the bytecode compiler.
+        It performs pattern matching for op_urshift and comparison to drop op_unsigned.
+        We adds op_below and op_above families to bytecodes. They only accept Int32 and
+        perform unsigned comparison.
+
+        This offers 4% performance improvement in Octane/zlib.
+
+                                    baseline                  patched
+
+        zlib           x2     431.07483+-16.28434       414.33407+-9.38375         might be 1.0404x faster
+
+        * bytecode/BytecodeDumper.cpp:
+        (JSC::BytecodeDumper<Block>::printCompareJump):
+        (JSC::BytecodeDumper<Block>::dumpBytecode):
+        * bytecode/BytecodeDumper.h:
+        * bytecode/BytecodeList.json:
+        * bytecode/BytecodeUseDef.h:
+        (JSC::computeUsesForBytecodeOffset):
+        (JSC::computeDefsForBytecodeOffset):
+        * bytecode/Opcode.h:
+        (JSC::isBranch):
+        * bytecode/PreciseJumpTargetsInlines.h:
+        (JSC::extractStoredJumpTargetsForBytecodeOffset):
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::emitJumpIfTrue):
+        (JSC::BytecodeGenerator::emitJumpIfFalse):
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::BinaryOpNode::emitBytecode):
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGCapabilities.cpp:
+        (JSC::DFG::capabilityLevel):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGIntegerRangeOptimizationPhase.cpp:
+        * dfg/DFGNodeType.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileCompareUnsigned):
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGValidate.cpp:
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileCompareBelow):
+        (JSC::FTL::DFG::LowerDFGToB3::compileCompareBelowEq):
+        * jit/JIT.cpp:
+        (JSC::JIT::privateCompileMainPass):
+        * jit/JIT.h:
+        * jit/JITArithmetic.cpp:
+        (JSC::JIT::emit_op_below):
+        (JSC::JIT::emit_op_beloweq):
+        (JSC::JIT::emit_op_jbelow):
+        (JSC::JIT::emit_op_jbeloweq):
+        (JSC::JIT::emit_compareUnsignedAndJump):
+        (JSC::JIT::emit_compareUnsigned):
+        * jit/JITArithmetic32_64.cpp:
+        (JSC::JIT::emit_compareUnsignedAndJump):
+        (JSC::JIT::emit_compareUnsigned):
+        * llint/LowLevelInterpreter.asm:
+        * llint/LowLevelInterpreter32_64.asm:
+        * llint/LowLevelInterpreter64.asm:
+        * parser/Nodes.h:
+        (JSC::ExpressionNode::isBinaryOpNode const):
+
 2017-10-12  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         WebAssembly: Wasm functions should have either JSFunctionType or TypeOfShouldCallGetCallData
index 5ab7b81..9bc117f 100644 (file)
@@ -360,6 +360,16 @@ void BytecodeDumper<Block>::printConditionalJump(PrintStream& out, const typenam
 }
 
 template<class Block>
+void BytecodeDumper<Block>::printCompareJump(PrintStream& out, const typename Block::Instruction*, const typename Block::Instruction*& it, int location, const char* op)
+{
+    int r0 = (++it)->u.operand;
+    int r1 = (++it)->u.operand;
+    int offset = (++it)->u.operand;
+    printLocationAndOp(out, location, it, op);
+    out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset);
+}
+
+template<class Block>
 void BytecodeDumper<Block>::printGetByIdOp(PrintStream& out, int location, const typename Block::Instruction*& it)
 {
     const char* op;
@@ -834,6 +844,14 @@ void BytecodeDumper<Block>::dumpBytecode(PrintStream& out, const typename Block:
         printBinaryOp(out, location, it, "greatereq");
         break;
     }
+    case op_below: {
+        printBinaryOp(out, location, it, "below");
+        break;
+    }
+    case op_beloweq: {
+        printBinaryOp(out, location, it, "beloweq");
+        break;
+    }
     case op_inc: {
         int r0 = (++it)->u.operand;
         printLocationOpAndRegisterOperand(out, location, it, "inc", r0);
@@ -1197,67 +1215,43 @@ void BytecodeDumper<Block>::dumpBytecode(PrintStream& out, const typename Block:
         break;
     }
     case op_jless: {
-        int r0 = (++it)->u.operand;
-        int r1 = (++it)->u.operand;
-        int offset = (++it)->u.operand;
-        printLocationAndOp(out, location, it, "jless");
-        out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset);
+        printCompareJump(out, begin, it, location, "jless");
         break;
     }
     case op_jlesseq: {
-        int r0 = (++it)->u.operand;
-        int r1 = (++it)->u.operand;
-        int offset = (++it)->u.operand;
-        printLocationAndOp(out, location, it, "jlesseq");
-        out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset);
+        printCompareJump(out, begin, it, location, "jlesseq");
         break;
     }
     case op_jgreater: {
-        int r0 = (++it)->u.operand;
-        int r1 = (++it)->u.operand;
-        int offset = (++it)->u.operand;
-        printLocationAndOp(out, location, it, "jgreater");
-        out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset);
+        printCompareJump(out, begin, it, location, "jgreater");
         break;
     }
     case op_jgreatereq: {
-        int r0 = (++it)->u.operand;
-        int r1 = (++it)->u.operand;
-        int offset = (++it)->u.operand;
-        printLocationAndOp(out, location, it, "jgreatereq");
-        out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset);
+        printCompareJump(out, begin, it, location, "jgreatereq");
         break;
     }
     case op_jnless: {
-        int r0 = (++it)->u.operand;
-        int r1 = (++it)->u.operand;
-        int offset = (++it)->u.operand;
-        printLocationAndOp(out, location, it, "jnless");
-        out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset);
+        printCompareJump(out, begin, it, location, "jnless");
         break;
     }
     case op_jnlesseq: {
-        int r0 = (++it)->u.operand;
-        int r1 = (++it)->u.operand;
-        int offset = (++it)->u.operand;
-        printLocationAndOp(out, location, it, "jnlesseq");
-        out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset);
+        printCompareJump(out, begin, it, location, "jnlesseq");
         break;
     }
     case op_jngreater: {
-        int r0 = (++it)->u.operand;
-        int r1 = (++it)->u.operand;
-        int offset = (++it)->u.operand;
-        printLocationAndOp(out, location, it, "jngreater");
-        out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset);
+        printCompareJump(out, begin, it, location, "jngreater");
         break;
     }
     case op_jngreatereq: {
-        int r0 = (++it)->u.operand;
-        int r1 = (++it)->u.operand;
-        int offset = (++it)->u.operand;
-        printLocationAndOp(out, location, it, "jngreatereq");
-        out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset);
+        printCompareJump(out, begin, it, location, "jngreatereq");
+        break;
+    }
+    case op_jbelow: {
+        printCompareJump(out, begin, it, location, "jbelow");
+        break;
+    }
+    case op_jbeloweq: {
+        printCompareJump(out, begin, it, location, "jbeloweq");
         break;
     }
     case op_loop_hint: {
index 8fe6033..7cd08c9 100644 (file)
@@ -67,6 +67,7 @@ private:
     void printUnaryOp(PrintStream& out, int location, const Instruction*& it, const char* op);
     void printBinaryOp(PrintStream& out, int location, const Instruction*& it, const char* op);
     void printConditionalJump(PrintStream& out, const Instruction*, const Instruction*& it, int location, const char* op);
+    void printCompareJump(PrintStream& out, const Instruction*, const Instruction*& it, int location, const char* op);
     void printGetByIdOp(PrintStream& out, int location, const Instruction*& it);
     void printGetByIdCacheStatus(PrintStream& out, int location, const StubInfoMap&);
     void printPutByIdCacheStatus(PrintStream& out, int location, const StubInfoMap&);
index 478482a..e11314f 100644 (file)
@@ -36,6 +36,8 @@
             { "name" : "op_lesseq", "length" : 4 },
             { "name" : "op_greater", "length" : 4 },
             { "name" : "op_greatereq", "length" : 4 },
+            { "name" : "op_below", "length" : 4 },
+            { "name" : "op_beloweq", "length" : 4 },
             { "name" : "op_inc", "length" : 2 },
             { "name" : "op_dec", "length" : 2 },
             { "name" : "op_to_number", "length" : 4 },
             { "name" : "op_jnlesseq", "length" : 4 },
             { "name" : "op_jngreater", "length" : 4 },
             { "name" : "op_jngreatereq", "length" : 4 },
+            { "name" : "op_jbelow", "length" : 4 },
+            { "name" : "op_jbeloweq", "length" : 4 },
             { "name" : "op_loop_hint", "length" : 1 },
             { "name" : "op_switch_imm", "length" : 4 },
             { "name" : "op_switch_char", "length" : 4 },
index 22ed8df..9749af7 100644 (file)
@@ -85,6 +85,8 @@ void computeUsesForBytecodeOffset(Block* codeBlock, OpcodeID opcodeID, Instructi
     case op_jngreater:
     case op_jngreatereq:
     case op_jless:
+    case op_jbelow:
+    case op_jbeloweq:
     case op_set_function_name:
     case op_log_shadow_chicken_tail: {
         ASSERT(opcodeLengths[opcodeID] > 2);
@@ -237,6 +239,8 @@ void computeUsesForBytecodeOffset(Block* codeBlock, OpcodeID opcodeID, Instructi
     case op_lesseq:
     case op_greater:
     case op_greatereq:
+    case op_below:
+    case op_beloweq:
     case op_nstricteq:
     case op_stricteq:
     case op_neq:
@@ -343,6 +347,8 @@ void computeDefsForBytecodeOffset(Block* codeBlock, OpcodeID opcodeID, Instructi
     case op_jnlesseq:
     case op_jngreater:
     case op_jngreatereq:
+    case op_jbelow:
+    case op_jbeloweq:
     case op_loop_hint:
     case op_switch_imm:
     case op_switch_char:
@@ -463,6 +469,8 @@ void computeDefsForBytecodeOffset(Block* codeBlock, OpcodeID opcodeID, Instructi
     case op_lesseq:
     case op_greater:
     case op_greatereq:
+    case op_below:
+    case op_beloweq:
     case op_neq_null:
     case op_eq_null:
     case op_not:
index 79b5d7f..758d340 100644 (file)
@@ -151,6 +151,8 @@ inline bool isBranch(OpcodeID opcodeID)
     case op_jnlesseq:
     case op_jngreater:
     case op_jngreatereq:
+    case op_jbelow:
+    case op_jbeloweq:
     case op_switch_imm:
     case op_switch_char:
     case op_switch_string:
index 42ac5ed..36725f9 100644 (file)
@@ -55,6 +55,8 @@ inline void extractStoredJumpTargetsForBytecodeOffset(Block* codeBlock, Instruct
     case op_jnlesseq:
     case op_jngreater:
     case op_jngreatereq:
+    case op_jbelow:
+    case op_jbeloweq:
         function(current[3].u.operand);
         break;
     case op_switch_imm:
index 2e8e91a..5cbbf4a 100644 (file)
@@ -1366,7 +1366,7 @@ void BytecodeGenerator::emitJump(Label& target)
 
 void BytecodeGenerator::emitJumpIfTrue(RegisterID* cond, Label& target)
 {
-    if (m_lastOpcodeID == op_less) {
+    auto fuseCompareAndJump = [&] (OpcodeID jumpID) {
         int dstIndex;
         int src1Index;
         int src2Index;
@@ -1377,63 +1377,33 @@ void BytecodeGenerator::emitJumpIfTrue(RegisterID* cond, Label& target)
             rewindBinaryOp();
 
             size_t begin = instructions().size();
-            emitOpcode(op_jless);
+            emitOpcode(jumpID);
             instructions().append(src1Index);
             instructions().append(src2Index);
             instructions().append(target.bind(begin, instructions().size()));
-            return;
+            return true;
         }
-    } else if (m_lastOpcodeID == op_lesseq) {
-        int dstIndex;
-        int src1Index;
-        int src2Index;
-
-        retrieveLastBinaryOp(dstIndex, src1Index, src2Index);
-
-        if (cond->index() == dstIndex && cond->isTemporary() && !cond->refCount()) {
-            rewindBinaryOp();
+        return false;
+    };
 
-            size_t begin = instructions().size();
-            emitOpcode(op_jlesseq);
-            instructions().append(src1Index);
-            instructions().append(src2Index);
-            instructions().append(target.bind(begin, instructions().size()));
+    if (m_lastOpcodeID == op_less) {
+        if (fuseCompareAndJump(op_jless))
+            return;
+    } else if (m_lastOpcodeID == op_lesseq) {
+        if (fuseCompareAndJump(op_jlesseq))
             return;
-        }
     } else if (m_lastOpcodeID == op_greater) {
-        int dstIndex;
-        int src1Index;
-        int src2Index;
-
-        retrieveLastBinaryOp(dstIndex, src1Index, src2Index);
-
-        if (cond->index() == dstIndex && cond->isTemporary() && !cond->refCount()) {
-            rewindBinaryOp();
-
-            size_t begin = instructions().size();
-            emitOpcode(op_jgreater);
-            instructions().append(src1Index);
-            instructions().append(src2Index);
-            instructions().append(target.bind(begin, instructions().size()));
+        if (fuseCompareAndJump(op_jgreater))
             return;
-        }
     } else if (m_lastOpcodeID == op_greatereq) {
-        int dstIndex;
-        int src1Index;
-        int src2Index;
-
-        retrieveLastBinaryOp(dstIndex, src1Index, src2Index);
-
-        if (cond->index() == dstIndex && cond->isTemporary() && !cond->refCount()) {
-            rewindBinaryOp();
-
-            size_t begin = instructions().size();
-            emitOpcode(op_jgreatereq);
-            instructions().append(src1Index);
-            instructions().append(src2Index);
-            instructions().append(target.bind(begin, instructions().size()));
+        if (fuseCompareAndJump(op_jgreatereq))
+            return;
+    } else if (m_lastOpcodeID == op_below) {
+        if (fuseCompareAndJump(op_jbelow))
+            return;
+    } else if (m_lastOpcodeID == op_beloweq) {
+        if (fuseCompareAndJump(op_jbeloweq))
             return;
-        }
     } else if (m_lastOpcodeID == op_eq_null && target.isForward()) {
         int dstIndex;
         int srcIndex;
@@ -1475,7 +1445,7 @@ void BytecodeGenerator::emitJumpIfTrue(RegisterID* cond, Label& target)
 
 void BytecodeGenerator::emitJumpIfFalse(RegisterID* cond, Label& target)
 {
-    if (m_lastOpcodeID == op_less && target.isForward()) {
+    auto fuseCompareAndJump = [&] (OpcodeID jumpID, bool replaceOperands) {
         int dstIndex;
         int src1Index;
         int src2Index;
@@ -1486,63 +1456,36 @@ void BytecodeGenerator::emitJumpIfFalse(RegisterID* cond, Label& target)
             rewindBinaryOp();
 
             size_t begin = instructions().size();
-            emitOpcode(op_jnless);
+            emitOpcode(jumpID);
+            // Since op_below and op_beloweq only accepts Int32, replacing operands is not observable to users.
+            if (replaceOperands)
+                std::swap(src1Index, src2Index);
             instructions().append(src1Index);
             instructions().append(src2Index);
             instructions().append(target.bind(begin, instructions().size()));
-            return;
+            return true;
         }
-    } else if (m_lastOpcodeID == op_lesseq && target.isForward()) {
-        int dstIndex;
-        int src1Index;
-        int src2Index;
-
-        retrieveLastBinaryOp(dstIndex, src1Index, src2Index);
-
-        if (cond->index() == dstIndex && cond->isTemporary() && !cond->refCount()) {
-            rewindBinaryOp();
+        return false;
+    };
 
-            size_t begin = instructions().size();
-            emitOpcode(op_jnlesseq);
-            instructions().append(src1Index);
-            instructions().append(src2Index);
-            instructions().append(target.bind(begin, instructions().size()));
+    if (m_lastOpcodeID == op_less && target.isForward()) {
+        if (fuseCompareAndJump(op_jnless, false))
+            return;
+    } else if (m_lastOpcodeID == op_lesseq && target.isForward()) {
+        if (fuseCompareAndJump(op_jnlesseq, false))
             return;
-        }
     } else if (m_lastOpcodeID == op_greater && target.isForward()) {
-        int dstIndex;
-        int src1Index;
-        int src2Index;
-
-        retrieveLastBinaryOp(dstIndex, src1Index, src2Index);
-
-        if (cond->index() == dstIndex && cond->isTemporary() && !cond->refCount()) {
-            rewindBinaryOp();
-
-            size_t begin = instructions().size();
-            emitOpcode(op_jngreater);
-            instructions().append(src1Index);
-            instructions().append(src2Index);
-            instructions().append(target.bind(begin, instructions().size()));
+        if (fuseCompareAndJump(op_jngreater, false))
             return;
-        }
     } else if (m_lastOpcodeID == op_greatereq && target.isForward()) {
-        int dstIndex;
-        int src1Index;
-        int src2Index;
-
-        retrieveLastBinaryOp(dstIndex, src1Index, src2Index);
-
-        if (cond->index() == dstIndex && cond->isTemporary() && !cond->refCount()) {
-            rewindBinaryOp();
-
-            size_t begin = instructions().size();
-            emitOpcode(op_jngreatereq);
-            instructions().append(src1Index);
-            instructions().append(src2Index);
-            instructions().append(target.bind(begin, instructions().size()));
+        if (fuseCompareAndJump(op_jngreatereq, false))
+            return;
+    } else if (m_lastOpcodeID == op_below && target.isForward()) {
+        if (fuseCompareAndJump(op_jbeloweq, true))
+            return;
+    } else if (m_lastOpcodeID == op_beloweq && target.isForward()) {
+        if (fuseCompareAndJump(op_jbelow, true))
             return;
-        }
     } else if (m_lastOpcodeID == op_not) {
         int dstIndex;
         int srcIndex;
index 734568e..355b4a7 100644 (file)
@@ -1988,6 +1988,64 @@ RegisterID* BinaryOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID*
 {
     OpcodeID opcodeID = this->opcodeID();
 
+    if (opcodeID == op_less || opcodeID == op_lesseq || opcodeID == op_greater || opcodeID == op_greatereq) {
+        auto isUInt32 = [&] (ExpressionNode* node) -> std::optional<UInt32Result> {
+            if (node->isBinaryOpNode() && static_cast<BinaryOpNode*>(node)->opcodeID() == op_urshift)
+                return UInt32Result::UInt32;
+            if (node->isNumber() && static_cast<NumberNode*>(node)->isIntegerNode()) {
+                int32_t value = static_cast<int32_t>(static_cast<IntegerNode*>(node)->value());
+                if (value >= 0)
+                    return UInt32Result::Constant;
+            }
+            return std::nullopt;
+        };
+        auto leftResult = isUInt32(m_expr1);
+        auto rightResult = isUInt32(m_expr2);
+        if ((leftResult && rightResult) && (leftResult.value() == UInt32Result::UInt32 || rightResult.value() == UInt32Result::UInt32)) {
+            auto* left = m_expr1;
+            auto* right = m_expr2;
+            if (left->isBinaryOpNode()) {
+                ASSERT(static_cast<BinaryOpNode*>(left)->opcodeID() == op_urshift);
+                static_cast<BinaryOpNode*>(left)->m_shouldToUnsignedResult = false;
+            }
+            if (right->isBinaryOpNode()) {
+                ASSERT(static_cast<BinaryOpNode*>(right)->opcodeID() == op_urshift);
+                static_cast<BinaryOpNode*>(right)->m_shouldToUnsignedResult = false;
+            }
+            RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(left, m_rightHasAssignments, right->isPure(generator));
+            RefPtr<RegisterID> src2 = generator.emitNode(right);
+            generator.emitExpressionInfo(position(), position(), position());
+
+            // Since the both sides only accept Int32, replacing operands is not observable to users.
+            bool replaceOperands = false;
+            OpcodeID resultOp = opcodeID;
+            switch (opcodeID) {
+            case op_less:
+                resultOp = op_below;
+                break;
+            case op_lesseq:
+                resultOp = op_beloweq;
+                break;
+            case op_greater:
+                resultOp = op_below;
+                replaceOperands = true;
+                break;
+            case op_greatereq:
+                resultOp = op_beloweq;
+                replaceOperands = true;
+                break;
+            default:
+                RELEASE_ASSERT_NOT_REACHED();
+            }
+            OperandTypes operandTypes(left->resultDescriptor(), right->resultDescriptor());
+            if (replaceOperands) {
+                std::swap(src1, src2);
+                operandTypes = OperandTypes(right->resultDescriptor(), left->resultDescriptor());
+            }
+            return generator.emitBinaryOp(resultOp, generator.finalDestination(dst, src1.get()), src1.get(), src2.get(), operandTypes);
+        }
+    }
+
     if (opcodeID == op_add && m_expr1->isAdd() && m_expr1->resultDescriptor().definitelyIsString()) {
         generator.emitExpressionInfo(position(), position(), position());
         return emitStrcat(generator, dst);
@@ -2023,8 +2081,10 @@ RegisterID* BinaryOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID*
         return generator.emitUnaryOp(op_not, generator.finalDestination(dst, tmp.get()), tmp.get());
     }
     RegisterID* result = generator.emitBinaryOp(opcodeID, generator.finalDestination(dst, src1.get()), src1.get(), src2.get(), OperandTypes(left->resultDescriptor(), right->resultDescriptor()));
-    if (opcodeID == op_urshift && dst != generator.ignoredResult())
-        return generator.emitUnaryOp(op_unsigned, result, result);
+    if (m_shouldToUnsignedResult) {
+        if (opcodeID == op_urshift && dst != generator.ignoredResult())
+            return generator.emitUnaryOp(op_unsigned, result, result);
+    }
     return result;
 }
 
index bea1689..56e5ac5 100644 (file)
@@ -1407,7 +1407,48 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         forNode(node).setType(m_graph, SpecStringIdent);
         break;
     }
-            
+
+    case CompareBelow:
+    case CompareBelowEq: {
+        JSValue leftConst = forNode(node->child1()).value();
+        JSValue rightConst = forNode(node->child2()).value();
+        if (leftConst && rightConst) {
+            if (leftConst.isInt32() && rightConst.isInt32()) {
+                uint32_t a = static_cast<uint32_t>(leftConst.asInt32());
+                uint32_t b = static_cast<uint32_t>(rightConst.asInt32());
+                switch (node->op()) {
+                case CompareBelow:
+                    setConstant(node, jsBoolean(a < b));
+                    break;
+                case CompareBelowEq:
+                    setConstant(node, jsBoolean(a <= b));
+                    break;
+                default:
+                    RELEASE_ASSERT_NOT_REACHED();
+                    break;
+                }
+                break;
+            }
+        }
+
+        if (node->child1() == node->child2()) {
+            switch (node->op()) {
+            case CompareBelow:
+                setConstant(node, jsBoolean(false));
+                break;
+            case CompareBelowEq:
+                setConstant(node, jsBoolean(true));
+                break;
+            default:
+                DFG_CRASH(m_graph, node, "Unexpected node type");
+                break;
+            }
+            break;
+        }
+        forNode(node).setType(SpecBoolean);
+        break;
+    }
+
     case CompareLess:
     case CompareLessEq:
     case CompareGreater:
index a12227a..87fd695 100644 (file)
@@ -4634,6 +4634,20 @@ void ByteCodeParser::parseBlock(unsigned limit)
             NEXT_OPCODE(op_greatereq);
         }
 
+        case op_below: {
+            Node* op1 = get(VirtualRegister(currentInstruction[2].u.operand));
+            Node* op2 = get(VirtualRegister(currentInstruction[3].u.operand));
+            set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(CompareBelow, op1, op2));
+            NEXT_OPCODE(op_below);
+        }
+
+        case op_beloweq: {
+            Node* op1 = get(VirtualRegister(currentInstruction[2].u.operand));
+            Node* op2 = get(VirtualRegister(currentInstruction[3].u.operand));
+            set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(CompareBelowEq, op1, op2));
+            NEXT_OPCODE(op_beloweq);
+        }
+
         case op_eq: {
             Node* op1 = get(VirtualRegister(currentInstruction[2].u.operand));
             Node* op2 = get(VirtualRegister(currentInstruction[3].u.operand));
@@ -5079,7 +5093,25 @@ void ByteCodeParser::parseBlock(unsigned limit)
             addToGraph(Branch, OpInfo(branchData(m_currentIndex + OPCODE_LENGTH(op_jngreatereq), m_currentIndex + relativeOffset)), condition);
             LAST_OPCODE(op_jngreatereq);
         }
-            
+
+        case op_jbelow: {
+            unsigned relativeOffset = currentInstruction[3].u.operand;
+            Node* op1 = get(VirtualRegister(currentInstruction[1].u.operand));
+            Node* op2 = get(VirtualRegister(currentInstruction[2].u.operand));
+            Node* condition = addToGraph(CompareBelow, op1, op2);
+            addToGraph(Branch, OpInfo(branchData(m_currentIndex + relativeOffset, m_currentIndex + OPCODE_LENGTH(op_jbelow))), condition);
+            LAST_OPCODE(op_jbelow);
+        }
+
+        case op_jbeloweq: {
+            unsigned relativeOffset = currentInstruction[3].u.operand;
+            Node* op1 = get(VirtualRegister(currentInstruction[1].u.operand));
+            Node* op2 = get(VirtualRegister(currentInstruction[2].u.operand));
+            Node* condition = addToGraph(CompareBelowEq, op1, op2);
+            addToGraph(Branch, OpInfo(branchData(m_currentIndex + relativeOffset, m_currentIndex + OPCODE_LENGTH(op_jbeloweq))), condition);
+            LAST_OPCODE(op_jbeloweq);
+        }
+
         case op_switch_imm: {
             SwitchData& data = *m_graph.m_switchData.add();
             data.kind = SwitchImm;
index 424a63b..a004949 100644 (file)
@@ -151,6 +151,8 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, Instruc
     case op_lesseq:
     case op_greater:
     case op_greatereq:
+    case op_below:
+    case op_beloweq:
     case op_eq:
     case op_eq_null:
     case op_stricteq:
@@ -192,6 +194,8 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, Instruc
     case op_jnlesseq:
     case op_jngreater:
     case op_jngreatereq:
+    case op_jbelow:
+    case op_jbeloweq:
     case op_loop_hint:
     case op_check_traps:
     case op_nop:
index 566490b..68ab243 100644 (file)
@@ -1486,6 +1486,11 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         }
         def(PureValue(node));
         return;
+
+    case CompareBelow:
+    case CompareBelowEq:
+        def(PureValue(node));
+        return;
         
     case CompareEq:
     case CompareLess:
index 58a75f5..b44d778 100644 (file)
@@ -143,6 +143,8 @@ bool doesGC(Graph& graph, Node* node)
     case CompareLessEq:
     case CompareGreater:
     case CompareGreaterEq:
+    case CompareBelow:
+    case CompareBelowEq:
     case CompareEq:
     case CompareStrictEq:
     case CompareEqPtr:
index 5ff98db..51410bc 100644 (file)
@@ -142,7 +142,6 @@ private:
                 node->setArithMode(Arith::CheckOverflow);
             else {
                 node->setArithMode(Arith::DoOverflow);
-                node->clearFlags(NodeMustGenerate);
                 node->setResult(enableInt52() ? NodeResultInt52 : NodeResultDouble);
             }
             break;
@@ -1597,6 +1596,13 @@ private:
             break;
         }
 
+        case CompareBelow:
+        case CompareBelowEq: {
+            fixEdge<Int32Use>(node->child1());
+            fixEdge<Int32Use>(node->child2());
+            break;
+        }
+
         case Phi:
         case Upsilon:
         case EntrySwitch:
index 3dbe171..b0bcdbf 100644 (file)
@@ -1140,6 +1140,7 @@ public:
                         relationshipForTrue = Relationship::safeCreate(
                             terminal->child1().node(), m_zero, Relationship::NotEqual, 0);
                     } else {
+                        // FIXME: Handle CompareBelow and CompareBelowEq.
                         Node* compare = terminal->child1().node();
                         switch (compare->op()) {
                         case CompareEq:
index 6dcec24..354ad37 100644 (file)
@@ -121,7 +121,7 @@ namespace JSC { namespace DFG {
     /* Bitwise operators call ToInt32 on their operands. */\
     macro(ValueToInt32, NodeResultInt32) \
     /* Used to box the result of URShift nodes (result has range 0..2^32-1). */\
-    macro(UInt32ToNumber, NodeResultNumber | NodeMustGenerate) \
+    macro(UInt32ToNumber, NodeResultNumber) \
     /* Converts booleans to numbers but passes everything else through. */\
     macro(BooleanToNumber, NodeResultJS) \
     \
@@ -286,6 +286,8 @@ namespace JSC { namespace DFG {
     macro(CompareLessEq, NodeResultBoolean | NodeMustGenerate) \
     macro(CompareGreater, NodeResultBoolean | NodeMustGenerate) \
     macro(CompareGreaterEq, NodeResultBoolean | NodeMustGenerate) \
+    macro(CompareBelow, NodeResultBoolean) \
+    macro(CompareBelowEq, NodeResultBoolean) \
     macro(CompareEq, NodeResultBoolean | NodeMustGenerate) \
     macro(CompareStrictEq, NodeResultBoolean) \
     macro(CompareEqPtr, NodeResultBoolean) \
index bb3a11f..d64154f 100644 (file)
@@ -818,6 +818,8 @@ private:
         case CompareLessEq:
         case CompareGreater:
         case CompareGreaterEq:
+        case CompareBelow:
+        case CompareBelowEq:
         case CompareEq:
         case CompareStrictEq:
         case CompareEqPtr:
index 2e54cb0..5c71d62 100644 (file)
@@ -266,6 +266,8 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case CompareLessEq:
     case CompareGreater:
     case CompareGreaterEq:
+    case CompareBelow:
+    case CompareBelowEq:
     case CompareEq:
     case CompareStrictEq:
     case CompareEqPtr:
index 30ab3a2..a6188d4 100644 (file)
@@ -5729,6 +5729,11 @@ bool SpeculativeJIT::compare(Node* node, MacroAssembler::RelationalCondition con
     return false;
 }
 
+void SpeculativeJIT::compileCompareUnsigned(Node* node, MacroAssembler::RelationalCondition condition)
+{
+    compileInt32Compare(node, condition);
+}
+
 bool SpeculativeJIT::compileStrictEq(Node* node)
 {
     // FIXME: Currently, we have op_jless, op_jgreater etc. But we don't have op_jeq, op_jstricteq etc.
index 25822ae..414d6a8 100644 (file)
@@ -2740,6 +2740,7 @@ public:
     }
     
     bool compare(Node*, MacroAssembler::RelationalCondition, MacroAssembler::DoubleCondition, S_JITOperation_EJJ);
+    void compileCompareUnsigned(Node*, MacroAssembler::RelationalCondition);
     bool compilePeepHoleBranch(Node*, MacroAssembler::RelationalCondition, MacroAssembler::DoubleCondition, S_JITOperation_EJJ);
     void compilePeepHoleInt32Branch(Node*, Node* branchNode, JITCompiler::RelationalCondition);
     void compilePeepHoleInt52Branch(Node*, Node* branchNode, JITCompiler::RelationalCondition);
index 43d0dac..a23b8a1 100644 (file)
@@ -2522,6 +2522,14 @@ void SpeculativeJIT::compile(Node* node)
             return;
         break;
 
+    case CompareBelow:
+        compileCompareUnsigned(node, JITCompiler::Below);
+        break;
+
+    case CompareBelowEq:
+        compileCompareUnsigned(node, JITCompiler::BelowOrEqual);
+        break;
+
     case CompareEq:
         if (compare(node, JITCompiler::Equal, JITCompiler::DoubleEqual, operationCompareEq))
             return;
index cf66e6a..84da999 100644 (file)
@@ -2664,6 +2664,14 @@ void SpeculativeJIT::compile(Node* node)
             return;
         break;
 
+    case CompareBelow:
+        compileCompareUnsigned(node, JITCompiler::Below);
+        break;
+
+    case CompareBelowEq:
+        compileCompareUnsigned(node, JITCompiler::BelowOrEqual);
+        break;
+
     case CompareEq:
         if (compare(node, JITCompiler::Equal, JITCompiler::DoubleEqual, operationCompareEq))
             return;
index c9ab2b4..64386b6 100644 (file)
@@ -267,6 +267,8 @@ public:
                 case CompareLessEq:
                 case CompareGreater:
                 case CompareGreaterEq:
+                case CompareBelow:
+                case CompareBelowEq:
                 case CompareEq:
                 case CompareStrictEq:
                 case StrCat:
index 1309e9e..6926e1e 100644 (file)
@@ -284,6 +284,8 @@ inline CapabilityLevel canCompile(Node* node)
     case CompareLessEq:
     case CompareGreater:
     case CompareGreaterEq:
+    case CompareBelow:
+    case CompareBelowEq:
     case CompareStrictEq:
     case DefineDataProperty:
     case DefineAccessorProperty:
index 2280215..3ba45af 100644 (file)
@@ -932,6 +932,12 @@ private:
         case CompareGreaterEq:
             compileCompareGreaterEq();
             break;
+        case CompareBelow:
+            compileCompareBelow();
+            break;
+        case CompareBelowEq:
+            compileCompareBelowEq();
+            break;
         case CompareEqPtr:
             compileCompareEqPtr();
             break;
@@ -6404,6 +6410,16 @@ private:
             operationCompareStringGreaterEq,
             operationCompareGreaterEq);
     }
+
+    void compileCompareBelow()
+    {
+        setBoolean(m_out.below(lowInt32(m_node->child1()), lowInt32(m_node->child2())));
+    }
+
+    void compileCompareBelowEq()
+    {
+        setBoolean(m_out.belowOrEqual(lowInt32(m_node->child1()), lowInt32(m_node->child2())));
+    }
     
     void compileLogicalNot()
     {
index 5ecf3a6..4a4bb8d 100644 (file)
@@ -301,6 +301,8 @@ void JIT::privateCompileMainPass()
         DEFINE_OP(op_get_scope)
         DEFINE_OP(op_eq)
         DEFINE_OP(op_eq_null)
+        DEFINE_OP(op_below)
+        DEFINE_OP(op_beloweq)
         DEFINE_OP(op_try_get_by_id)
         case op_get_array_length:
         case op_get_by_id_proto_load:
@@ -331,6 +333,8 @@ void JIT::privateCompileMainPass()
         DEFINE_OP(op_jnlesseq)
         DEFINE_OP(op_jngreater)
         DEFINE_OP(op_jngreatereq)
+        DEFINE_OP(op_jbelow)
+        DEFINE_OP(op_jbeloweq)
         DEFINE_OP(op_jtrue)
         DEFINE_OP(op_loop_hint)
         DEFINE_OP(op_check_traps)
index 7cbae35..6d086c4 100644 (file)
@@ -460,6 +460,8 @@ namespace JSC {
 #endif // USE(JSVALUE32_64)
 
         void emit_compareAndJump(OpcodeID, int op1, int op2, unsigned target, RelationalCondition);
+        void emit_compareUnsigned(int dst, int op1, int op2, RelationalCondition);
+        void emit_compareUnsignedAndJump(int op1, int op2, unsigned target, RelationalCondition);
         void emit_compareAndJumpSlow(int op1, int op2, unsigned target, DoubleCondition, size_t (JIT_OPERATION *operation)(ExecState*, EncodedJSValue, EncodedJSValue), bool invert, Vector<SlowCaseEntry>::iterator&);
         
         void assertStackPointerOffset();
@@ -499,6 +501,8 @@ namespace JSC {
         void emit_op_get_scope(Instruction*);
         void emit_op_eq(Instruction*);
         void emit_op_eq_null(Instruction*);
+        void emit_op_below(Instruction*);
+        void emit_op_beloweq(Instruction*);
         void emit_op_try_get_by_id(Instruction*);
         void emit_op_get_by_id(Instruction*);
         void emit_op_get_by_id_with_this(Instruction*);
@@ -529,6 +533,8 @@ namespace JSC {
         void emit_op_jnlesseq(Instruction*);
         void emit_op_jngreater(Instruction*);
         void emit_op_jngreatereq(Instruction*);
+        void emit_op_jbelow(Instruction*);
+        void emit_op_jbeloweq(Instruction*);
         void emit_op_jtrue(Instruction*);
         void emit_op_loop_hint(Instruction*);
         void emit_op_check_traps(Instruction*);
index 58fdb10..c1ca275 100644 (file)
@@ -197,6 +197,40 @@ void JIT::emitSlow_op_jngreatereq(Instruction* currentInstruction, Vector<SlowCa
     emit_compareAndJumpSlow(op1, op2, target, DoubleLessThanOrUnordered, operationCompareGreaterEq, true, iter);
 }
 
+void JIT::emit_op_below(Instruction* currentInstruction)
+{
+    int dst = currentInstruction[1].u.operand;
+    int op1 = currentInstruction[2].u.operand;
+    int op2 = currentInstruction[3].u.operand;
+    emit_compareUnsigned(dst, op1, op2, Below);
+}
+
+void JIT::emit_op_beloweq(Instruction* currentInstruction)
+{
+    int dst = currentInstruction[1].u.operand;
+    int op1 = currentInstruction[2].u.operand;
+    int op2 = currentInstruction[3].u.operand;
+    emit_compareUnsigned(dst, op1, op2, BelowOrEqual);
+}
+
+void JIT::emit_op_jbelow(Instruction* currentInstruction)
+{
+    int op1 = currentInstruction[1].u.operand;
+    int op2 = currentInstruction[2].u.operand;
+    unsigned target = currentInstruction[3].u.operand;
+
+    emit_compareUnsignedAndJump(op1, op2, target, Below);
+}
+
+void JIT::emit_op_jbeloweq(Instruction* currentInstruction)
+{
+    int op1 = currentInstruction[1].u.operand;
+    int op2 = currentInstruction[2].u.operand;
+    unsigned target = currentInstruction[3].u.operand;
+
+    emit_compareUnsignedAndJump(op1, op2, target, BelowOrEqual);
+}
+
 #if USE(JSVALUE64)
 
 void JIT::emit_op_unsigned(Instruction* currentInstruction)
@@ -264,6 +298,40 @@ void JIT::emit_compareAndJump(OpcodeID, int op1, int op2, unsigned target, Relat
     }
 }
 
+void JIT::emit_compareUnsignedAndJump(int op1, int op2, unsigned target, RelationalCondition condition)
+{
+    if (isOperandConstantInt(op2)) {
+        emitGetVirtualRegister(op1, regT0);
+        int32_t op2imm = getOperandConstantInt(op2);
+        addJump(branch32(condition, regT0, Imm32(op2imm)), target);
+    } else if (isOperandConstantInt(op1)) {
+        emitGetVirtualRegister(op2, regT1);
+        int32_t op1imm = getOperandConstantInt(op1);
+        addJump(branch32(commute(condition), regT1, Imm32(op1imm)), target);
+    } else {
+        emitGetVirtualRegisters(op1, regT0, op2, regT1);
+        addJump(branch32(condition, regT0, regT1), target);
+    }
+}
+
+void JIT::emit_compareUnsigned(int dst, int op1, int op2, RelationalCondition condition)
+{
+    if (isOperandConstantInt(op2)) {
+        emitGetVirtualRegister(op1, regT0);
+        int32_t op2imm = getOperandConstantInt(op2);
+        compare32(condition, regT0, Imm32(op2imm), regT0);
+    } else if (isOperandConstantInt(op1)) {
+        emitGetVirtualRegister(op2, regT0);
+        int32_t op1imm = getOperandConstantInt(op1);
+        compare32(commute(condition), regT0, Imm32(op1imm), regT0);
+    } else {
+        emitGetVirtualRegisters(op1, regT0, op2, regT1);
+        compare32(condition, regT0, regT1, regT0);
+    }
+    emitTagBool(regT0);
+    emitPutVirtualRegister(dst);
+}
+
 void JIT::emit_compareAndJumpSlow(int op1, int op2, unsigned target, DoubleCondition condition, size_t (JIT_OPERATION *operation)(ExecState*, EncodedJSValue, EncodedJSValue), bool invert, Vector<SlowCaseEntry>::iterator& iter)
 {
     COMPILE_ASSERT(OPCODE_LENGTH(op_jless) == OPCODE_LENGTH(op_jlesseq), OPCODE_LENGTH_op_jlesseq_equals_op_jless);
index d271d48..0e187c8 100644 (file)
@@ -92,6 +92,36 @@ void JIT::emit_compareAndJump(OpcodeID opcode, int op1, int op2, unsigned target
     end.link(this);
 }
 
+void JIT::emit_compareUnsignedAndJump(int op1, int op2, unsigned target, RelationalCondition condition)
+{
+    if (isOperandConstantInt(op1)) {
+        emitLoad(op2, regT3, regT2);
+        addJump(branch32(commute(condition), regT2, Imm32(getConstantOperand(op1).asInt32())), target);
+    } else if (isOperandConstantInt(op2)) {
+        emitLoad(op1, regT1, regT0);
+        addJump(branch32(condition, regT0, Imm32(getConstantOperand(op2).asInt32())), target);
+    } else {
+        emitLoad2(op1, regT1, regT0, op2, regT3, regT2);
+        addJump(branch32(condition, regT0, regT2), target);
+    }
+}
+
+
+void JIT::emit_compareUnsigned(int dst, int op1, int op2, RelationalCondition condition)
+{
+    if (isOperandConstantInt(op1)) {
+        emitLoad(op2, regT3, regT2);
+        compare32(commute(condition), regT2, Imm32(getConstantOperand(op1).asInt32()), regT0);
+    } else if (isOperandConstantInt(op2)) {
+        emitLoad(op1, regT1, regT0);
+        compare32(condition, regT0, Imm32(getConstantOperand(op2).asInt32()), regT0);
+    } else {
+        emitLoad2(op1, regT1, regT0, op2, regT3, regT2);
+        compare32(condition, regT0, regT2, regT0);
+    }
+    emitStoreBool(dst, regT0);
+}
+
 void JIT::emit_compareAndJumpSlow(int op1, int op2, unsigned target, DoubleCondition, size_t (JIT_OPERATION *operation)(ExecState*, EncodedJSValue, EncodedJSValue), bool invert, Vector<SlowCaseEntry>::iterator& iter)
 {
     if (isOperandConstantChar(op1) || isOperandConstantChar(op2)) {
index 5dde664..ab888d3 100644 (file)
@@ -1398,6 +1398,18 @@ _llint_op_greatereq:
     dispatch(constexpr op_greatereq_length)
 
 
+_llint_op_below:
+    traceExecution()
+    compareUnsigned(
+        macro (left, right, result) cib left, right, result end)
+
+
+_llint_op_beloweq:
+    traceExecution()
+    compareUnsigned(
+        macro (left, right, result) cibeq left, right, result end)
+
+
 _llint_op_mod:
     traceExecution()
     callOpcodeSlowPath(_slow_path_mod)
@@ -1577,6 +1589,18 @@ _llint_op_jngreatereq:
         _llint_slow_path_jngreatereq)
 
 
+_llint_op_jbelow:
+    traceExecution()
+    compareUnsignedJump(
+        macro (left, right, target) bib left, right, target end)
+
+
+_llint_op_jbeloweq:
+    traceExecution()
+    compareUnsignedJump(
+        macro (left, right, target) bibeq left, right, target end)
+
+
 _llint_op_loop_hint:
     traceExecution()
     checkSwitchToJITForLoop()
index 114a978..4a47582 100644 (file)
@@ -1796,6 +1796,32 @@ _llint_op_jneq_ptr:
     dispatch(constexpr op_jneq_ptr_length)
 
 
+macro compareUnsignedJump(integerCompare)
+    loadi 4[PC], t2
+    loadi 8[PC], t3
+    loadConstantOrVariable(t2, t0, t1)
+    loadConstantOrVariable2Reg(t3, t2, t3)
+    integerCompare(t1, t3, .jumpTarget)
+    dispatch(4)
+
+.jumpTarget:
+    dispatchBranch(12[PC])
+end
+
+
+macro compareUnsigned(integerCompareAndSet)
+    loadi 12[PC], t2
+    loadi 8[PC], t0
+    loadConstantOrVariable(t2, t3, t1)
+    loadConstantOrVariable2Reg(t0, t2, t0)
+    integerCompareAndSet(t0, t1, t0)
+    loadi 4[PC], t2
+    storei BooleanTag, TagOffset[cfr, t2, 8]
+    storei t0, PayloadOffset[cfr, t2, 8]
+    dispatch(4)
+end
+
+
 macro compare(integerCompare, doubleCompare, slowPath)
     loadi 4[PC], t2
     loadi 8[PC], t3
index 5d62987..85c77d2 100644 (file)
@@ -1813,6 +1813,33 @@ macro compare(integerCompare, doubleCompare, slowPath)
 end
 
 
+macro compareUnsignedJump(integerCompare)
+    loadisFromInstruction(1, t2)
+    loadisFromInstruction(2, t3)
+    loadConstantOrVariable(t2, t0)
+    loadConstantOrVariable(t3, t1)
+    integerCompare(t0, t1, .jumpTarget)
+    dispatch(4)
+
+.jumpTarget:
+    dispatchIntIndirect(3)
+end
+
+
+macro compareUnsigned(integerCompareAndSet)
+    traceExecution()
+    loadisFromInstruction(3, t0)
+    loadisFromInstruction(2, t2)
+    loadisFromInstruction(1, t3)
+    loadConstantOrVariable(t0, t1)
+    loadConstantOrVariable(t2, t0)
+    integerCompareAndSet(t0, t1, t0)
+    orq ValueFalse, t0
+    storeq t0, [cfr, t3, 8]
+    dispatch(4)
+end
+
+
 _llint_op_switch_imm:
     traceExecution()
     loadisFromInstruction(3, t2)
index 4110deb..2a6ddd2 100644 (file)
@@ -190,6 +190,7 @@ namespace JSC {
         virtual bool isNewTarget() const { return false; }
         virtual bool isImportMeta() const { return false; }
         virtual bool isBytecodeIntrinsicNode() const { return false; }
+        virtual bool isBinaryOpNode() const { return false; }
 
         virtual void emitBytecodeInConditionContext(BytecodeGenerator&, Label&, Label&, FallThroughMode);
 
@@ -1085,7 +1086,11 @@ namespace JSC {
         ExpressionNode* lhs() { return m_expr1; };
         ExpressionNode* rhs() { return m_expr2; };
 
+        bool isBinaryOpNode() const override { return true; }
+
     private:
+        enum class UInt32Result { UInt32, Constant, };
+
         void tryFoldToBranch(BytecodeGenerator&, TriState& branchCondition, ExpressionNode*& branchExpression);
         RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
 
@@ -1099,6 +1104,7 @@ namespace JSC {
         OpcodeID m_opcodeID;
     protected:
         bool m_rightHasAssignments;
+        bool m_shouldToUnsignedResult { true };
     };
 
     class PowNode : public BinaryOpNode {