Diff
Modified: trunk/JSTests/ChangeLog (223317 => 223318)
--- trunk/JSTests/ChangeLog 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/JSTests/ChangeLog 2017-10-14 15:35:48 UTC (rev 223318)
@@ -1,3 +1,31 @@
+2017-10-14 Yusuke Suzuki <utatane....@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....@gmail.com>
WebAssembly: Wasm functions should have either JSFunctionType or TypeOfShouldCallGetCallData
Added: trunk/JSTests/stress/uint32-comparison-jump.js (0 => 223318)
--- trunk/JSTests/stress/uint32-comparison-jump.js (rev 0)
+++ trunk/JSTests/stress/uint32-comparison-jump.js 2017-10-14 15:35:48 UTC (rev 223318)
@@ -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);
+}
+
Added: trunk/JSTests/stress/uint32-comparison.js (0 => 223318)
--- trunk/JSTests/stress/uint32-comparison.js (rev 0)
+++ trunk/JSTests/stress/uint32-comparison.js 2017-10-14 15:35:48 UTC (rev 223318)
@@ -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);
+ }
+}());
Modified: trunk/Source/_javascript_Core/ChangeLog (223317 => 223318)
--- trunk/Source/_javascript_Core/ChangeLog 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/ChangeLog 2017-10-14 15:35:48 UTC (rev 223318)
@@ -1,3 +1,135 @@
+2017-10-14 Yusuke Suzuki <utatane....@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....@gmail.com>
WebAssembly: Wasm functions should have either JSFunctionType or TypeOfShouldCallGetCallData
Modified: trunk/Source/_javascript_Core/bytecode/BytecodeDumper.cpp (223317 => 223318)
--- trunk/Source/_javascript_Core/bytecode/BytecodeDumper.cpp 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/bytecode/BytecodeDumper.cpp 2017-10-14 15:35:48 UTC (rev 223318)
@@ -360,6 +360,16 @@
}
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 @@
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,69 +1215,45 @@
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: {
printLocationAndOp(out, location, it, "loop_hint");
break;
Modified: trunk/Source/_javascript_Core/bytecode/BytecodeDumper.h (223317 => 223318)
--- trunk/Source/_javascript_Core/bytecode/BytecodeDumper.h 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/bytecode/BytecodeDumper.h 2017-10-14 15:35:48 UTC (rev 223318)
@@ -67,6 +67,7 @@
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&);
Modified: trunk/Source/_javascript_Core/bytecode/BytecodeList.json (223317 => 223318)
--- trunk/Source/_javascript_Core/bytecode/BytecodeList.json 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/bytecode/BytecodeList.json 2017-10-14 15:35:48 UTC (rev 223318)
@@ -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 },
@@ -115,6 +117,8 @@
{ "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 },
Modified: trunk/Source/_javascript_Core/bytecode/BytecodeUseDef.h (223317 => 223318)
--- trunk/Source/_javascript_Core/bytecode/BytecodeUseDef.h 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/bytecode/BytecodeUseDef.h 2017-10-14 15:35:48 UTC (rev 223318)
@@ -85,6 +85,8 @@
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 @@
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 @@
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 @@
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:
Modified: trunk/Source/_javascript_Core/bytecode/Opcode.h (223317 => 223318)
--- trunk/Source/_javascript_Core/bytecode/Opcode.h 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/bytecode/Opcode.h 2017-10-14 15:35:48 UTC (rev 223318)
@@ -151,6 +151,8 @@
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:
Modified: trunk/Source/_javascript_Core/bytecode/PreciseJumpTargetsInlines.h (223317 => 223318)
--- trunk/Source/_javascript_Core/bytecode/PreciseJumpTargetsInlines.h 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/bytecode/PreciseJumpTargetsInlines.h 2017-10-14 15:35:48 UTC (rev 223318)
@@ -55,6 +55,8 @@
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:
Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp (223317 => 223318)
--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp 2017-10-14 15:35:48 UTC (rev 223318)
@@ -1366,7 +1366,7 @@
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 @@
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;
+ return false;
+ };
- retrieveLastBinaryOp(dstIndex, src1Index, src2Index);
-
- if (cond->index() == dstIndex && cond->isTemporary() && !cond->refCount()) {
- rewindBinaryOp();
-
- 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::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 @@
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;
+ return false;
+ };
- retrieveLastBinaryOp(dstIndex, src1Index, src2Index);
-
- if (cond->index() == dstIndex && cond->isTemporary() && !cond->refCount()) {
- rewindBinaryOp();
-
- 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;
Modified: trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp (223317 => 223318)
--- trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp 2017-10-14 15:35:48 UTC (rev 223318)
@@ -1988,6 +1988,64 @@
{
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 @@
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;
}
Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (223317 => 223318)
--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2017-10-14 15:35:48 UTC (rev 223318)
@@ -1407,7 +1407,48 @@
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:
Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (223317 => 223318)
--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2017-10-14 15:35:48 UTC (rev 223318)
@@ -4634,6 +4634,20 @@
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 @@
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 = ""
data.kind = SwitchImm;
Modified: trunk/Source/_javascript_Core/dfg/DFGCapabilities.cpp (223317 => 223318)
--- trunk/Source/_javascript_Core/dfg/DFGCapabilities.cpp 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/dfg/DFGCapabilities.cpp 2017-10-14 15:35:48 UTC (rev 223318)
@@ -151,6 +151,8 @@
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 @@
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:
Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (223317 => 223318)
--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2017-10-14 15:35:48 UTC (rev 223318)
@@ -1486,6 +1486,11 @@
}
def(PureValue(node));
return;
+
+ case CompareBelow:
+ case CompareBelowEq:
+ def(PureValue(node));
+ return;
case CompareEq:
case CompareLess:
Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (223317 => 223318)
--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2017-10-14 15:35:48 UTC (rev 223318)
@@ -143,6 +143,8 @@
case CompareLessEq:
case CompareGreater:
case CompareGreaterEq:
+ case CompareBelow:
+ case CompareBelowEq:
case CompareEq:
case CompareStrictEq:
case CompareEqPtr:
Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (223317 => 223318)
--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2017-10-14 15:35:48 UTC (rev 223318)
@@ -142,7 +142,6 @@
node->setArithMode(Arith::CheckOverflow);
else {
node->setArithMode(Arith::DoOverflow);
- node->clearFlags(NodeMustGenerate);
node->setResult(enableInt52() ? NodeResultInt52 : NodeResultDouble);
}
break;
@@ -1597,6 +1596,13 @@
break;
}
+ case CompareBelow:
+ case CompareBelowEq: {
+ fixEdge<Int32Use>(node->child1());
+ fixEdge<Int32Use>(node->child2());
+ break;
+ }
+
case Phi:
case Upsilon:
case EntrySwitch:
Modified: trunk/Source/_javascript_Core/dfg/DFGIntegerRangeOptimizationPhase.cpp (223317 => 223318)
--- trunk/Source/_javascript_Core/dfg/DFGIntegerRangeOptimizationPhase.cpp 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/dfg/DFGIntegerRangeOptimizationPhase.cpp 2017-10-14 15:35:48 UTC (rev 223318)
@@ -1140,6 +1140,7 @@
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:
Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (223317 => 223318)
--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2017-10-14 15:35:48 UTC (rev 223318)
@@ -121,7 +121,7 @@
/* 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 @@
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) \
Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (223317 => 223318)
--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2017-10-14 15:35:48 UTC (rev 223318)
@@ -818,6 +818,8 @@
case CompareLessEq:
case CompareGreater:
case CompareGreaterEq:
+ case CompareBelow:
+ case CompareBelowEq:
case CompareEq:
case CompareStrictEq:
case CompareEqPtr:
Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (223317 => 223318)
--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2017-10-14 15:35:48 UTC (rev 223318)
@@ -266,6 +266,8 @@
case CompareLessEq:
case CompareGreater:
case CompareGreaterEq:
+ case CompareBelow:
+ case CompareBelowEq:
case CompareEq:
case CompareStrictEq:
case CompareEqPtr:
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (223317 => 223318)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2017-10-14 15:35:48 UTC (rev 223318)
@@ -5729,6 +5729,11 @@
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.
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (223317 => 223318)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h 2017-10-14 15:35:48 UTC (rev 223318)
@@ -2740,6 +2740,7 @@
}
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);
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (223317 => 223318)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2017-10-14 15:35:48 UTC (rev 223318)
@@ -2522,6 +2522,14 @@
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;
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (223317 => 223318)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2017-10-14 15:35:48 UTC (rev 223318)
@@ -2664,6 +2664,14 @@
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;
Modified: trunk/Source/_javascript_Core/dfg/DFGValidate.cpp (223317 => 223318)
--- trunk/Source/_javascript_Core/dfg/DFGValidate.cpp 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/dfg/DFGValidate.cpp 2017-10-14 15:35:48 UTC (rev 223318)
@@ -267,6 +267,8 @@
case CompareLessEq:
case CompareGreater:
case CompareGreaterEq:
+ case CompareBelow:
+ case CompareBelowEq:
case CompareEq:
case CompareStrictEq:
case StrCat:
Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (223317 => 223318)
--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2017-10-14 15:35:48 UTC (rev 223318)
@@ -284,6 +284,8 @@
case CompareLessEq:
case CompareGreater:
case CompareGreaterEq:
+ case CompareBelow:
+ case CompareBelowEq:
case CompareStrictEq:
case DefineDataProperty:
case DefineAccessorProperty:
Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (223317 => 223318)
--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2017-10-14 15:35:48 UTC (rev 223318)
@@ -932,6 +932,12 @@
case CompareGreaterEq:
compileCompareGreaterEq();
break;
+ case CompareBelow:
+ compileCompareBelow();
+ break;
+ case CompareBelowEq:
+ compileCompareBelowEq();
+ break;
case CompareEqPtr:
compileCompareEqPtr();
break;
@@ -6404,6 +6410,16 @@
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()
{
Modified: trunk/Source/_javascript_Core/jit/JIT.cpp (223317 => 223318)
--- trunk/Source/_javascript_Core/jit/JIT.cpp 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/jit/JIT.cpp 2017-10-14 15:35:48 UTC (rev 223318)
@@ -301,6 +301,8 @@
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 @@
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)
Modified: trunk/Source/_javascript_Core/jit/JIT.h (223317 => 223318)
--- trunk/Source/_javascript_Core/jit/JIT.h 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/jit/JIT.h 2017-10-14 15:35:48 UTC (rev 223318)
@@ -460,6 +460,8 @@
#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 @@
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 @@
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*);
Modified: trunk/Source/_javascript_Core/jit/JITArithmetic.cpp (223317 => 223318)
--- trunk/Source/_javascript_Core/jit/JITArithmetic.cpp 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/jit/JITArithmetic.cpp 2017-10-14 15:35:48 UTC (rev 223318)
@@ -197,6 +197,40 @@
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_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);
Modified: trunk/Source/_javascript_Core/jit/JITArithmetic32_64.cpp (223317 => 223318)
--- trunk/Source/_javascript_Core/jit/JITArithmetic32_64.cpp 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/jit/JITArithmetic32_64.cpp 2017-10-14 15:35:48 UTC (rev 223318)
@@ -92,6 +92,36 @@
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)) {
Modified: trunk/Source/_javascript_Core/llint/LowLevelInterpreter.asm (223317 => 223318)
--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter.asm 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter.asm 2017-10-14 15:35:48 UTC (rev 223318)
@@ -1398,6 +1398,18 @@
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_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()
Modified: trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm (223317 => 223318)
--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm 2017-10-14 15:35:48 UTC (rev 223318)
@@ -1796,6 +1796,32 @@
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
Modified: trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm (223317 => 223318)
--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm 2017-10-14 15:35:48 UTC (rev 223318)
@@ -1813,6 +1813,33 @@
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)
Modified: trunk/Source/_javascript_Core/parser/Nodes.h (223317 => 223318)
--- trunk/Source/_javascript_Core/parser/Nodes.h 2017-10-14 15:07:44 UTC (rev 223317)
+++ trunk/Source/_javascript_Core/parser/Nodes.h 2017-10-14 15:35:48 UTC (rev 223318)
@@ -190,6 +190,7 @@
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 @@
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 @@
OpcodeID m_opcodeID;
protected:
bool m_rightHasAssignments;
+ bool m_shouldToUnsignedResult { true };
};
class PowNode : public BinaryOpNode {