Baseline op_jtrue emits an insane amount of code
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 2 Jun 2018 21:13:47 +0000 (21:13 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 2 Jun 2018 21:13:47 +0000 (21:13 +0000)
https://bugs.webkit.org/show_bug.cgi?id=185708

Reviewed by Filip Pizlo.

JSTests:

* stress/logical-not-masquerades-as-undefined.js: Added.
(shouldBe):
(test):
* stress/logical-not.js: Added.
(shouldBe):
(test):

Source/JavaScriptCore:

op_jtrue / op_jfalse bloats massive amount of code. This patch attempts to reduce the size of this code by,

1. op_jtrue / op_jfalse immediately jumps if the condition met. We add AssemblyHelpers::branchIf{Truthy,Falsey}
   to jump directly. This tightens the code.

2. Align our emitConvertValueToBoolean implementation to FTL's boolify function. It emits less code.

This reduces the code size of op_jtrue in x64 from 220 bytes to 164 bytes.

[  12] jtrue             arg1, 6(->18)
      0x7f233170162c: mov 0x30(%rbp), %rax
      0x7f2331701630: mov %rax, %rsi
      0x7f2331701633: xor $0x6, %rsi
      0x7f2331701637: test $0xfffffffffffffffe, %rsi
      0x7f233170163e: jnz 0x7f2331701654
      0x7f2331701644: cmp $0x7, %eax
      0x7f2331701647: setz %sil
      0x7f233170164b: movzx %sil, %esi
      0x7f233170164f: jmp 0x7f2331701705
      0x7f2331701654: test %rax, %r14
      0x7f2331701657: jz 0x7f233170169c
      0x7f233170165d: cmp %r14, %rax
      0x7f2331701660: jb 0x7f2331701675
      0x7f2331701666: test %eax, %eax
      0x7f2331701668: setnz %sil
      0x7f233170166c: movzx %sil, %esi
      0x7f2331701670: jmp 0x7f2331701705
      0x7f2331701675: lea (%r14,%rax), %rsi
      0x7f2331701679: movq %rsi, %xmm0
      0x7f233170167e: xorps %xmm1, %xmm1
      0x7f2331701681: ucomisd %xmm1, %xmm0
      0x7f2331701685: jz 0x7f2331701695
      0x7f233170168b: mov $0x1, %esi
      0x7f2331701690: jmp 0x7f2331701705
      0x7f2331701695: xor %esi, %esi
      0x7f2331701697: jmp 0x7f2331701705
      0x7f233170169c: test %rax, %r15
      0x7f233170169f: jnz 0x7f2331701703
      0x7f23317016a5: cmp $0x1, 0x5(%rax)
      0x7f23317016a9: jnz 0x7f23317016c1
      0x7f23317016af: mov 0x8(%rax), %esi
      0x7f23317016b2: test %esi, %esi
      0x7f23317016b4: setnz %sil
      0x7f23317016b8: movzx %sil, %esi
      0x7f23317016bc: jmp 0x7f2331701705
      0x7f23317016c1: test $0x1, 0x6(%rax)
      0x7f23317016c5: jz 0x7f23317016f9
      0x7f23317016cb: mov (%rax), %esi
      0x7f23317016cd: mov $0x7f23315000c8, %rdx
      0x7f23317016d7: mov (%rdx), %rdx
      0x7f23317016da: mov (%rdx,%rsi,8), %rsi
      0x7f23317016de: mov $0x7f2330de0000, %rdx
      0x7f23317016e8: cmp %rdx, 0x18(%rsi)
      0x7f23317016ec: jnz 0x7f23317016f9
      0x7f23317016f2: xor %esi, %esi
      0x7f23317016f4: jmp 0x7f2331701705
      0x7f23317016f9: mov $0x1, %esi
      0x7f23317016fe: jmp 0x7f2331701705
      0x7f2331701703: xor %esi, %esi
      0x7f2331701705: test %esi, %esi
      0x7f2331701707: jnz 0x7f233170171b

[  12] jtrue             arg1, 6(->18)
      0x7f6c8710156c: mov 0x30(%rbp), %rax
      0x7f6c87101570: test %rax, %r15
      0x7f6c87101573: jnz 0x7f6c871015c8
      0x7f6c87101579: cmp $0x1, 0x5(%rax)
      0x7f6c8710157d: jnz 0x7f6c87101592
      0x7f6c87101583: cmp $0x0, 0x8(%rax)
      0x7f6c87101587: jnz 0x7f6c87101623
      0x7f6c8710158d: jmp 0x7f6c87101615
      0x7f6c87101592: test $0x1, 0x6(%rax)
      0x7f6c87101596: jz 0x7f6c87101623
      0x7f6c8710159c: mov (%rax), %esi
      0x7f6c8710159e: mov $0x7f6c86f000e0, %rdx
      0x7f6c871015a8: mov (%rdx), %rdx
      0x7f6c871015ab: mov (%rdx,%rsi,8), %rsi
      0x7f6c871015af: mov $0x7f6c867e0000, %rdx
      0x7f6c871015b9: cmp %rdx, 0x18(%rsi)
      0x7f6c871015bd: jnz 0x7f6c87101623
      0x7f6c871015c3: jmp 0x7f6c87101615
      0x7f6c871015c8: cmp %r14, %rax
      0x7f6c871015cb: jb 0x7f6c871015de
      0x7f6c871015d1: test %eax, %eax
      0x7f6c871015d3: jnz 0x7f6c87101623
      0x7f6c871015d9: jmp 0x7f6c87101615
      0x7f6c871015de: test %rax, %r14
      0x7f6c871015e1: jz 0x7f6c87101602
      0x7f6c871015e7: lea (%r14,%rax), %rsi
      0x7f6c871015eb: movq %rsi, %xmm0
      0x7f6c871015f0: xorps %xmm1, %xmm1
      0x7f6c871015f3: ucomisd %xmm1, %xmm0
      0x7f6c871015f7: jz 0x7f6c87101615
      0x7f6c871015fd: jmp 0x7f6c87101623
      0x7f6c87101602: mov $0x7, %r11
      0x7f6c8710160c: cmp %r11, %rax
      0x7f6c8710160f: jz 0x7f6c87101623

* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::emitBranch):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::emitBranch):
* jit/AssemblyHelpers.cpp:
(JSC::AssemblyHelpers::emitConvertValueToBoolean):
(JSC::AssemblyHelpers::branchIfValue):
* jit/AssemblyHelpers.h:
(JSC::AssemblyHelpers::branchIfTruthy):
(JSC::AssemblyHelpers::branchIfFalsey):
* jit/JIT.h:
* jit/JITInlines.h:
(JSC::JIT::addJump):
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_jfalse):
(JSC::JIT::emit_op_jtrue):
* jit/JITOpcodes32_64.cpp:
(JSC::JIT::emit_op_jfalse):
(JSC::JIT::emit_op_jtrue):

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

12 files changed:
JSTests/ChangeLog
JSTests/stress/logical-not-masquerades-as-undefined.js [new file with mode: 0644]
JSTests/stress/logical-not.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/jit/AssemblyHelpers.cpp
Source/JavaScriptCore/jit/AssemblyHelpers.h
Source/JavaScriptCore/jit/JIT.h
Source/JavaScriptCore/jit/JITInlines.h
Source/JavaScriptCore/jit/JITOpcodes.cpp
Source/JavaScriptCore/jit/JITOpcodes32_64.cpp

index 77320eb..8f732f4 100644 (file)
@@ -1,3 +1,17 @@
+2018-06-01  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        Baseline op_jtrue emits an insane amount of code
+        https://bugs.webkit.org/show_bug.cgi?id=185708
+
+        Reviewed by Filip Pizlo.
+
+        * stress/logical-not-masquerades-as-undefined.js: Added.
+        (shouldBe):
+        (test):
+        * stress/logical-not.js: Added.
+        (shouldBe):
+        (test):
+
 2018-06-02  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [JSC] Remove WeakReferenceHarvester
diff --git a/JSTests/stress/logical-not-masquerades-as-undefined.js b/JSTests/stress/logical-not-masquerades-as-undefined.js
new file mode 100644 (file)
index 0000000..3db7d1a
--- /dev/null
@@ -0,0 +1,35 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test(value)
+{
+    return !value;
+}
+noInline(test);
+
+var data = [
+    [ {}, true ],
+    [ true, true ],
+    [ false, false ],
+    [ -0, false ],
+    [ 1, true ],
+    [ 4.2, true ],
+    [ NaN, false ],
+    [ Infinity, true ],
+    [ [], true ],
+    [ new Date(), true ],
+    [ "", false ],
+    [ "" + "" + "", false ],
+    [ "Cocoa", true ],
+    [ undefined, false ],
+    [ null, false ],
+    [ Symbol(), true ],
+    [ makeMasquerader() , false]
+];
+
+for (var i = 0; i < 1e4; ++i) {
+    for (let [ value, result ] of data)
+        shouldBe(!test(value), result);
+}
diff --git a/JSTests/stress/logical-not.js b/JSTests/stress/logical-not.js
new file mode 100644 (file)
index 0000000..aa90dcb
--- /dev/null
@@ -0,0 +1,34 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test(value)
+{
+    return !value;
+}
+noInline(test);
+
+var data = [
+    [ {}, true ],
+    [ true, true ],
+    [ false, false ],
+    [ -0, false ],
+    [ 1, true ],
+    [ 4.2, true ],
+    [ NaN, false ],
+    [ Infinity, true ],
+    [ [], true ],
+    [ new Date(), true ],
+    [ "", false ],
+    [ "" + "" + "", false ],
+    [ "Cocoa", true ],
+    [ undefined, false ],
+    [ null, false ],
+    [ Symbol(), true ],
+];
+
+for (var i = 0; i < 1e4; ++i) {
+    for (let [ value, result ] of data)
+        shouldBe(!test(value), result);
+}
index 31fedd2..9fb98b5 100644 (file)
@@ -1,3 +1,128 @@
+2018-06-01  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        Baseline op_jtrue emits an insane amount of code
+        https://bugs.webkit.org/show_bug.cgi?id=185708
+
+        Reviewed by Filip Pizlo.
+
+        op_jtrue / op_jfalse bloats massive amount of code. This patch attempts to reduce the size of this code by,
+
+        1. op_jtrue / op_jfalse immediately jumps if the condition met. We add AssemblyHelpers::branchIf{Truthy,Falsey}
+           to jump directly. This tightens the code.
+
+        2. Align our emitConvertValueToBoolean implementation to FTL's boolify function. It emits less code.
+
+        This reduces the code size of op_jtrue in x64 from 220 bytes to 164 bytes.
+
+        [  12] jtrue             arg1, 6(->18)
+              0x7f233170162c: mov 0x30(%rbp), %rax
+              0x7f2331701630: mov %rax, %rsi
+              0x7f2331701633: xor $0x6, %rsi
+              0x7f2331701637: test $0xfffffffffffffffe, %rsi
+              0x7f233170163e: jnz 0x7f2331701654
+              0x7f2331701644: cmp $0x7, %eax
+              0x7f2331701647: setz %sil
+              0x7f233170164b: movzx %sil, %esi
+              0x7f233170164f: jmp 0x7f2331701705
+              0x7f2331701654: test %rax, %r14
+              0x7f2331701657: jz 0x7f233170169c
+              0x7f233170165d: cmp %r14, %rax
+              0x7f2331701660: jb 0x7f2331701675
+              0x7f2331701666: test %eax, %eax
+              0x7f2331701668: setnz %sil
+              0x7f233170166c: movzx %sil, %esi
+              0x7f2331701670: jmp 0x7f2331701705
+              0x7f2331701675: lea (%r14,%rax), %rsi
+              0x7f2331701679: movq %rsi, %xmm0
+              0x7f233170167e: xorps %xmm1, %xmm1
+              0x7f2331701681: ucomisd %xmm1, %xmm0
+              0x7f2331701685: jz 0x7f2331701695
+              0x7f233170168b: mov $0x1, %esi
+              0x7f2331701690: jmp 0x7f2331701705
+              0x7f2331701695: xor %esi, %esi
+              0x7f2331701697: jmp 0x7f2331701705
+              0x7f233170169c: test %rax, %r15
+              0x7f233170169f: jnz 0x7f2331701703
+              0x7f23317016a5: cmp $0x1, 0x5(%rax)
+              0x7f23317016a9: jnz 0x7f23317016c1
+              0x7f23317016af: mov 0x8(%rax), %esi
+              0x7f23317016b2: test %esi, %esi
+              0x7f23317016b4: setnz %sil
+              0x7f23317016b8: movzx %sil, %esi
+              0x7f23317016bc: jmp 0x7f2331701705
+              0x7f23317016c1: test $0x1, 0x6(%rax)
+              0x7f23317016c5: jz 0x7f23317016f9
+              0x7f23317016cb: mov (%rax), %esi
+              0x7f23317016cd: mov $0x7f23315000c8, %rdx
+              0x7f23317016d7: mov (%rdx), %rdx
+              0x7f23317016da: mov (%rdx,%rsi,8), %rsi
+              0x7f23317016de: mov $0x7f2330de0000, %rdx
+              0x7f23317016e8: cmp %rdx, 0x18(%rsi)
+              0x7f23317016ec: jnz 0x7f23317016f9
+              0x7f23317016f2: xor %esi, %esi
+              0x7f23317016f4: jmp 0x7f2331701705
+              0x7f23317016f9: mov $0x1, %esi
+              0x7f23317016fe: jmp 0x7f2331701705
+              0x7f2331701703: xor %esi, %esi
+              0x7f2331701705: test %esi, %esi
+              0x7f2331701707: jnz 0x7f233170171b
+
+        [  12] jtrue             arg1, 6(->18)
+              0x7f6c8710156c: mov 0x30(%rbp), %rax
+              0x7f6c87101570: test %rax, %r15
+              0x7f6c87101573: jnz 0x7f6c871015c8
+              0x7f6c87101579: cmp $0x1, 0x5(%rax)
+              0x7f6c8710157d: jnz 0x7f6c87101592
+              0x7f6c87101583: cmp $0x0, 0x8(%rax)
+              0x7f6c87101587: jnz 0x7f6c87101623
+              0x7f6c8710158d: jmp 0x7f6c87101615
+              0x7f6c87101592: test $0x1, 0x6(%rax)
+              0x7f6c87101596: jz 0x7f6c87101623
+              0x7f6c8710159c: mov (%rax), %esi
+              0x7f6c8710159e: mov $0x7f6c86f000e0, %rdx
+              0x7f6c871015a8: mov (%rdx), %rdx
+              0x7f6c871015ab: mov (%rdx,%rsi,8), %rsi
+              0x7f6c871015af: mov $0x7f6c867e0000, %rdx
+              0x7f6c871015b9: cmp %rdx, 0x18(%rsi)
+              0x7f6c871015bd: jnz 0x7f6c87101623
+              0x7f6c871015c3: jmp 0x7f6c87101615
+              0x7f6c871015c8: cmp %r14, %rax
+              0x7f6c871015cb: jb 0x7f6c871015de
+              0x7f6c871015d1: test %eax, %eax
+              0x7f6c871015d3: jnz 0x7f6c87101623
+              0x7f6c871015d9: jmp 0x7f6c87101615
+              0x7f6c871015de: test %rax, %r14
+              0x7f6c871015e1: jz 0x7f6c87101602
+              0x7f6c871015e7: lea (%r14,%rax), %rsi
+              0x7f6c871015eb: movq %rsi, %xmm0
+              0x7f6c871015f0: xorps %xmm1, %xmm1
+              0x7f6c871015f3: ucomisd %xmm1, %xmm0
+              0x7f6c871015f7: jz 0x7f6c87101615
+              0x7f6c871015fd: jmp 0x7f6c87101623
+              0x7f6c87101602: mov $0x7, %r11
+              0x7f6c8710160c: cmp %r11, %rax
+              0x7f6c8710160f: jz 0x7f6c87101623
+
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::emitBranch):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::emitBranch):
+        * jit/AssemblyHelpers.cpp:
+        (JSC::AssemblyHelpers::emitConvertValueToBoolean):
+        (JSC::AssemblyHelpers::branchIfValue):
+        * jit/AssemblyHelpers.h:
+        (JSC::AssemblyHelpers::branchIfTruthy):
+        (JSC::AssemblyHelpers::branchIfFalsey):
+        * jit/JIT.h:
+        * jit/JITInlines.h:
+        (JSC::JIT::addJump):
+        * jit/JITOpcodes.cpp:
+        (JSC::JIT::emit_op_jfalse):
+        (JSC::JIT::emit_op_jtrue):
+        * jit/JITOpcodes32_64.cpp:
+        (JSC::JIT::emit_op_jfalse):
+        (JSC::JIT::emit_op_jtrue):
+
 2018-06-02  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [JSC] Remove WeakReferenceHarvester
index a8967d2..93d7710 100644 (file)
@@ -1713,8 +1713,8 @@ void SpeculativeJIT::emitBranch(Node* node)
 
         bool shouldCheckMasqueradesAsUndefined = !masqueradesAsUndefinedWatchpointIsStillValid();
         JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic);
-        m_jit.emitConvertValueToBoolean(*m_jit.vm(), valueRegs, resultGPR, temp.gpr(), valueFPR.fpr(), tempFPR.fpr(), shouldCheckMasqueradesAsUndefined, globalObject);
-        branchTest32(JITCompiler::Zero, resultGPR, notTaken);
+        auto falsey = m_jit.branchIfFalsey(*m_jit.vm(), valueRegs, resultGPR, temp.gpr(), valueFPR.fpr(), tempFPR.fpr(), shouldCheckMasqueradesAsUndefined, globalObject);
+        addBranch(falsey, notTaken);
         jump(taken, ForceJump);
 
         noResult(node, UseChildrenCalledExplicitly);
index b70c211..8c21cd3 100644 (file)
@@ -1888,9 +1888,8 @@ void SpeculativeJIT::emitBranch(Node* node)
             value.use();
 
             JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic);
-            m_jit.emitConvertValueToBoolean(*m_jit.vm(), JSValueRegs(valueGPR), resultGPR, scratchGPR, valueFPR, tempFPR, shouldCheckMasqueradesAsUndefined, globalObject);
-    
-            branchTest32(MacroAssembler::NonZero, resultGPR, taken);
+            auto truthy = m_jit.branchIfTruthy(*m_jit.vm(), JSValueRegs(valueGPR), resultGPR, scratchGPR, valueFPR, tempFPR, shouldCheckMasqueradesAsUndefined, globalObject);
+            addBranch(truthy, taken);
             jump(notTaken);
         }
         
index 6cca650..8cc878f 100644 (file)
@@ -754,92 +754,166 @@ void AssemblyHelpers::wangsInt64Hash(GPRReg inputAndResult, GPRReg scratch)
 }
 #endif // USE(JSVALUE64)
 
-void AssemblyHelpers::emitConvertValueToBoolean(VM& vm, JSValueRegs value, GPRReg result, GPRReg scratch, FPRReg valueAsFPR, FPRReg tempFPR, bool shouldCheckMasqueradesAsUndefined, JSGlobalObject* globalObject, bool negateResult)
+void AssemblyHelpers::emitConvertValueToBoolean(VM& vm, JSValueRegs value, GPRReg result, GPRReg scratchIfShouldCheckMasqueradesAsUndefined, FPRReg valueAsFPR, FPRReg tempFPR, bool shouldCheckMasqueradesAsUndefined, JSGlobalObject* globalObject, bool invert)
 {
     // Implements the following control flow structure:
-    // if (value is boolean) {
-    //     result = value === true
-    // } else if (value is integer) {
-    //     result = value !== 0
-    // } else if (value is double) {
-    //     result = value !== 0.0 && !isNaN(value);
-    // } else if (value is cell) {
-    //     if (value is string) {
-    //          result = value.length() !== 0;
-    //     } else {
-    //          do crazy things for masquerades as undefined
+    // if (value is cell) {
+    //     if (value is string)
+    //         result = !!value->length
+    //     else {
+    //         do evil things for masquerades-as-undefined
+    //         result = true
     //     }
+    // } else if (value is int32) {
+    //     result = !!unboxInt32(value)
+    // } else if (value is number) {
+    //     result = !!unboxDouble(value)
     // } else {
-    //     result = false;
+    //     result = value == jsTrue
     // }
-    //
-    // if (negateResult)
-    //     result = !result;
 
     JumpList done;
-    auto notBoolean = branchIfNotBoolean(value, result);
-#if USE(JSVALUE64)
-    compare32(negateResult ? NotEqual : Equal, value.gpr(), TrustedImm32(ValueTrue), result);
-#else
-    compare32(negateResult ? Equal : NotEqual, value.payloadGPR(), TrustedImm32(0), result);
-#endif
+
+    auto notCell = branchIfNotCell(value);
+    auto isCellButNotString = branchIfNotString(value.payloadGPR());
+    load32(Address(value.payloadGPR(), JSString::offsetOfLength()), result);
+    compare32(invert ? Equal : NotEqual, result, TrustedImm32(0), result);
     done.append(jump());
 
-    notBoolean.link(this);
-#if USE(JSVALUE64)
-    auto isNotNumber = branchIfNotNumber(value.gpr());
-#else
-    ASSERT(scratch != InvalidGPRReg);
-    auto isNotNumber = branchIfNotNumber(value, scratch);
-#endif
-    auto isDouble = branchIfNotInt32(value);
+    isCellButNotString.link(this);
+    if (shouldCheckMasqueradesAsUndefined) {
+        ASSERT(scratchIfShouldCheckMasqueradesAsUndefined != InvalidGPRReg);
+        JumpList isNotMasqueradesAsUndefined;
+        isNotMasqueradesAsUndefined.append(branchTest8(Zero, Address(value.payloadGPR(), JSCell::typeInfoFlagsOffset()), TrustedImm32(MasqueradesAsUndefined)));
+        emitLoadStructure(vm, value.payloadGPR(), result, scratchIfShouldCheckMasqueradesAsUndefined);
+        move(TrustedImmPtr(globalObject), scratchIfShouldCheckMasqueradesAsUndefined);
+        isNotMasqueradesAsUndefined.append(branchPtr(NotEqual, Address(result, Structure::globalObjectOffset()), scratchIfShouldCheckMasqueradesAsUndefined));
 
-    // It's an int32.
-    compare32(negateResult ? Equal : NotEqual, value.payloadGPR(), TrustedImm32(0), result);
+        // We act like we are "undefined" here.
+        move(invert ? TrustedImm32(1) : TrustedImm32(0), result);
+        done.append(jump());
+        isNotMasqueradesAsUndefined.link(this);
+    }
+    move(invert ? TrustedImm32(0) : TrustedImm32(1), result);
+    done.append(jump());
+
+    notCell.link(this);
+    auto notInt32 = branchIfNotInt32(value);
+    compare32(invert ? Equal : NotEqual, value.payloadGPR(), TrustedImm32(0), result);
     done.append(jump());
 
-    isDouble.link(this);
+    notInt32.link(this);
+    auto notDouble = branchIfNotDoubleKnownNotInt32(value);
 #if USE(JSVALUE64)
     unboxDouble(value.gpr(), result, valueAsFPR);
 #else
     unboxDouble(value, valueAsFPR, tempFPR);
 #endif
-    auto isZeroOrNaN = branchDoubleZeroOrNaN(valueAsFPR, tempFPR);
-    move(negateResult ? TrustedImm32(0) : TrustedImm32(1), result);
-    done.append(jump());
-    isZeroOrNaN.link(this);
-    move(negateResult ? TrustedImm32(1) : TrustedImm32(0), result);
+    move(invert ? TrustedImm32(1) : TrustedImm32(0), result);
+    done.append(branchDoubleZeroOrNaN(valueAsFPR, tempFPR));
+    move(invert ? TrustedImm32(0) : TrustedImm32(1), result);
     done.append(jump());
 
-    isNotNumber.link(this);
-    auto isNotCellAndIsNotNumberAndIsNotBoolean = branchIfNotCell(value);
-    auto isCellButNotString = branch8(NotEqual,
-        Address(value.payloadGPR(), JSCell::typeInfoTypeOffset()), TrustedImm32(StringType));
-    load32(Address(value.payloadGPR(), JSString::offsetOfLength()), result);
-    compare32(negateResult ? Equal : NotEqual, result, TrustedImm32(0), result);
+    notDouble.link(this);
+#if USE(JSVALUE64)
+    static_assert(static_cast<int32_t>(ValueTrue) == ValueTrue, "");
+    compare64(invert ? NotEqual : Equal, value.gpr(), TrustedImm32(ValueTrue), result);
+#else
+    move(invert ? TrustedImm32(1) : TrustedImm32(0), result);
+    done.append(branchIfNotBoolean(value, InvalidGPRReg));
+    compare32(invert ? Equal : NotEqual, value.payloadGPR(), TrustedImm32(0), result);
+#endif
+
+    done.link(this);
+}
+
+AssemblyHelpers::JumpList AssemblyHelpers::branchIfValue(VM& vm, JSValueRegs value, GPRReg scratch, GPRReg scratchIfShouldCheckMasqueradesAsUndefined, FPRReg valueAsFPR, FPRReg tempFPR, bool shouldCheckMasqueradesAsUndefined, JSGlobalObject* globalObject, bool invert)
+{
+    // Implements the following control flow structure:
+    // if (value is cell) {
+    //     if (value is string)
+    //         result = !!value->length
+    //     else {
+    //         do evil things for masquerades-as-undefined
+    //         result = true
+    //     }
+    // } else if (value is int32) {
+    //     result = !!unboxInt32(value)
+    // } else if (value is number) {
+    //     result = !!unboxDouble(value)
+    // } else {
+    //     result = value == jsTrue
+    // }
+
+    JumpList done;
+    JumpList truthy;
+
+    auto notCell = branchIfNotCell(value);
+    auto isCellButNotString = branchIfNotString(value.payloadGPR());
+    truthy.append(branchTest32(invert ? Zero : NonZero, Address(value.payloadGPR(), JSString::offsetOfLength())));
     done.append(jump());
 
-    isCellButNotString.link(this);
     if (shouldCheckMasqueradesAsUndefined) {
-        ASSERT(scratch != InvalidGPRReg);
+        isCellButNotString.link(this);
+        ASSERT(scratchIfShouldCheckMasqueradesAsUndefined != InvalidGPRReg);
         JumpList isNotMasqueradesAsUndefined;
         isNotMasqueradesAsUndefined.append(branchTest8(Zero, Address(value.payloadGPR(), JSCell::typeInfoFlagsOffset()), TrustedImm32(MasqueradesAsUndefined)));
-        emitLoadStructure(vm, value.payloadGPR(), result, scratch);
-        move(TrustedImmPtr(globalObject), scratch);
-        isNotMasqueradesAsUndefined.append(branchPtr(NotEqual, Address(result, Structure::globalObjectOffset()), scratch));
+        emitLoadStructure(vm, value.payloadGPR(), scratch, scratchIfShouldCheckMasqueradesAsUndefined);
+        move(TrustedImmPtr(globalObject), scratchIfShouldCheckMasqueradesAsUndefined);
+        isNotMasqueradesAsUndefined.append(branchPtr(NotEqual, Address(scratch, Structure::globalObjectOffset()), scratchIfShouldCheckMasqueradesAsUndefined));
+
         // We act like we are "undefined" here.
-        move(negateResult ? TrustedImm32(1) : TrustedImm32(0), result);
-        done.append(jump());
-        isNotMasqueradesAsUndefined.link(this);
+        if (invert)
+            truthy.append(jump());
+        else
+            done.append(jump());
+
+        if (invert)
+            done.append(isNotMasqueradesAsUndefined);
+        else
+            truthy.append(isNotMasqueradesAsUndefined);
+    } else {
+        if (invert)
+            done.append(isCellButNotString);
+        else
+            truthy.append(isCellButNotString);
     }
-    move(negateResult ? TrustedImm32(0) : TrustedImm32(1), result);
+
+    notCell.link(this);
+    auto notInt32 = branchIfNotInt32(value);
+    truthy.append(branchTest32(invert ? Zero : NonZero, value.payloadGPR()));
     done.append(jump());
 
-    // null or undefined.
-    isNotCellAndIsNotNumberAndIsNotBoolean.link(this); 
-    move(negateResult ? TrustedImm32(1) : TrustedImm32(0), result);
+    notInt32.link(this);
+    auto notDouble = branchIfNotDoubleKnownNotInt32(value);
+#if USE(JSVALUE64)
+    unboxDouble(value.gpr(), scratch, valueAsFPR);
+#else
+    unboxDouble(value, valueAsFPR, tempFPR);
+#endif
+    if (invert) {
+        truthy.append(branchDoubleZeroOrNaN(valueAsFPR, tempFPR));
+        done.append(jump());
+    } else {
+        done.append(branchDoubleZeroOrNaN(valueAsFPR, tempFPR));
+        truthy.append(jump());
+    }
+
+    notDouble.link(this);
+#if USE(JSVALUE64)
+    truthy.append(branch64(invert ? NotEqual : Equal, value.gpr(), TrustedImm64(JSValue::encode(jsBoolean(true)))));
+#else
+    auto notBoolean = branchIfNotBoolean(value, InvalidGPRReg);
+    if (invert)
+        truthy.append(notBoolean);
+    else
+        done.append(notBoolean);
+    truthy.append(branch32(invert ? Equal : NotEqual, value.payloadGPR(), TrustedImm32(0)));
+#endif
 
     done.link(this);
+
+    return truthy;
 }
 
 #if ENABLE(WEBASSEMBLY)
index 1e387f6..205ffef 100644 (file)
@@ -1758,6 +1758,15 @@ public:
         storePtr(TrustedImmPtr(nullptr), Address(resultGPR, JSObject::butterflyOffset()));
     }
 
+    JumpList branchIfValue(VM&, JSValueRegs value, GPRReg scratch, GPRReg scratchIfShouldCheckMasqueradesAsUndefined, FPRReg, FPRReg, bool shouldCheckMasqueradesAsUndefined, JSGlobalObject*, bool negateResult);
+    JumpList branchIfTruthy(VM& vm, JSValueRegs value, GPRReg scratch, GPRReg scratchIfShouldCheckMasqueradesAsUndefined, FPRReg scratchFPR0, FPRReg scratchFPR1, bool shouldCheckMasqueradesAsUndefined, JSGlobalObject* globalObject)
+    {
+        return branchIfValue(vm, value, scratch, scratchIfShouldCheckMasqueradesAsUndefined, scratchFPR0, scratchFPR1, shouldCheckMasqueradesAsUndefined, globalObject, false);
+    }
+    JumpList branchIfFalsey(VM& vm, JSValueRegs value, GPRReg scratch, GPRReg scratchIfShouldCheckMasqueradesAsUndefined, FPRReg scratchFPR0, FPRReg scratchFPR1, bool shouldCheckMasqueradesAsUndefined, JSGlobalObject* globalObject)
+    {
+        return branchIfValue(vm, value, scratch, scratchIfShouldCheckMasqueradesAsUndefined, scratchFPR0, scratchFPR1, shouldCheckMasqueradesAsUndefined, globalObject, true);
+    }
     void emitConvertValueToBoolean(VM&, JSValueRegs value, GPRReg result, GPRReg scratchIfShouldCheckMasqueradesAsUndefined, FPRReg, FPRReg, bool shouldCheckMasqueradesAsUndefined, JSGlobalObject*, bool negateResult = false);
     
     template<typename ClassType>
index cf203c6..593d5bf 100644 (file)
@@ -305,6 +305,7 @@ namespace JSC {
         void addSlowCase(const JumpList&);
         void addSlowCase();
         void addJump(Jump, int);
+        void addJump(const JumpList&, int);
         void emitJumpSlowToHot(Jump, int);
 
         void compileOpCall(OpcodeID, Instruction*, unsigned callLinkInfoIndex);
index 723b73e..2bb67f1 100644 (file)
@@ -235,6 +235,14 @@ ALWAYS_INLINE void JIT::addJump(Jump jump, int relativeOffset)
     m_jmpTable.append(JumpTable(jump, m_bytecodeOffset + relativeOffset));
 }
 
+ALWAYS_INLINE void JIT::addJump(const JumpList& jumpList, int relativeOffset)
+{
+    ASSERT(m_bytecodeOffset != std::numeric_limits<unsigned>::max()); // This method should only be called during hot/cold path generation, so that m_bytecodeOffset is set.
+
+    for (auto& jump : jumpList.jumps())
+        addJump(jump, relativeOffset);
+}
+
 ALWAYS_INLINE void JIT::emitJumpSlowToHot(Jump jump, int relativeOffset)
 {
     ASSERT(m_bytecodeOffset != std::numeric_limits<unsigned>::max()); // This method should only be called during hot/cold path generation, so that m_bytecodeOffset is set.
index 459d1a2..146dacf 100644 (file)
@@ -351,14 +351,12 @@ void JIT::emit_op_jfalse(Instruction* currentInstruction)
     unsigned target = currentInstruction[2].u.operand;
 
     GPRReg value = regT0;
-    GPRReg result = regT1;
-    GPRReg scratch = regT2;
+    GPRReg scratch1 = regT1;
+    GPRReg scratch2 = regT2;
     bool shouldCheckMasqueradesAsUndefined = true;
 
     emitGetVirtualRegister(currentInstruction[1].u.operand, value);
-    emitConvertValueToBoolean(*vm(), JSValueRegs(value), result, scratch, fpRegT0, fpRegT1, shouldCheckMasqueradesAsUndefined, m_codeBlock->globalObject());
-
-    addJump(branchTest32(Zero, result), target);
+    addJump(branchIfFalsey(*vm(), JSValueRegs(value), scratch1, scratch2, fpRegT0, fpRegT1, shouldCheckMasqueradesAsUndefined, m_codeBlock->globalObject()), target);
 }
 
 void JIT::emit_op_jeq_null(Instruction* currentInstruction)
@@ -442,12 +440,11 @@ void JIT::emit_op_jtrue(Instruction* currentInstruction)
     unsigned target = currentInstruction[2].u.operand;
 
     GPRReg value = regT0;
-    GPRReg result = regT1;
-    GPRReg scratch = regT2;
+    GPRReg scratch1 = regT1;
+    GPRReg scratch2 = regT2;
     bool shouldCheckMasqueradesAsUndefined = true;
     emitGetVirtualRegister(currentInstruction[1].u.operand, value);
-    emitConvertValueToBoolean(*vm(), JSValueRegs(value), result, scratch, fpRegT0, fpRegT1, shouldCheckMasqueradesAsUndefined, m_codeBlock->globalObject());
-    addJump(branchTest32(NonZero, result), target);
+    addJump(branchIfTruthy(*vm(), JSValueRegs(value), scratch1, scratch2, fpRegT0, fpRegT1, shouldCheckMasqueradesAsUndefined, m_codeBlock->globalObject()), target);
 }
 
 void JIT::emit_op_neq(Instruction* currentInstruction)
index eee9d28..f4c016b 100644 (file)
@@ -347,12 +347,10 @@ void JIT::emit_op_jfalse(Instruction* currentInstruction)
     emitLoad(cond, regT1, regT0);
 
     JSValueRegs value(regT1, regT0);
-    GPRReg scratch = regT2;
-    GPRReg result = regT3;
+    GPRReg scratch1 = regT2;
+    GPRReg scratch2 = regT3;
     bool shouldCheckMasqueradesAsUndefined = true;
-    emitConvertValueToBoolean(*vm(), value, result, scratch, fpRegT0, fpRegT1, shouldCheckMasqueradesAsUndefined, m_codeBlock->globalObject());
-
-    addJump(branchTest32(Zero, result), target);
+    addJump(branchIfFalsey(*vm(), value, scratch1, scratch2, fpRegT0, fpRegT1, shouldCheckMasqueradesAsUndefined, m_codeBlock->globalObject()), target);
 }
 
 void JIT::emit_op_jtrue(Instruction* currentInstruction)
@@ -363,11 +361,9 @@ void JIT::emit_op_jtrue(Instruction* currentInstruction)
     emitLoad(cond, regT1, regT0);
     bool shouldCheckMasqueradesAsUndefined = true;
     JSValueRegs value(regT1, regT0);
-    GPRReg scratch = regT2;
-    GPRReg result = regT3;
-    emitConvertValueToBoolean(*vm(), value, result, scratch, fpRegT0, fpRegT1, shouldCheckMasqueradesAsUndefined, m_codeBlock->globalObject());
-
-    addJump(branchTest32(NonZero, result), target);
+    GPRReg scratch1 = regT2;
+    GPRReg scratch2 = regT3;
+    addJump(branchIfTruthy(*vm(), value, scratch1, scratch2, fpRegT0, fpRegT1, shouldCheckMasqueradesAsUndefined, m_codeBlock->globalObject()), target);
 }
 
 void JIT::emit_op_jeq_null(Instruction* currentInstruction)