[JSC] Add SameValue DFG node
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 2 May 2018 05:51:33 +0000 (05:51 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 2 May 2018 05:51:33 +0000 (05:51 +0000)
https://bugs.webkit.org/show_bug.cgi?id=185065

Reviewed by Saam Barati.

JSTests:

* microbenchmarks/object-is.js: Added.
(incognito):
(sameValue):
(test1):
(test2):
(test3):
(test4):
(test5):
(test6):
* stress/object-is.js: Added.
(shouldBe):
(is1):
(is2):
(is3):
(is4):
(is5):
(is6):
(is7):
(is8):
(is9):
(is10):
(is11):
(is12):
(is13):
(is14):
(is15):

Source/JavaScriptCore:

This patch adds Object.is handling in DFG and FTL. Object.is is converted to SameValue DFG node.
And DFG fixup phase attempts to convert SameValue node to CompareStrictEq with type filter edges
if possible. Since SameValue(Untyped, Untyped) and SameValue(Double, Double) have different semantics
from CompareStrictEq, we do not convert SameValue to CompareStrictEq for them. DFG and FTL have
implementations for these SameValue nodes.

This old MacroAssemblerX86Common::compareDouble was dead code since the derived class, "MacroAssembler"
has a generalized compareDouble, which just uses branchDouble. Since this was not used, this function
was broken. This patch fixes issues and move compareDouble to MacroAssemblerX86Common, and remove a
generalized compareDouble for x86 arch to use this specialized efficient version instead. The fixes are
correctly using set32 to zero-extending the result, and setting the initial value of `dest` register
correctly for DoubleEqual and DoubleNotEqualOrUnordered cases.

Added microbenchmark shows performance improvement.

    object-is           651.0053+-38.8204    ^    241.3467+-15.8753       ^ definitely 2.6974x faster

* assembler/MacroAssembler.h:
* assembler/MacroAssemblerX86Common.h:
(JSC::MacroAssemblerX86Common::compareDouble):
* assembler/MacroAssemblerX86_64.h:
(JSC::MacroAssemblerX86_64::compareDouble): Deleted.
* assembler/testmasm.cpp:
(JSC::doubleOperands):
(JSC::testCompareDouble):
(JSC::run):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::fixupCompareStrictEqAndSameValue):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileSameValue):
* 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::compileSameValue):
* runtime/Intrinsic.cpp:
(JSC::intrinsicName):
* runtime/Intrinsic.h:
* runtime/ObjectConstructor.cpp:

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

29 files changed:
JSTests/ChangeLog
JSTests/microbenchmarks/object-is.js [new file with mode: 0644]
JSTests/stress/object-is.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/assembler/MacroAssembler.h
Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h
Source/JavaScriptCore/assembler/MacroAssemblerX86_64.h
Source/JavaScriptCore/assembler/testmasm.cpp
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.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/runtime/Intrinsic.cpp
Source/JavaScriptCore/runtime/Intrinsic.h
Source/JavaScriptCore/runtime/ObjectConstructor.cpp

index 89e551d4294bfa7a197c0c210c8fce3475c74663..b68a7185618838fac0c7326f5679684b030c025f 100644 (file)
@@ -1,3 +1,37 @@
+2018-05-01  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Add SameValue DFG node
+        https://bugs.webkit.org/show_bug.cgi?id=185065
+
+        Reviewed by Saam Barati.
+
+        * microbenchmarks/object-is.js: Added.
+        (incognito):
+        (sameValue):
+        (test1):
+        (test2):
+        (test3):
+        (test4):
+        (test5):
+        (test6):
+        * stress/object-is.js: Added.
+        (shouldBe):
+        (is1):
+        (is2):
+        (is3):
+        (is4):
+        (is5):
+        (is6):
+        (is7):
+        (is8):
+        (is9):
+        (is10):
+        (is11):
+        (is12):
+        (is13):
+        (is14):
+        (is15):
+
 2018-05-01  Robin Morisset  <rmorisset@apple.com>
 
         Correctly detect string overflow when using the 'Function' constructor
diff --git a/JSTests/microbenchmarks/object-is.js b/JSTests/microbenchmarks/object-is.js
new file mode 100644 (file)
index 0000000..8c4d94f
--- /dev/null
@@ -0,0 +1,82 @@
+function incognito(value) {
+    var array = [];
+    array.push(value);
+    array.push("ignore me");
+    array.push(value);
+    array.push({ ignore: "me" });
+    return array[((Math.random() * 2) | 0) * 2];
+}
+
+// cached Object.is
+var objectIs = Object.is;
+
+// pure JS version of Object.is
+function sameValue(a, b) {
+    return (a === b) ?
+        (a !== 0 || (1 / a === 1 / b)) :
+        (a !== a && b !== b);
+}
+
+var testFiveA = incognito("back5");
+var testFiveB = incognito("2back5".substring(1));
+var testPi = incognito("PI");
+var testNaN = incognito(NaN);
+var testNaN_2 = incognito(NaN);
+
+var result;
+
+function test1()
+{
+    return testFiveA === testFiveB;
+}
+noInline(test1);
+
+function test2()
+{
+    return Object.is(testFiveA, testFiveB);
+}
+noInline(test2);
+
+function test3()
+{
+    return sameValue(testFiveA, testFiveB);
+}
+noInline(test3);
+
+function test4()
+{
+    return testFiveA === testPi;
+}
+noInline(test4);
+
+function test5()
+{
+    return Object.is(testFiveA, testPi);
+}
+noInline(test5);
+
+function test6()
+{
+    return sameValue(testFiveA, testPi);
+}
+noInline(test6);
+
+var verbose = false;
+var tests = [
+//     test1,
+    test2,
+//     test3,
+//     test4,
+    test5,
+//     test6,
+];
+for (let test of tests) {
+    if (verbose)
+        var time = Date.now();
+
+    for (let i = 0; i < 2e7; ++i)
+        test();
+
+    if (verbose)
+        print(Date.now() - time);
+}
diff --git a/JSTests/stress/object-is.js b/JSTests/stress/object-is.js
new file mode 100644 (file)
index 0000000..4e74e35
--- /dev/null
@@ -0,0 +1,72 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function is1(a, b) { return Object.is(a, b); }
+noInline(is1);
+function is2(a, b) { return Object.is(a, b); }
+noInline(is2);
+function is3(a, b) { return Object.is(a, b); }
+noInline(is3);
+function is4(a, b) { return Object.is(a, b); }
+noInline(is4);
+function is5(a, b) { return Object.is(a, b); }
+noInline(is5);
+function is6(a, b) { return Object.is(a, b); }
+noInline(is6);
+function is7(a, b) { return Object.is(a, b); }
+noInline(is7);
+function is8(a, b) { return Object.is(a, b); }
+noInline(is8);
+function is9(a, b) { return Object.is(a, b); }
+noInline(is9);
+function is10(a, b) { return Object.is(a, b); }
+noInline(is10);
+function is11(a, b) { return Object.is(a, b); }
+noInline(is11);
+function is12(a, b) { return Object.is(a, b); }
+noInline(is12);
+function is13(a, b) { return Object.is(a, b); }
+noInline(is13);
+function is14(a, b) { return Object.is(a, b); }
+noInline(is14);
+function is15(a, b) { return Object.is(a, b); }
+noInline(is15);
+
+for (var i = 0; i < 1e5; ++i) {
+    shouldBe(Object.is(NaN, NaN), true);
+    shouldBe(Object.is(null, null), true);
+    shouldBe(Object.is(null), false);
+    shouldBe(Object.is(undefined, undefined), true);
+    shouldBe(Object.is(true, true), true);
+    shouldBe(Object.is(false, false), true);
+    shouldBe(Object.is('abc', 'abc'), true);
+    shouldBe(Object.is(Infinity, Infinity), true);
+    shouldBe(Object.is(0, 0), true);
+    shouldBe(Object.is(-0, -0), true);
+    shouldBe(Object.is(0, -0), false);
+    shouldBe(Object.is(-0, 0), false);
+    var obj = {};
+    shouldBe(Object.is(obj, obj), true);
+    var arr = [];
+    shouldBe(Object.is(arr, arr), true);
+    var sym = Symbol();
+    shouldBe(Object.is(sym, sym), true);
+
+    shouldBe(is1(NaN, NaN), true);
+    shouldBe(is2(null, null), true);
+    shouldBe(is3(null), false);
+    shouldBe(is4(undefined, undefined), true);
+    shouldBe(is5(true, true), true);
+    shouldBe(is6(false, false), true);
+    shouldBe(is7('abc', 'abc'), true);
+    shouldBe(is8(Infinity, Infinity), true);
+    shouldBe(is9(0, 0), true);
+    shouldBe(is10(-0, -0), true);
+    shouldBe(is11(0, -0), false);
+    shouldBe(is12(-0, 0), false);
+    shouldBe(is13(obj, obj), true);
+    shouldBe(is14(arr, arr), true);
+    shouldBe(is15(sym, sym), true);
+}
index 433e5eaa5909e64f896197040bc696d98dcaf52f..4a1162741a4ea0721993ac2a4fcb300b9910b63e 100644 (file)
@@ -1,3 +1,73 @@
+2018-05-01  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Add SameValue DFG node
+        https://bugs.webkit.org/show_bug.cgi?id=185065
+
+        Reviewed by Saam Barati.
+
+        This patch adds Object.is handling in DFG and FTL. Object.is is converted to SameValue DFG node.
+        And DFG fixup phase attempts to convert SameValue node to CompareStrictEq with type filter edges
+        if possible. Since SameValue(Untyped, Untyped) and SameValue(Double, Double) have different semantics
+        from CompareStrictEq, we do not convert SameValue to CompareStrictEq for them. DFG and FTL have
+        implementations for these SameValue nodes.
+
+        This old MacroAssemblerX86Common::compareDouble was dead code since the derived class, "MacroAssembler"
+        has a generalized compareDouble, which just uses branchDouble. Since this was not used, this function
+        was broken. This patch fixes issues and move compareDouble to MacroAssemblerX86Common, and remove a
+        generalized compareDouble for x86 arch to use this specialized efficient version instead. The fixes are
+        correctly using set32 to zero-extending the result, and setting the initial value of `dest` register
+        correctly for DoubleEqual and DoubleNotEqualOrUnordered cases.
+
+        Added microbenchmark shows performance improvement.
+
+            object-is           651.0053+-38.8204    ^    241.3467+-15.8753       ^ definitely 2.6974x faster
+
+        * assembler/MacroAssembler.h:
+        * assembler/MacroAssemblerX86Common.h:
+        (JSC::MacroAssemblerX86Common::compareDouble):
+        * assembler/MacroAssemblerX86_64.h:
+        (JSC::MacroAssemblerX86_64::compareDouble): Deleted.
+        * assembler/testmasm.cpp:
+        (JSC::doubleOperands):
+        (JSC::testCompareDouble):
+        (JSC::run):
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGConstantFoldingPhase.cpp:
+        (JSC::DFG::ConstantFoldingPhase::foldConstants):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        (JSC::DFG::FixupPhase::fixupCompareStrictEqAndSameValue):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileSameValue):
+        * 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::compileSameValue):
+        * runtime/Intrinsic.cpp:
+        (JSC::intrinsicName):
+        * runtime/Intrinsic.h:
+        * runtime/ObjectConstructor.cpp:
+
 2018-04-30  Filip Pizlo  <fpizlo@apple.com>
 
         B3::demoteValues should be able to handle patchpoint terminals
index d25da3c1d74df60d0cc38899c30dd528c72929cd..8679261fb8eb88898c4d06ee1cd789c19a874cfe 100644 (file)
@@ -1459,9 +1459,10 @@ public:
 
 #endif // !CPU(X86_64)
 
-#if ENABLE(B3_JIT)
     // We should implement this the right way eventually, but for now, it's fine because it arises so
     // infrequently.
+
+#if !CPU(X86) && !CPU(X86_64)
     void compareDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right, RegisterID dest)
     {
         move(TrustedImm32(0), dest);
@@ -1469,6 +1470,9 @@ public:
         move(TrustedImm32(1), dest);
         falseCase.link(this);
     }
+#endif
+
+#if ENABLE(B3_JIT)
     void compareFloat(DoubleCondition cond, FPRegisterID left, FPRegisterID right, RegisterID dest)
     {
         move(TrustedImm32(0), dest);
index 3b2233a90f77632debaeeb2e4f68c72f7c0609e4..1f43f407ef1d7d3cf388d237463d5777ec26cfb1 100644 (file)
@@ -2014,6 +2014,48 @@ public:
         return jumpAfterFloatingPointCompare(cond, left, right);
     }
 
+    void compareDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right, RegisterID dest)
+    {
+        if (cond & DoubleConditionBitSpecial) {
+            ASSERT(!(cond & DoubleConditionBitInvert));
+            if (cond == DoubleEqual) {
+                if (left == right) {
+                    m_assembler.ucomisd_rr(right, left);
+                    set32(X86Assembler::ConditionNP, dest);
+                    return;
+                }
+
+                move(TrustedImm32(0), dest);
+                m_assembler.ucomisd_rr(right, left);
+                Jump isUnordered = m_assembler.jp();
+                set32(X86Assembler::ConditionE, dest);
+                isUnordered.link(this);
+                return;
+            }
+            if (cond == DoubleNotEqualOrUnordered) {
+                if (left == right) {
+                    m_assembler.ucomisd_rr(right, left);
+                    set32(X86Assembler::ConditionP, dest);
+                    return;
+                }
+
+                move(TrustedImm32(1), dest);
+                m_assembler.ucomisd_rr(right, left);
+                Jump isUnordered = m_assembler.jp();
+                set32(X86Assembler::ConditionNE, dest);
+                isUnordered.link(this);
+                return;
+            }
+            return;
+        }
+
+        if (cond & DoubleConditionBitInvert)
+            m_assembler.ucomisd_rr(left, right);
+        else
+            m_assembler.ucomisd_rr(right, left);
+        set32(static_cast<X86Assembler::Condition>(cond & ~DoubleConditionBits), dest);
+    }
+
     // Truncates 'src' to an integer, and places the resulting 'dest'.
     // If the result is not representable as a 32 bit value, branch.
     // May also branch for some values that are representable in 32 bits
index d4428cc53d0529a5c1a27b93d2e1c13a66a13790..d550a88a1f89b0fd49f4d7d34a5409bd9ad33db2 100644 (file)
@@ -983,40 +983,6 @@ public:
         set32(x86Condition(cond), dest);
     }
 
-    void compareDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right, RegisterID dest)
-    {
-        if (cond & DoubleConditionBitInvert)
-            m_assembler.ucomisd_rr(left, right);
-        else
-            m_assembler.ucomisd_rr(right, left);
-
-        if (cond == DoubleEqual) {
-            if (left == right) {
-                m_assembler.setnp_r(dest);
-                return;
-            }
-
-            Jump isUnordered(m_assembler.jp());
-            m_assembler.sete_r(dest);
-            isUnordered.link(this);
-            return;
-        }
-
-        if (cond == DoubleNotEqualOrUnordered) {
-            if (left == right) {
-                m_assembler.setp_r(dest);
-                return;
-            }
-
-            m_assembler.setp_r(dest);
-            m_assembler.setne_r(dest);
-            return;
-        }
-
-        ASSERT(!(cond & DoubleConditionBitSpecial));
-        m_assembler.setCC_r(static_cast<X86Assembler::Condition>(cond & ~DoubleConditionBits), dest);
-    }
-
     Jump branch64(RelationalCondition cond, RegisterID left, RegisterID right)
     {
         m_assembler.cmpq_rr(right, left);
index f18c412154492285c44b50f5767d812cd91f4eb4..e4fe153baff233b393895e5d708eb1221106d84c 100644 (file)
@@ -227,6 +227,67 @@ void testBranchTruncateDoubleToInt32(double val, int32_t expected)
 }
 
 
+static Vector<double> doubleOperands()
+{
+    return Vector<double> {
+        0,
+        -0,
+        1,
+        -1,
+        42,
+        -42,
+        std::numeric_limits<double>::max(),
+        std::numeric_limits<double>::min(),
+        std::numeric_limits<double>::lowest(),
+        std::numeric_limits<double>::quiet_NaN(),
+        std::numeric_limits<double>::infinity(),
+        -std::numeric_limits<double>::infinity(),
+    };
+}
+
+
+void testCompareDouble(MacroAssembler::DoubleCondition condition)
+{
+    double arg1 = 0;
+    double arg2 = 0;
+
+    auto compareDouble = compile([&, condition] (CCallHelpers& jit) {
+        jit.emitFunctionPrologue();
+
+        jit.loadDouble(CCallHelpers::TrustedImmPtr(&arg1), FPRInfo::fpRegT0);
+        jit.loadDouble(CCallHelpers::TrustedImmPtr(&arg2), FPRInfo::fpRegT1);
+        jit.move(CCallHelpers::TrustedImm32(-1), GPRInfo::returnValueGPR);
+        jit.compareDouble(condition, FPRInfo::fpRegT0, FPRInfo::fpRegT1, GPRInfo::returnValueGPR);
+
+        jit.emitFunctionEpilogue();
+        jit.ret();
+    });
+
+    auto compareDoubleGeneric = compile([&, condition] (CCallHelpers& jit) {
+        jit.emitFunctionPrologue();
+
+        jit.loadDouble(CCallHelpers::TrustedImmPtr(&arg1), FPRInfo::fpRegT0);
+        jit.loadDouble(CCallHelpers::TrustedImmPtr(&arg2), FPRInfo::fpRegT1);
+        jit.move(CCallHelpers::TrustedImm32(1), GPRInfo::returnValueGPR);
+        auto jump = jit.branchDouble(condition, FPRInfo::fpRegT0, FPRInfo::fpRegT1);
+        jit.move(CCallHelpers::TrustedImm32(0), GPRInfo::returnValueGPR);
+        jump.link(&jit);
+
+        jit.emitFunctionEpilogue();
+        jit.ret();
+    });
+
+    auto operands = doubleOperands();
+    for (auto a : operands) {
+        for (auto b : operands) {
+            arg1 = a;
+            arg2 = b;
+            CHECK_EQ(invoke<int>(compareDouble), invoke<int>(compareDoubleGeneric));
+        }
+    }
+}
+
+
 #if ENABLE(MASM_PROBE)
 void testProbeReadsArgumentRegisters()
 {
@@ -787,6 +848,19 @@ void run(const char* filter)
     // reset to check a conversion result
     RUN(testBranchTruncateDoubleToInt32(123, 123));
 
+    RUN(testCompareDouble(MacroAssembler::DoubleEqual));
+    RUN(testCompareDouble(MacroAssembler::DoubleNotEqual));
+    RUN(testCompareDouble(MacroAssembler::DoubleGreaterThan));
+    RUN(testCompareDouble(MacroAssembler::DoubleGreaterThanOrEqual));
+    RUN(testCompareDouble(MacroAssembler::DoubleLessThan));
+    RUN(testCompareDouble(MacroAssembler::DoubleLessThanOrEqual));
+    RUN(testCompareDouble(MacroAssembler::DoubleEqualOrUnordered));
+    RUN(testCompareDouble(MacroAssembler::DoubleNotEqualOrUnordered));
+    RUN(testCompareDouble(MacroAssembler::DoubleGreaterThanOrUnordered));
+    RUN(testCompareDouble(MacroAssembler::DoubleGreaterThanOrEqualOrUnordered));
+    RUN(testCompareDouble(MacroAssembler::DoubleLessThanOrUnordered));
+    RUN(testCompareDouble(MacroAssembler::DoubleLessThanOrEqualOrUnordered));
+
 #if ENABLE(MASM_PROBE)
     RUN(testProbeReadsArgumentRegisters());
     RUN(testProbeWritesArgumentRegisters());
index c6c98dbaea03a637376fe2244a0726d8c8f0a815..ed6b32c5e673a1748df294654cb6a224633e97e6 100644 (file)
@@ -1673,7 +1673,8 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         break;
     }
             
-    case CompareStrictEq: {
+    case CompareStrictEq:
+    case SameValue: {
         Node* leftNode = node->child1().node();
         Node* rightNode = node->child2().node();
         JSValue left = forNode(leftNode).value();
@@ -1689,7 +1690,10 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                     break;
                 }
             } else {
-                setConstant(node, jsBoolean(JSValue::strictEqual(0, left, right)));
+                if (node->op() == CompareStrictEq)
+                    setConstant(node, jsBoolean(JSValue::strictEqual(nullptr, left, right)));
+                else
+                    setConstant(node, jsBoolean(sameValue(nullptr, left, right)));
                 break;
             }
         }
index 1cb947a22a673b8c07a6c01ece10c3c0501a4076..4f3ede1da1fb7f16f551f2348888da3893e6c5a4 100644 (file)
@@ -2648,6 +2648,15 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrin
         return true;
     }
 
+    case ObjectIsIntrinsic: {
+        if (argumentCountIncludingThis < 3)
+            return false;
+
+        insertChecks();
+        set(VirtualRegister(resultOperand), addToGraph(SameValue, get(virtualRegisterForArgument(1, registerOffset)), get(virtualRegisterForArgument(2, registerOffset))));
+        return true;
+    }
+
     case ReflectGetPrototypeOfIntrinsic: {
         if (argumentCountIncludingThis != 2)
             return false;
index 88343530d43d3e5580f1cacf0a3227f5d055b88a..ceeced85d9e7766c0da886a8d43aed336da9370e 100644 (file)
@@ -165,6 +165,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case GetGlobalObject:
     case StringCharCodeAt:
     case CompareStrictEq:
+    case SameValue:
     case IsEmpty:
     case IsUndefined:
     case IsBoolean:
index cee65623431770f4bf402ada0c877358f800b67d..e9a1b54a48024bce6190a115fcfd2888bb27b6cf 100644 (file)
@@ -140,7 +140,8 @@ private:
                 break;
             }
 
-            case CompareStrictEq: {
+            case CompareStrictEq:
+            case SameValue: {
                 if (node->isBinaryUseKind(UntypedUse)) {
                     JSValue child1Constant = m_state.forNode(node->child1().node()).value();
                     JSValue child2Constant = m_state.forNode(node->child2().node()).value();
index 8287fac07c4a53aeb5321e8fbbf55488eb83fa82..974d971366ffd8b79786fbb26ded3ac25b687fda 100644 (file)
@@ -154,6 +154,7 @@ bool doesGC(Graph& graph, Node* node)
     case CompareEq:
     case CompareStrictEq:
     case CompareEqPtr:
+    case SameValue:
     case Call:
     case DirectCall:
     case TailCallInlinedCaller:
index dd95388cfaf2d816cd7f2d9abffbfaf060ef490b..0d6bfef982f28df18d402453050dee07d8f8f2c0 100644 (file)
@@ -589,106 +589,12 @@ private:
             break;
         }
             
-        case CompareStrictEq: {
-            if (Node::shouldSpeculateBoolean(node->child1().node(), node->child2().node())) {
-                fixEdge<BooleanUse>(node->child1());
-                fixEdge<BooleanUse>(node->child2());
-                break;
-            }
-            if (Node::shouldSpeculateInt32(node->child1().node(), node->child2().node())) {
-                fixEdge<Int32Use>(node->child1());
-                fixEdge<Int32Use>(node->child2());
-                break;
-            }
-            if (enableInt52()
-                && Node::shouldSpeculateAnyInt(node->child1().node(), node->child2().node())) {
-                fixEdge<Int52RepUse>(node->child1());
-                fixEdge<Int52RepUse>(node->child2());
-                break;
-            }
-            if (Node::shouldSpeculateNumber(node->child1().node(), node->child2().node())) {
-                fixEdge<DoubleRepUse>(node->child1());
-                fixEdge<DoubleRepUse>(node->child2());
-                break;
-            }
-            if (Node::shouldSpeculateSymbol(node->child1().node(), node->child2().node())) {
-                fixEdge<SymbolUse>(node->child1());
-                fixEdge<SymbolUse>(node->child2());
-                break;
-            }
-            if (Node::shouldSpeculateBigInt(node->child1().node(), node->child2().node())) {
-                fixEdge<BigIntUse>(node->child1());
-                fixEdge<BigIntUse>(node->child2());
-                break;
-            }
-            if (node->child1()->shouldSpeculateStringIdent() && node->child2()->shouldSpeculateStringIdent()) {
-                fixEdge<StringIdentUse>(node->child1());
-                fixEdge<StringIdentUse>(node->child2());
-                break;
-            }
-            if (node->child1()->shouldSpeculateString() && node->child2()->shouldSpeculateString() && ((GPRInfo::numberOfRegisters >= 7) || isFTL(m_graph.m_plan.mode))) {
-                fixEdge<StringUse>(node->child1());
-                fixEdge<StringUse>(node->child2());
-                break;
-            }
-            WatchpointSet* masqueradesAsUndefinedWatchpoint = m_graph.globalObjectFor(node->origin.semantic)->masqueradesAsUndefinedWatchpoint();
-            if (masqueradesAsUndefinedWatchpoint->isStillValid()) {
-                
-                if (node->child1()->shouldSpeculateObject()) {
-                    m_graph.watchpoints().addLazily(masqueradesAsUndefinedWatchpoint);
-                    fixEdge<ObjectUse>(node->child1());
-                    break;
-                }
-                if (node->child2()->shouldSpeculateObject()) {
-                    m_graph.watchpoints().addLazily(masqueradesAsUndefinedWatchpoint);
-                    fixEdge<ObjectUse>(node->child2());
-                    break;
-                }
-                
-            } else if (node->child1()->shouldSpeculateObject() && node->child2()->shouldSpeculateObject()) {
-                fixEdge<ObjectUse>(node->child1());
-                fixEdge<ObjectUse>(node->child2());
-                break;
-            }
-            if (node->child1()->shouldSpeculateSymbol()) {
-                fixEdge<SymbolUse>(node->child1());
-                break;
-            }
-            if (node->child2()->shouldSpeculateSymbol()) {
-                fixEdge<SymbolUse>(node->child2());
-                break;
-            }
-            if (node->child1()->shouldSpeculateMisc()) {
-                fixEdge<MiscUse>(node->child1());
-                break;
-            }
-            if (node->child2()->shouldSpeculateMisc()) {
-                fixEdge<MiscUse>(node->child2());
-                break;
-            }
-            if (node->child1()->shouldSpeculateStringIdent()
-                && node->child2()->shouldSpeculateNotStringVar()) {
-                fixEdge<StringIdentUse>(node->child1());
-                fixEdge<NotStringVarUse>(node->child2());
-                break;
-            }
-            if (node->child2()->shouldSpeculateStringIdent()
-                && node->child1()->shouldSpeculateNotStringVar()) {
-                fixEdge<StringIdentUse>(node->child2());
-                fixEdge<NotStringVarUse>(node->child1());
-                break;
-            }
-            if (node->child1()->shouldSpeculateString() && ((GPRInfo::numberOfRegisters >= 8) || isFTL(m_graph.m_plan.mode))) {
-                fixEdge<StringUse>(node->child1());
-                break;
-            }
-            if (node->child2()->shouldSpeculateString() && ((GPRInfo::numberOfRegisters >= 8) || isFTL(m_graph.m_plan.mode))) {
-                fixEdge<StringUse>(node->child2());
-                break;
-            }
+        case CompareStrictEq:
+        case SameValue: {
+            fixupCompareStrictEqAndSameValue(node);
             break;
         }
-            
+
         case StringFromCharCode:
             if (node->child1()->shouldSpeculateInt32()) {
                 fixEdge<Int32Use>(node->child1());
@@ -3478,6 +3384,138 @@ private:
         }
     }
 
+    void fixupCompareStrictEqAndSameValue(Node* node)
+    {
+        ASSERT(node->op() == SameValue || node->op() == CompareStrictEq);
+
+        if (Node::shouldSpeculateBoolean(node->child1().node(), node->child2().node())) {
+            fixEdge<BooleanUse>(node->child1());
+            fixEdge<BooleanUse>(node->child2());
+            node->setOpAndDefaultFlags(CompareStrictEq);
+            return;
+        }
+        if (Node::shouldSpeculateInt32(node->child1().node(), node->child2().node())) {
+            fixEdge<Int32Use>(node->child1());
+            fixEdge<Int32Use>(node->child2());
+            node->setOpAndDefaultFlags(CompareStrictEq);
+            return;
+        }
+        if (enableInt52()
+            && Node::shouldSpeculateAnyInt(node->child1().node(), node->child2().node())) {
+            fixEdge<Int52RepUse>(node->child1());
+            fixEdge<Int52RepUse>(node->child2());
+            node->setOpAndDefaultFlags(CompareStrictEq);
+            return;
+        }
+        if (Node::shouldSpeculateNumber(node->child1().node(), node->child2().node())) {
+            fixEdge<DoubleRepUse>(node->child1());
+            fixEdge<DoubleRepUse>(node->child2());
+            // Do not convert SameValue to CompareStrictEq in this case since SameValue(NaN, NaN) and SameValue(-0, +0)
+            // are not the same to CompareStrictEq(NaN, NaN) and CompareStrictEq(-0, +0).
+            return;
+        }
+        if (Node::shouldSpeculateSymbol(node->child1().node(), node->child2().node())) {
+            fixEdge<SymbolUse>(node->child1());
+            fixEdge<SymbolUse>(node->child2());
+            node->setOpAndDefaultFlags(CompareStrictEq);
+            return;
+        }
+        if (Node::shouldSpeculateBigInt(node->child1().node(), node->child2().node())) {
+            fixEdge<BigIntUse>(node->child1());
+            fixEdge<BigIntUse>(node->child2());
+            node->setOpAndDefaultFlags(CompareStrictEq);
+            return;
+        }
+        if (node->child1()->shouldSpeculateStringIdent() && node->child2()->shouldSpeculateStringIdent()) {
+            fixEdge<StringIdentUse>(node->child1());
+            fixEdge<StringIdentUse>(node->child2());
+            node->setOpAndDefaultFlags(CompareStrictEq);
+            return;
+        }
+        if (node->child1()->shouldSpeculateString() && node->child2()->shouldSpeculateString() && ((GPRInfo::numberOfRegisters >= 7) || isFTL(m_graph.m_plan.mode))) {
+            fixEdge<StringUse>(node->child1());
+            fixEdge<StringUse>(node->child2());
+            node->setOpAndDefaultFlags(CompareStrictEq);
+            return;
+        }
+
+        if (node->op() == SameValue) {
+            if (node->child1()->shouldSpeculateObject()) {
+                fixEdge<ObjectUse>(node->child1());
+                node->setOpAndDefaultFlags(CompareStrictEq);
+                return;
+            }
+            if (node->child2()->shouldSpeculateObject()) {
+                fixEdge<ObjectUse>(node->child2());
+                node->setOpAndDefaultFlags(CompareStrictEq);
+                return;
+            }
+        } else {
+            WatchpointSet* masqueradesAsUndefinedWatchpoint = m_graph.globalObjectFor(node->origin.semantic)->masqueradesAsUndefinedWatchpoint();
+            if (masqueradesAsUndefinedWatchpoint->isStillValid()) {
+                if (node->child1()->shouldSpeculateObject()) {
+                    m_graph.watchpoints().addLazily(masqueradesAsUndefinedWatchpoint);
+                    fixEdge<ObjectUse>(node->child1());
+                    return;
+                }
+                if (node->child2()->shouldSpeculateObject()) {
+                    m_graph.watchpoints().addLazily(masqueradesAsUndefinedWatchpoint);
+                    fixEdge<ObjectUse>(node->child2());
+                    return;
+                }
+            } else if (node->child1()->shouldSpeculateObject() && node->child2()->shouldSpeculateObject()) {
+                fixEdge<ObjectUse>(node->child1());
+                fixEdge<ObjectUse>(node->child2());
+                return;
+            }
+        }
+
+        if (node->child1()->shouldSpeculateSymbol()) {
+            fixEdge<SymbolUse>(node->child1());
+            node->setOpAndDefaultFlags(CompareStrictEq);
+            return;
+        }
+        if (node->child2()->shouldSpeculateSymbol()) {
+            fixEdge<SymbolUse>(node->child2());
+            node->setOpAndDefaultFlags(CompareStrictEq);
+            return;
+        }
+        if (node->child1()->shouldSpeculateMisc()) {
+            fixEdge<MiscUse>(node->child1());
+            node->setOpAndDefaultFlags(CompareStrictEq);
+            return;
+        }
+        if (node->child2()->shouldSpeculateMisc()) {
+            fixEdge<MiscUse>(node->child2());
+            node->setOpAndDefaultFlags(CompareStrictEq);
+            return;
+        }
+        if (node->child1()->shouldSpeculateStringIdent()
+            && node->child2()->shouldSpeculateNotStringVar()) {
+            fixEdge<StringIdentUse>(node->child1());
+            fixEdge<NotStringVarUse>(node->child2());
+            node->setOpAndDefaultFlags(CompareStrictEq);
+            return;
+        }
+        if (node->child2()->shouldSpeculateStringIdent()
+            && node->child1()->shouldSpeculateNotStringVar()) {
+            fixEdge<StringIdentUse>(node->child2());
+            fixEdge<NotStringVarUse>(node->child1());
+            node->setOpAndDefaultFlags(CompareStrictEq);
+            return;
+        }
+        if (node->child1()->shouldSpeculateString() && ((GPRInfo::numberOfRegisters >= 8) || isFTL(m_graph.m_plan.mode))) {
+            fixEdge<StringUse>(node->child1());
+            node->setOpAndDefaultFlags(CompareStrictEq);
+            return;
+        }
+        if (node->child2()->shouldSpeculateString() && ((GPRInfo::numberOfRegisters >= 8) || isFTL(m_graph.m_plan.mode))) {
+            fixEdge<StringUse>(node->child2());
+            node->setOpAndDefaultFlags(CompareStrictEq);
+            return;
+        }
+    }
+
     void fixupChecksInBlock(BasicBlock* block)
     {
         if (!block)
index 075b8af6769ecb8823638b5ad6c216d15e9f86c7..48e0d996aae2df3cbe8f6701361251deed71552f 100644 (file)
@@ -295,6 +295,7 @@ namespace JSC { namespace DFG {
     macro(CompareEq, NodeResultBoolean | NodeMustGenerate) \
     macro(CompareStrictEq, NodeResultBoolean) \
     macro(CompareEqPtr, NodeResultBoolean) \
+    macro(SameValue, NodeResultBoolean) \
     \
     /* Calls. */\
     macro(Call, NodeResultJS | NodeMustGenerate | NodeHasVarArgs) \
index f8fa9295ffdb7e93986949e5e3f07126f9c53407..e79d5852001e976e9e49b2319f0afbd56ed50d26 100644 (file)
@@ -1237,6 +1237,14 @@ size_t JIT_OPERATION operationCompareStrictEqCell(ExecState* exec, JSCell* op1,
     return JSValue::strictEqualSlowCaseInline(exec, op1, op2);
 }
 
+size_t JIT_OPERATION operationSameValue(ExecState* exec, EncodedJSValue arg1, EncodedJSValue arg2)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+
+    return sameValue(exec, JSValue::decode(arg1), JSValue::decode(arg2));
+}
+
 EncodedJSValue JIT_OPERATION operationToPrimitive(ExecState* exec, EncodedJSValue value)
 {
     VM* vm = &exec->vm();
index f48f9ab89f18f9e2219987bea6cc31ff1391b0d1..dd62b4ecd600d7ea78b5f728a156803f37644512 100644 (file)
@@ -161,6 +161,7 @@ size_t JIT_OPERATION operationRegExpTestString(ExecState*, JSGlobalObject*, RegE
 size_t JIT_OPERATION operationRegExpTest(ExecState*, JSGlobalObject*, RegExpObject*, EncodedJSValue) WTF_INTERNAL;
 size_t JIT_OPERATION operationRegExpTestGeneric(ExecState*, JSGlobalObject*, EncodedJSValue, EncodedJSValue) WTF_INTERNAL;
 size_t JIT_OPERATION operationCompareStrictEqCell(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
+size_t JIT_OPERATION operationSameValue(ExecState*, EncodedJSValue, EncodedJSValue) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationCreateActivationDirect(ExecState*, Structure*, JSScope*, SymbolTable*, EncodedJSValue);
 JSCell* JIT_OPERATION operationCreateDirectArguments(ExecState*, Structure*, uint32_t length, uint32_t minCapacity);
 JSCell* JIT_OPERATION operationCreateDirectArgumentsDuringExit(ExecState*, InlineCallFrame*, JSFunction*, uint32_t argumentCount);
index d4bbb4a61ca8915db081cca3cd6b51b5ccd26156..4bcb02c6085ca100685f3f5917f3855f1a1cb431 100644 (file)
@@ -851,6 +851,7 @@ private:
         case CompareEq:
         case CompareStrictEq:
         case CompareEqPtr:
+        case SameValue:
         case OverridesHasInstance:
         case InstanceOf:
         case InstanceOfCustom:
index 21db340a98289fc4cf9ea2204daa0c85fdacc9e6..f183ae949e4457e6b394b61d8e4f47f326d49e9c 100644 (file)
@@ -279,6 +279,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case CompareEq:
     case CompareStrictEq:
     case CompareEqPtr:
+    case SameValue:
     case Call:
     case DirectCall:
     case TailCallInlinedCaller:
index 92784e8fb9bb27f905c180cb8e3ee003fdaab3fa..81cbbe6911d5899cd7e4518d0111d781c185455c 100644 (file)
@@ -6365,6 +6365,72 @@ void SpeculativeJIT::compileStringIdentCompare(Node* node, MacroAssembler::Relat
     unblessedBooleanResult(resultGPR, node);
 }
 
+void SpeculativeJIT::compileSameValue(Node* node)
+{
+    if (node->isBinaryUseKind(DoubleRepUse)) {
+        SpeculateDoubleOperand arg1(this, node->child1());
+        SpeculateDoubleOperand arg2(this, node->child2());
+        GPRTemporary result(this);
+        GPRTemporary temp(this);
+        GPRTemporary temp2(this);
+
+        FPRReg arg1FPR = arg1.fpr();
+        FPRReg arg2FPR = arg2.fpr();
+        GPRReg resultGPR = result.gpr();
+        GPRReg tempGPR = temp.gpr();
+        GPRReg temp2GPR = temp2.gpr();
+
+#if USE(JSVALUE64)
+        m_jit.moveDoubleTo64(arg1FPR, tempGPR);
+        m_jit.moveDoubleTo64(arg2FPR, temp2GPR);
+        auto trueCase = m_jit.branch64(CCallHelpers::Equal, tempGPR, temp2GPR);
+#else
+        GPRTemporary temp3(this);
+        GPRTemporary temp4(this);
+
+        GPRReg temp3GPR = temp.gpr();
+        GPRReg temp4GPR = temp2.gpr();
+
+        m_jit.moveDoubleToInts(arg1FPR, tempGPR, temp2GPR);
+        m_jit.moveDoubleToInts(arg2FPR, temp3GPR, temp4GPR);
+        auto notEqual = m_jit.branch32(CCallHelpers::NotEqual, tempGPR, temp3GPR);
+        auto trueCase = m_jit.branch32(CCallHelpers::Equal, temp2GPR, temp4GPR);
+        notEqual.link(&m_jit);
+#endif
+
+        m_jit.compareDouble(CCallHelpers::DoubleNotEqualOrUnordered, arg1FPR, arg1FPR, tempGPR);
+        m_jit.compareDouble(CCallHelpers::DoubleNotEqualOrUnordered, arg2FPR, arg2FPR, temp2GPR);
+        m_jit.and32(tempGPR, temp2GPR, resultGPR);
+        auto done = m_jit.jump();
+
+        trueCase.link(&m_jit);
+        m_jit.move(CCallHelpers::TrustedImm32(1), resultGPR);
+        done.link(&m_jit);
+
+        unblessedBooleanResult(resultGPR, node);
+        return;
+    }
+
+    ASSERT(node->isBinaryUseKind(UntypedUse));
+
+    JSValueOperand arg1(this, node->child1());
+    JSValueOperand arg2(this, node->child2());
+    JSValueRegs arg1Regs = arg1.jsValueRegs();
+    JSValueRegs arg2Regs = arg2.jsValueRegs();
+
+    arg1.use();
+    arg2.use();
+
+    flushRegisters();
+
+    GPRFlushedCallResult result(this);
+    GPRReg resultGPR = result.gpr();
+    callOperation(operationSameValue, resultGPR, arg1Regs, arg2Regs);
+    m_jit.exceptionCheck();
+
+    unblessedBooleanResult(resultGPR, node, UseChildrenCalledExplicitly);
+}
+
 void SpeculativeJIT::compileStringZeroLength(Node* node)
 {
     SpeculateCellOperand str(this, node->child1());
index bb93965150a9a2d3cdbf1c2e3714d99d94c078e9..055ff4ed240bd24a9f3c1fb56123949aa4964257 100644 (file)
@@ -1263,6 +1263,8 @@ public:
     void compileStringIdentCompare(Node*, MacroAssembler::RelationalCondition);
     
     bool compileStrictEq(Node*);
+
+    void compileSameValue(Node*);
     
     void compileAllocatePropertyStorage(Node*);
     void compileReallocatePropertyStorage(Node*);
index 945e40c3fbbe74cf435f805263c7709d45eb6dc6..3d39621335a7535e144c705d2f688a439fb6a5e8 100644 (file)
@@ -2211,6 +2211,10 @@ void SpeculativeJIT::compile(Node* node)
         compileCompareEqPtr(node);
         break;
 
+    case SameValue:
+        compileSameValue(node);
+        break;
+
     case StringCharCodeAt: {
         compileGetCharCodeAt(node);
         break;
index 2af307fe1516179d7806f2f2a72db3edb997bdd6..ccc3cada01e3218d5a4eac9cfdb4a69d260aa3f3 100644 (file)
@@ -2377,6 +2377,10 @@ void SpeculativeJIT::compile(Node* node)
         compileCompareEqPtr(node);
         break;
 
+    case SameValue:
+        compileSameValue(node);
+        break;
+
     case StringCharCodeAt: {
         compileGetCharCodeAt(node);
         break;
index e94783353042c980205451d9a0fa488afaf6632b..d7726bbe1df4c2b0b72d97304bf8777cbdd1e53d 100644 (file)
@@ -271,6 +271,7 @@ public:
                 case CompareBelowEq:
                 case CompareEq:
                 case CompareStrictEq:
+                case SameValue:
                 case StrCat:
                     VALIDATE((node), !!node->child1());
                     VALIDATE((node), !!node->child2());
index 2f74baecfe632a638f5e67f51cdbfbff053e72da..06863d0e22d64e6a1b13373bb13b0b743a5cc2ed 100644 (file)
@@ -314,6 +314,7 @@ inline CapabilityLevel canCompile(Node* node)
     case CompareBelow:
     case CompareBelowEq:
     case CompareStrictEq:
+    case SameValue:
     case DefineDataProperty:
     case DefineAccessorProperty:
     case StringSlice:
index eef68b99d4d03bd23573e320d3d14c7251193140..5f858179b789134b9e62e416ccfbc258018e3ed6 100644 (file)
@@ -987,6 +987,9 @@ private:
         case CompareEqPtr:
             compileCompareEqPtr();
             break;
+        case SameValue:
+            compileSameValue();
+            break;
         case LogicalNot:
             compileLogicalNot();
             break;
@@ -7072,6 +7075,45 @@ private:
     {
         setBoolean(m_out.belowOrEqual(lowInt32(m_node->child1()), lowInt32(m_node->child2())));
     }
+
+    void compileSameValue()
+    {
+        if (m_node->isBinaryUseKind(DoubleRepUse)) {
+            LValue arg1 = lowDouble(m_node->child1());
+            LValue arg2 = lowDouble(m_node->child2());
+
+            LBasicBlock numberCase = m_out.newBlock();
+            LBasicBlock continuation = m_out.newBlock();
+
+            PatchpointValue* patchpoint = m_out.patchpoint(Int32);
+            patchpoint->append(arg1, ValueRep::SomeRegister);
+            patchpoint->append(arg2, ValueRep::SomeRegister);
+            patchpoint->numGPScratchRegisters = 1;
+            patchpoint->setGenerator(
+                [] (CCallHelpers& jit, const StackmapGenerationParams& params) {
+                    GPRReg scratchGPR = params.gpScratch(0);
+                    jit.moveDoubleTo64(params[1].fpr(), scratchGPR);
+                    jit.moveDoubleTo64(params[2].fpr(), params[0].gpr());
+                    jit.compare64(CCallHelpers::Equal, scratchGPR, params[0].gpr(), params[0].gpr());
+                });
+            patchpoint->effects = Effects::none();
+            ValueFromBlock compareResult = m_out.anchor(patchpoint);
+            m_out.branch(patchpoint, unsure(continuation), unsure(numberCase));
+
+            LBasicBlock lastNext = m_out.appendTo(numberCase, continuation);
+            LValue isArg1NaN = m_out.doubleNotEqualOrUnordered(arg1, arg1);
+            LValue isArg2NaN = m_out.doubleNotEqualOrUnordered(arg2, arg2);
+            ValueFromBlock nanResult = m_out.anchor(m_out.bitAnd(isArg1NaN, isArg2NaN));
+            m_out.jump(continuation);
+
+            m_out.appendTo(continuation, lastNext);
+            setBoolean(m_out.phi(Int32, compareResult, nanResult));
+            return;
+        }
+
+        ASSERT(m_node->isBinaryUseKind(UntypedUse));
+        setBoolean(vmCall(Int32, m_out.operation(operationSameValue), m_callFrame, lowJSValue(m_node->child1()), lowJSValue(m_node->child2())));
+    }
     
     void compileLogicalNot()
     {
index ad49b17b2d1857123cfaab1caf4c501fd6f8e0a7..6652de04467b4615cd80ff753bf2da5cb990d980 100644 (file)
@@ -115,6 +115,8 @@ const char* intrinsicName(Intrinsic intrinsic)
         return "RegExpMatchFastIntrinsic";
     case ObjectGetPrototypeOfIntrinsic:
         return "ObjectGetPrototypeOfIntrinsic";
+    case ObjectIsIntrinsic:
+        return "ObjectIsIntrinsic";
     case ReflectGetPrototypeOfIntrinsic:
         return "ReflectGetPrototypeOfIntrinsic";
     case StringPrototypeValueOfIntrinsic:
index d64e9c7f12ad64e956b38c6609be8a02846f6e44..61fe0bd7f2580f265652167ff82bceaabbe76a00 100644 (file)
@@ -70,6 +70,7 @@ enum Intrinsic {
     RegExpTestFastIntrinsic,
     RegExpMatchFastIntrinsic,
     ObjectGetPrototypeOfIntrinsic,
+    ObjectIsIntrinsic,
     ReflectGetPrototypeOfIntrinsic,
     StringPrototypeValueOfIntrinsic,
     StringPrototypeReplaceIntrinsic,
index 22d64596dac2f9319c514b7268cd72f96fc7138f..e18c765473a024d33bf50ced0be9c1cc1a29b166 100644 (file)
@@ -83,7 +83,7 @@ const ClassInfo ObjectConstructor::s_info = { "Function", &InternalFunction::s_i
   isSealed                  objectConstructorIsSealed                   DontEnum|Function 1
   isFrozen                  objectConstructorIsFrozen                   DontEnum|Function 1
   isExtensible              objectConstructorIsExtensible               DontEnum|Function 1
-  is                        objectConstructorIs                         DontEnum|Function 2
+  is                        objectConstructorIs                         DontEnum|Function 2 ObjectIsIntrinsic
   assign                    objectConstructorAssign                     DontEnum|Function 2
   values                    objectConstructorValues                     DontEnum|Function 1
   entries                   JSBuiltin                                   DontEnum|Function 1