Diff
Modified: trunk/JSTests/ChangeLog (274036 => 274037)
--- trunk/JSTests/ChangeLog 2021-03-06 14:31:56 UTC (rev 274036)
+++ trunk/JSTests/ChangeLog 2021-03-06 14:42:37 UTC (rev 274037)
@@ -1,3 +1,20 @@
+2021-03-06 Alexey Shvayka <[email protected]>
+
+ BooleanConstructor should be inlined in DFG / FTL
+ https://bugs.webkit.org/show_bug.cgi?id=220322
+
+ Reviewed by Yusuke Suzuki.
+
+ Reorganize tests so the every UseKind / needsTypeCheck / invert combination is covered.
+
+ * microbenchmarks/array-filter-boolean-constructor.js: Added.
+ * stress/dfg-branch.js: Added.
+ * stress/dfg-to-boolean.js: Added.
+ * stress/logical-not-masquerades-as-undefined.js: Removed.
+ * stress/logical-not-masquerades.js: Removed.
+ * stress/logical-not.js: Removed.
+ * stress/value-to-boolean.js: Removed.
+
2021-03-05 Tadeu Zagallo <[email protected]>
OpGetPrivateName needs to be listed in FOR_EACH_OPCODE_WITH_VALUE_PROFILE
Added: trunk/JSTests/microbenchmarks/array-filter-boolean-constructor.js (0 => 274037)
--- trunk/JSTests/microbenchmarks/array-filter-boolean-constructor.js (rev 0)
+++ trunk/JSTests/microbenchmarks/array-filter-boolean-constructor.js 2021-03-06 14:42:37 UTC (rev 274037)
@@ -0,0 +1,12 @@
+(function() {
+ var array = new Array(300);
+ for (var i = 0; i < 300; i++)
+ array[i] = i % 10;
+
+ var compacted;
+ for (var j = 0; j < 30000; j++)
+ compacted = array.filter(Boolean);
+
+ if (compacted.length !== 270)
+ throw new Error("Bad assert!");
+})();
Added: trunk/JSTests/stress/dfg-branch.js (0 => 274037)
--- trunk/JSTests/stress/dfg-branch.js (rev 0)
+++ trunk/JSTests/stress/dfg-branch.js 2021-03-06 14:42:37 UTC (rev 274037)
@@ -0,0 +1,159 @@
+//@ if $buildType == "release" then runDefault else skip end
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`Bad value: ${actual}!`);
+}
+noInline(shouldBe);
+
+(function ObjectOrOtherUse() {
+ function test1(x) { if (x) return true; else return false; }
+ function test2(x) { return x ? true : false; }
+ function testInvert(x) { if (!x) return true; else return false; }
+
+ noInline(test1);
+ noInline(test2);
+ noInline(testInvert);
+
+ for (let i = 0; i < 1e5; i++) {
+ shouldBe(test1(undefined), false);
+ shouldBe(test1({}), true);
+ shouldBe(test2([]), true);
+ shouldBe(testInvert(new String("")), false);
+ shouldBe(testInvert(null), true);
+
+ shouldBe(test1(makeMasquerader()), false);
+ shouldBe(test2(makeMasquerader()), false);
+ shouldBe(testInvert(makeMasquerader()), true);
+ }
+})();
+
+(function Int32Use() {
+ function test1(x) { if (x) return true; else return false; }
+ function test2(x) { return x ? true : false; }
+ function testInvert(x) { if (!x) return true; else return false; }
+
+ noInline(test1);
+ noInline(test2);
+ noInline(testInvert);
+
+ for (let i = 0; i < 1e5; i++) {
+ shouldBe(test1(1), true);
+ shouldBe(test2(0), false);
+ shouldBe(test2(2147483647), true);
+ shouldBe(testInvert(-2147483648), false);
+ }
+})();
+
+(function DoubleRepUse() {
+ function test1(x) { if (x) return true; else return false; }
+ function test2(x) { return x ? true : false; }
+ function testInvert(x) { if (!x) return true; else return false; }
+
+ noInline(test1);
+ noInline(test2);
+ noInline(testInvert);
+
+ for (let i = 0; i < 1e5; i++) {
+ shouldBe(test1(0.5), true);
+ shouldBe(test1(-Infinity), true);
+ shouldBe(test2(-0), false);
+ shouldBe(testInvert(NaN), true);
+ }
+})();
+
+(function BooleanUse() {
+ function test1(x) { if (x) return true; else return false; }
+ function test2(x) { return x ? true : false; }
+ function testInvert(x) { if (!x) return true; else return false; }
+ function testTypeCheck(x) { return test1(x) ? true : false; }
+
+ noInline(test1);
+ noInline(test2);
+ noInline(testInvert);
+ noInline(testTypeCheck);
+
+ for (let i = 0; i < 1e5; i++) {
+ // needsTypeCheck: false
+ shouldBe(test1(true), true);
+ shouldBe(test2(false), false);
+ shouldBe(testInvert(true), false);
+
+ // needsTypeCheck: true
+ shouldBe(testTypeCheck(true), true);
+ }
+})();
+
+(function UntypedUse() {
+ function test1(x) { if (x) return true; else return false; }
+ function test2(x) { return x ? true : false; }
+ function testInvert(x) { if (!x) return true; else return false; }
+
+ noInline(test1);
+ noInline(test2);
+ noInline(testInvert);
+
+ const testCases = [
+ [undefined, false],
+ [null, false],
+ [0, false],
+ [2147483647, true],
+ [false, false],
+ [true, true],
+ ["" + "" + "", false],
+ ["foo", true],
+ [-0, false],
+ [3.14, true],
+ [NaN, false],
+ [Infinity, true],
+ [Symbol(), true],
+ [{}, true],
+ [[], true],
+ [function() {}, true],
+ [makeMasquerader(), false],
+ ];
+
+ for (let i = 0; i < 1e5; i++) {
+ for (const [value, expected] of testCases) {
+ shouldBe(test1(value), expected);
+ shouldBe(test2(value), expected);
+ shouldBe(testInvert(value), !expected);
+ }
+ }
+})();
+
+(function StringUse() {
+ function test1(x) { if (x) return true; else return false; }
+ function test2(x) { return x ? true : false; }
+ function testInvert(x) { if (!x) return true; else return false; }
+
+ noInline(test1);
+ noInline(test2);
+ noInline(testInvert);
+
+ for (let i = 0; i < 1e5; i++) {
+ shouldBe(test1("\0"), true);
+ shouldBe(test2(""), false);
+ shouldBe(testInvert("" + "" + ""), true);
+ shouldBe(testInvert("foo" + "foo" + "foo"), false);
+ }
+})();
+
+(function StringOrOtherUse() {
+ function test1(x) { if (x) return true; else return false; }
+ function test2(x) { return x ? true : false; }
+ function testInvert(x) { if (!x) return true; else return false; }
+
+ noInline(test1);
+ noInline(test2);
+ noInline(testInvert);
+
+ for (let i = 0; i < 1e5; i++) {
+ shouldBe(test1("" + "" + ""), false);
+ shouldBe(test1(null), false);
+ shouldBe(test2("foo"), true);
+ shouldBe(test2(undefined), false);
+ shouldBe(testInvert(""), true);
+ shouldBe(testInvert(null), true);
+ }
+})();
Added: trunk/JSTests/stress/dfg-to-boolean.js (0 => 274037)
--- trunk/JSTests/stress/dfg-to-boolean.js (rev 0)
+++ trunk/JSTests/stress/dfg-to-boolean.js 2021-03-06 14:42:37 UTC (rev 274037)
@@ -0,0 +1,159 @@
+//@ if $buildType == "release" then runDefault else skip end
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`Bad value: ${actual}!`);
+}
+noInline(shouldBe);
+
+(function ObjectOrOtherUse() {
+ function test1(x) { return !!x; }
+ function test2(x) { return Boolean(x); }
+ function testInvert(x) { return !x; }
+
+ noInline(test1);
+ noInline(test2);
+ noInline(testInvert);
+
+ for (let i = 0; i < 1e5; i++) {
+ shouldBe(test1(undefined), false);
+ shouldBe(test1({}), true);
+ shouldBe(test2([]), true);
+ shouldBe(testInvert(new String("")), false);
+ shouldBe(testInvert(null), true);
+
+ shouldBe(test1(makeMasquerader()), false);
+ shouldBe(test2(makeMasquerader()), false);
+ shouldBe(testInvert(makeMasquerader()), true);
+ }
+})();
+
+(function Int32Use() {
+ function test1(x) { return !!x; }
+ function test2(x) { return Boolean(x); }
+ function testInvert(x) { return !x; }
+
+ noInline(test1);
+ noInline(test2);
+ noInline(testInvert);
+
+ for (let i = 0; i < 1e5; i++) {
+ shouldBe(test1(1), true);
+ shouldBe(test2(0), false);
+ shouldBe(test2(2147483647), true);
+ shouldBe(testInvert(-2147483648), false);
+ }
+})();
+
+(function DoubleRepUse() {
+ function test1(x) { return !!x; }
+ function test2(x) { return Boolean(x); }
+ function testInvert(x) { return !x; }
+
+ noInline(test1);
+ noInline(test2);
+ noInline(testInvert);
+
+ for (let i = 0; i < 1e5; i++) {
+ shouldBe(test1(0.5), true);
+ shouldBe(test1(-Infinity), true);
+ shouldBe(test2(-0), false);
+ shouldBe(testInvert(NaN), true);
+ }
+})();
+
+(function BooleanUse() {
+ function test1(x) { return !!x; }
+ function test2(x) { return Boolean(x); }
+ function testInvert(x) { return !x; }
+
+ noInline(test1);
+ noInline(test2);
+ noInline(testInvert);
+
+ for (let i = 0; i < 1e5; i++) {
+ // needsTypeCheck: false
+ shouldBe(test1(true), true);
+ shouldBe(test2(false), false);
+ shouldBe(testInvert(true), false);
+
+ // needsTypeCheck: true
+ shouldBe(!test1(false), true);
+ shouldBe(Boolean(test2(true)), true);
+ shouldBe(!testInvert(false), false);
+ }
+})();
+
+(function UntypedUse() {
+ function test1(x) { return !!x; }
+ function test2(x) { return Boolean(x); }
+ function testInvert(x) { return !x; }
+
+ noInline(test1);
+ noInline(test2);
+ noInline(testInvert);
+
+ const testCases = [
+ [undefined, false],
+ [null, false],
+ [0, false],
+ [2147483647, true],
+ [false, false],
+ [true, true],
+ ["" + "" + "", false],
+ ["foo", true],
+ [-0, false],
+ [3.14, true],
+ [NaN, false],
+ [Infinity, true],
+ [Symbol(), true],
+ [{}, true],
+ [[], true],
+ [function() {}, true],
+ [makeMasquerader(), false],
+ ];
+
+ for (let i = 0; i < 1e5; i++) {
+ for (const [value, expected] of testCases) {
+ shouldBe(test1(value), expected);
+ shouldBe(test2(value), expected);
+ shouldBe(testInvert(value), !expected);
+ }
+ }
+})();
+
+(function StringUse() {
+ function test1(x) { return !!x; }
+ function test2(x) { return Boolean(x); }
+ function testInvert(x) { return !x; }
+
+ noInline(test1);
+ noInline(test2);
+ noInline(testInvert);
+
+ for (let i = 0; i < 1e5; i++) {
+ shouldBe(test1("\0"), true);
+ shouldBe(test2(""), false);
+ shouldBe(testInvert("" + "" + ""), true);
+ shouldBe(testInvert("foo" + "foo" + "foo"), false);
+ }
+})();
+
+(function StringOrOtherUse() {
+ function test1(x) { return !!x; }
+ function test2(x) { return Boolean(x); }
+ function testInvert(x) { return !x; }
+
+ noInline(test1);
+ noInline(test2);
+ noInline(testInvert);
+
+ for (let i = 0; i < 1e5; i++) {
+ shouldBe(test1("" + "" + ""), false);
+ shouldBe(test1(null), false);
+ shouldBe(test2("foo"), true);
+ shouldBe(test2(undefined), false);
+ shouldBe(testInvert(""), true);
+ shouldBe(testInvert(null), true);
+ }
+})();
Deleted: trunk/JSTests/stress/logical-not-masquerades-as-undefined.js (274036 => 274037)
--- trunk/JSTests/stress/logical-not-masquerades-as-undefined.js 2021-03-06 14:31:56 UTC (rev 274036)
+++ trunk/JSTests/stress/logical-not-masquerades-as-undefined.js 2021-03-06 14:42:37 UTC (rev 274037)
@@ -1,35 +0,0 @@
-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);
-}
Deleted: trunk/JSTests/stress/logical-not-masquerades.js (274036 => 274037)
--- trunk/JSTests/stress/logical-not-masquerades.js 2021-03-06 14:31:56 UTC (rev 274036)
+++ trunk/JSTests/stress/logical-not-masquerades.js 2021-03-06 14:42:37 UTC (rev 274037)
@@ -1,33 +0,0 @@
-function foo(value) {
- return !!value;
-}
-
-noInline(foo);
-
-var tests = [
- [0, false],
- [1, true],
- [0/0, false],
- [0/-1, false],
- [0.0, false],
- ["", false],
- ["f", true],
- ["hello", true],
- [{}, true],
- [[], true],
- [null, false],
- [void 0, false],
- [false, false],
- [true, true],
- [makeMasquerader(), false]
-];
-
-for (var i = 0; i < 10000; ++i) {
- for (var j = 0; j < tests.length; ++j) {
- var input = tests[j][0];
- var expected = tests[j][1];
- var result = foo(input);
- if (result !== expected)
- throw "Error: bad result for " + input + ": " + result;
- }
-}
Deleted: trunk/JSTests/stress/logical-not.js (274036 => 274037)
--- trunk/JSTests/stress/logical-not.js 2021-03-06 14:31:56 UTC (rev 274036)
+++ trunk/JSTests/stress/logical-not.js 2021-03-06 14:42:37 UTC (rev 274037)
@@ -1,34 +0,0 @@
-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);
-}
Deleted: trunk/JSTests/stress/value-to-boolean.js (274036 => 274037)
--- trunk/JSTests/stress/value-to-boolean.js 2021-03-06 14:31:56 UTC (rev 274036)
+++ trunk/JSTests/stress/value-to-boolean.js 2021-03-06 14:42:37 UTC (rev 274037)
@@ -1,65 +0,0 @@
-//@ if $buildType == "release" then runDefault else skip end
-
-function assert(b) {
- if (!b)
- throw new Error("Bad assertion")
-}
-noInline(assert);
-
-let tests = [
- [true, true],
- [false, false],
- ["", false],
- ["" + "" + "", false],
- ["foo", true],
- ["foo" + "bar", true],
- [{}, true],
- [Symbol(), true],
- [undefined, false],
- [null, false],
- [0, false],
- [-0, false],
- [+0, false],
- [NaN, false],
- [10, true],
- [10.2012, true],
- [function() { }, true],
- [new String("foo"), true],
- [new String(""), true],
- [new String, true]
-];
-
-function test1(c) {
- return !!c;
-}
-noInline(test1);
-
-function test2(c) {
- if (c)
- return true;
- return false;
-}
-noInline(test2);
-
-function test3(c) {
- if (!c)
- return false;
- return true;
-}
-noInline(test3);
-
-let testFunctions = [test1, test2, test3];
-
-for (let testFunction of testFunctions) {
- for (let i = 0; i < 10000; i++) {
- let item = tests[i % tests.length];
- assert(testFunction(item[0]) === item[1]);
- }
-}
-
-let masquerader = makeMasquerader();
-for (let testFunction of testFunctions) {
- for (let i = 0; i < 10000; i++) {
- assert(testFunction(masquerader) === false);
- }
-}
Modified: trunk/Source/_javascript_Core/ChangeLog (274036 => 274037)
--- trunk/Source/_javascript_Core/ChangeLog 2021-03-06 14:31:56 UTC (rev 274036)
+++ trunk/Source/_javascript_Core/ChangeLog 2021-03-06 14:42:37 UTC (rev 274037)
@@ -1,3 +1,68 @@
+2021-03-06 Alexey Shvayka <[email protected]>
+
+ BooleanConstructor should be inlined in DFG / FTL
+ https://bugs.webkit.org/show_bug.cgi?id=220322
+
+ Reviewed by Yusuke Suzuki.
+
+ `array.filter(Boolean)` is a rather popular idiom for removing falsy items from an array.
+ Also, `Boolean(X)` is sometimes used for explicit type casting.
+
+ This patch introduces ToBoolean DFG node and reorganizes compileLogicalNot(node) into
+ compileToBoolean(node, bool invert), leveraging already existing emitConvertValueToBoolean().
+
+ This approach is better than emitting LogicalNot<KnownBooleanUse>(LogicalNot(X)) as it results
+ in cleaner DFG node tree and is ~7% faster w/o FTL. Also, it enables adding a op_to_boolean
+ bytecode that will be generated for very common `!!X` patterns, reducing instruction count.
+
+ Just as LogicalNot, BooleanConstructor should handle masquerader objects, because Annex B
+ patches ToBoolean abstract op [1], preventing us from emitting simpler code.
+
+ This change advances provided microbenchmark by 110%, and is neutral for other ToBoolean cases.
+
+ [1]: https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot-to-boolean
+
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::handleConstantInternalFunction):
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize):
+ * dfg/DFGDoesGC.cpp:
+ (JSC::DFG::doesGC):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGMayExit.cpp:
+ * dfg/DFGNodeType.h:
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ * dfg/DFGSafeToExecute.h:
+ (JSC::DFG::safeToExecute):
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::compileToBooleanString):
+ (JSC::DFG::SpeculativeJIT::compileToBooleanStringOrOther):
+ (JSC::DFG::SpeculativeJIT::compileStringZeroLength): Deleted.
+ (JSC::DFG::SpeculativeJIT::compileLogicalNotStringOrOther): Deleted.
+ * dfg/DFGSpeculativeJIT.h:
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compileToBooleanObjectOrOther):
+ (JSC::DFG::SpeculativeJIT::compileToBoolean):
+ (JSC::DFG::SpeculativeJIT::compile):
+ (JSC::DFG::SpeculativeJIT::compileObjectOrOtherLogicalNot): Deleted.
+ (JSC::DFG::SpeculativeJIT::compileLogicalNot): Deleted.
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compileToBooleanObjectOrOther):
+ (JSC::DFG::SpeculativeJIT::compileToBoolean):
+ (JSC::DFG::SpeculativeJIT::compile):
+ (JSC::DFG::SpeculativeJIT::compileObjectOrOtherLogicalNot): Deleted.
+ (JSC::DFG::SpeculativeJIT::compileLogicalNot): Deleted.
+ * dfg/DFGWatchpointCollectionPhase.cpp:
+ (JSC::DFG::WatchpointCollectionPhase::handle):
+ * ftl/FTLCapabilities.cpp:
+ (JSC::FTL::canCompile):
+ * ftl/FTLLowerDFGToB3.cpp:
+ (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+ (JSC::FTL::DFG::LowerDFGToB3::compileToBoolean):
+
2021-03-06 Tim Horton <[email protected]>
<model> should create a model-owning compositing layer
Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (274036 => 274037)
--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2021-03-06 14:31:56 UTC (rev 274036)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2021-03-06 14:42:37 UTC (rev 274037)
@@ -1348,18 +1348,18 @@
executeDoubleUnaryOpEffects(node, arithUnaryFunction(node->arithUnaryType()));
break;
+ case ToBoolean:
case LogicalNot: {
- switch (booleanResult(node, forNode(node->child1()))) {
- case TriState::True:
- setConstant(node, jsBoolean(false));
- break;
- case TriState::False:
- setConstant(node, jsBoolean(true));
- break;
- case TriState::Indeterminate:
+ TriState result = booleanResult(node, forNode(node->child1()));
+ if (result == TriState::Indeterminate) {
setNonCellTypeForNode(node, SpecBoolean);
break;
}
+
+ bool resultAsBool = result == TriState::True;
+ if (node->op() == LogicalNot)
+ resultAsBool = !resultAsBool;
+ setConstant(node, jsBoolean(resultAsBool));
break;
}
Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (274036 => 274037)
--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2021-03-06 14:31:56 UTC (rev 274036)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2021-03-06 14:42:37 UTC (rev 274037)
@@ -31,6 +31,7 @@
#include "ArithProfile.h"
#include "ArrayConstructor.h"
#include "ArrayPrototype.h"
+#include "BooleanConstructor.h"
#include "BuiltinNames.h"
#include "ByValInfo.h"
#include "BytecodeGenerator.h"
@@ -4067,6 +4068,22 @@
return true;
}
+
+ if (function->classInfo(*m_vm) == BooleanConstructor::info()) {
+ if (kind == CodeForConstruct)
+ return false;
+
+ insertChecks();
+
+ Node* resultNode;
+ if (argumentCountIncludingThis <= 1)
+ resultNode = jsConstant(jsBoolean(false));
+ else
+ resultNode = addToGraph(ToBoolean, get(virtualRegisterForArgumentIncludingThis(1, registerOffset)));
+
+ set(result, resultNode);
+ return true;
+ }
if (function->classInfo(*m_vm) == StringConstructor::info()) {
insertChecks();
Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (274036 => 274037)
--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2021-03-06 14:31:56 UTC (rev 274036)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2021-03-06 14:42:37 UTC (rev 274037)
@@ -233,6 +233,7 @@
case NumberIsInteger:
case IsObject:
case IsTypedArrayView:
+ case ToBoolean:
case LogicalNot:
case CheckInBounds:
case DoubleRep:
Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (274036 => 274037)
--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2021-03-06 14:31:56 UTC (rev 274036)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2021-03-06 14:42:37 UTC (rev 274037)
@@ -156,6 +156,7 @@
case IsCellWithType:
case IsTypedArrayView:
case TypeOf:
+ case ToBoolean:
case LogicalNot:
case Jump:
case Branch:
Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (274036 => 274037)
--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2021-03-06 14:31:56 UTC (rev 274036)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2021-03-06 14:42:37 UTC (rev 274037)
@@ -833,6 +833,7 @@
break;
}
+ case ToBoolean:
case LogicalNot: {
if (node->child1()->shouldSpeculateBoolean()) {
if (node->child1()->result() == NodeResultBoolean) {
Modified: trunk/Source/_javascript_Core/dfg/DFGMayExit.cpp (274036 => 274037)
--- trunk/Source/_javascript_Core/dfg/DFGMayExit.cpp 2021-03-06 14:31:56 UTC (rev 274036)
+++ trunk/Source/_javascript_Core/dfg/DFGMayExit.cpp 2021-03-06 14:42:37 UTC (rev 274037)
@@ -93,6 +93,7 @@
case ExtractOSREntryLocal:
case ExtractCatchLocal:
case ClearCatchLocals:
+ case ToBoolean:
case LogicalNot:
case NotifyWrite:
case PutStructure:
Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (274036 => 274037)
--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2021-03-06 14:31:56 UTC (rev 274036)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2021-03-06 14:42:37 UTC (rev 274037)
@@ -415,6 +415,7 @@
macro(IsConstructor, NodeResultBoolean) \
macro(IsTypedArrayView, NodeResultBoolean) \
macro(TypeOf, NodeResultJS) \
+ macro(ToBoolean, NodeResultBoolean) \
macro(LogicalNot, NodeResultBoolean) \
macro(ToPrimitive, NodeResultJS | NodeMustGenerate) \
macro(ToPropertyKey, NodeResultJS | NodeMustGenerate) \
Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (274036 => 274037)
--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2021-03-06 14:31:56 UTC (rev 274036)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2021-03-06 14:42:37 UTC (rev 274037)
@@ -1025,6 +1025,7 @@
case DeleteByVal:
case DeleteById:
case MultiDeleteByOffset:
+ case ToBoolean:
case LogicalNot:
case CompareLess:
case CompareLessEq:
Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (274036 => 274037)
--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2021-03-06 14:31:56 UTC (rev 274036)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2021-03-06 14:42:37 UTC (rev 274037)
@@ -270,6 +270,7 @@
case IsCellWithType:
case IsTypedArrayView:
case TypeOf:
+ case ToBoolean:
case LogicalNot:
case ToString:
case NumberToStringWithValidRadixConstant:
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (274036 => 274037)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2021-03-06 14:31:56 UTC (rev 274036)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2021-03-06 14:42:37 UTC (rev 274037)
@@ -7397,7 +7397,7 @@
unblessedBooleanResult(resultGPR, node, UseChildrenCalledExplicitly);
}
-void SpeculativeJIT::compileStringZeroLength(Node* node)
+void SpeculativeJIT::compileToBooleanString(Node* node, bool invert)
{
SpeculateCellOperand str(this, node->child1());
GPRReg strGPR = str.gpr();
@@ -7409,11 +7409,11 @@
GPRReg eqGPR = eq.gpr();
m_jit.move(TrustedImmPtr::weakPointer(m_jit.graph(), jsEmptyString(vm())), eqGPR);
- m_jit.comparePtr(CCallHelpers::Equal, strGPR, eqGPR, eqGPR);
+ m_jit.comparePtr(invert ? CCallHelpers::Equal : CCallHelpers::NotEqual, strGPR, eqGPR, eqGPR);
unblessedBooleanResult(eqGPR, node);
}
-void SpeculativeJIT::compileLogicalNotStringOrOther(Node* node)
+void SpeculativeJIT::compileToBooleanStringOrOther(Node* node, bool invert)
{
JSValueOperand value(this, node->child1(), ManualOperandSpeculation);
GPRTemporary temp(this);
@@ -7426,17 +7426,16 @@
valueRegs, node->child1(), (~SpecCellCheck) | SpecString, m_jit.branchIfNotString(cellGPR));
m_jit.move(TrustedImmPtr::weakPointer(m_jit.graph(), jsEmptyString(vm())), tempGPR);
- m_jit.comparePtr(CCallHelpers::Equal, cellGPR, tempGPR, tempGPR);
+ m_jit.comparePtr(invert ? CCallHelpers::Equal : CCallHelpers::NotEqual, cellGPR, tempGPR, tempGPR);
auto done = m_jit.jump();
notCell.link(&m_jit);
DFG_TYPE_CHECK(
valueRegs, node->child1(), SpecCellCheck | SpecOther, m_jit.branchIfNotOther(valueRegs, tempGPR));
- m_jit.move(TrustedImm32(1), tempGPR);
+ m_jit.move(invert ? TrustedImm32(1) : TrustedImm32(0), tempGPR);
done.link(&m_jit);
unblessedBooleanResult(tempGPR, node);
-
}
void SpeculativeJIT::emitStringBranch(Edge nodeUse, BasicBlock* taken, BasicBlock* notTaken)
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (274036 => 274037)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h 2021-03-06 14:31:56 UTC (rev 274036)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h 2021-03-06 14:42:37 UTC (rev 274037)
@@ -1169,9 +1169,10 @@
void compileObjectEquality(Node*);
void compileObjectStrictEquality(Edge objectChild, Edge otherChild);
void compileObjectToObjectOrOtherEquality(Edge leftChild, Edge rightChild);
- void compileObjectOrOtherLogicalNot(Edge value);
- void compileLogicalNot(Node*);
- void compileLogicalNotStringOrOther(Node*);
+ void compileToBoolean(Node*, bool invert);
+ void compileToBooleanObjectOrOther(Edge value, bool invert);
+ void compileToBooleanString(Node*, bool invert);
+ void compileToBooleanStringOrOther(Node*, bool invert);
void compileStringEquality(
Node*, GPRReg leftGPR, GPRReg rightGPR, GPRReg lengthGPR,
GPRReg leftTempGPR, GPRReg rightTempGPR, GPRReg leftTemp2GPR,
@@ -1181,7 +1182,6 @@
void compileStringIdentEquality(Node*);
void compileStringToUntypedEquality(Node*, Edge stringEdge, Edge untypedEdge);
void compileStringIdentToNotStringVarEquality(Node*, Edge stringEdge, Edge notStringVarEdge);
- void compileStringZeroLength(Node*);
void compileMiscStrictEq(Node*);
void compileSymbolEquality(Node*);
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (274036 => 274037)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2021-03-06 14:31:56 UTC (rev 274036)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2021-03-06 14:42:37 UTC (rev 274037)
@@ -1458,7 +1458,7 @@
booleanResult(resultPayloadGPR, node);
}
-void SpeculativeJIT::compileObjectOrOtherLogicalNot(Edge nodeUse)
+void SpeculativeJIT::compileToBooleanObjectOrOther(Edge nodeUse, bool invert)
{
JSValueOperand value(this, nodeUse, ManualOperandSpeculation);
GPRTemporary resultPayload(this);
@@ -1504,7 +1504,7 @@
isNotMasqueradesAsUndefined.link(&m_jit);
}
- m_jit.move(TrustedImm32(0), resultPayloadGPR);
+ m_jit.move(invert ? TrustedImm32(0) : TrustedImm32(1), resultPayloadGPR);
MacroAssembler::Jump done = m_jit.jump();
notCell.link(&m_jit);
@@ -1519,7 +1519,7 @@
resultPayloadGPR,
TrustedImm32(JSValue::NullTag)));
}
- m_jit.move(TrustedImm32(1), resultPayloadGPR);
+ m_jit.move(invert ? TrustedImm32(1) : TrustedImm32(0), resultPayloadGPR);
done.link(&m_jit);
@@ -1526,7 +1526,7 @@
booleanResult(resultPayloadGPR, m_currentNode);
}
-void SpeculativeJIT::compileLogicalNot(Node* node)
+void SpeculativeJIT::compileToBoolean(Node* node, bool invert)
{
switch (node->child1().useKind()) {
case BooleanUse:
@@ -1533,13 +1533,18 @@
case KnownBooleanUse: {
SpeculateBooleanOperand value(this, node->child1());
GPRTemporary result(this, Reuse, value);
- m_jit.xor32(TrustedImm32(1), value.gpr(), result.gpr());
+
+ if (invert)
+ m_jit.xor32(TrustedImm32(1), value.gpr(), result.gpr());
+ else
+ m_jit.move(value.gpr(), result.gpr());
+
booleanResult(result.gpr(), node);
return;
}
case ObjectOrOtherUse: {
- compileObjectOrOtherLogicalNot(node->child1());
+ compileToBooleanObjectOrOther(node->child1(), invert);
return;
}
@@ -1546,7 +1551,7 @@
case Int32Use: {
SpeculateInt32Operand value(this, node->child1());
GPRTemporary resultPayload(this, Reuse, value);
- m_jit.compare32(MacroAssembler::Equal, value.gpr(), MacroAssembler::TrustedImm32(0), resultPayload.gpr());
+ m_jit.compare32(invert ? MacroAssembler::Equal : MacroAssembler::NotEqual, value.gpr(), MacroAssembler::TrustedImm32(0), resultPayload.gpr());
booleanResult(resultPayload.gpr(), node);
return;
}
@@ -1555,9 +1560,9 @@
SpeculateDoubleOperand value(this, node->child1());
FPRTemporary scratch(this);
GPRTemporary resultPayload(this);
- m_jit.move(TrustedImm32(0), resultPayload.gpr());
+ m_jit.move(invert ? TrustedImm32(0) : TrustedImm32(1), resultPayload.gpr());
MacroAssembler::Jump nonZero = m_jit.branchDoubleNonZero(value.fpr(), scratch.fpr());
- m_jit.move(TrustedImm32(1), resultPayload.gpr());
+ m_jit.move(invert ? TrustedImm32(1) : TrustedImm32(0), resultPayload.gpr());
nonZero.link(&m_jit);
booleanResult(resultPayload.gpr(), node);
return;
@@ -1574,16 +1579,17 @@
bool shouldCheckMasqueradesAsUndefined = !masqueradesAsUndefinedWatchpointIsStillValid();
JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic);
- bool negateResult = true;
- m_jit.emitConvertValueToBoolean(vm(), arg1.jsValueRegs(), resultGPR, temp.gpr(), valueFPR.fpr(), tempFPR.fpr(), shouldCheckMasqueradesAsUndefined, globalObject, negateResult);
+ m_jit.emitConvertValueToBoolean(vm(), arg1.jsValueRegs(), resultGPR, temp.gpr(), valueFPR.fpr(), tempFPR.fpr(), shouldCheckMasqueradesAsUndefined, globalObject, invert);
booleanResult(resultGPR, node);
return;
}
case StringUse:
- return compileStringZeroLength(node);
+ compileToBooleanString(node, invert);
+ return;
case StringOrOtherUse:
- return compileLogicalNotStringOrOther(node);
+ compileToBooleanStringOrOther(node, invert);
+ return;
default:
RELEASE_ASSERT_NOT_REACHED();
@@ -2159,10 +2165,18 @@
compileArithUnary(node);
break;
- case LogicalNot:
- compileLogicalNot(node);
+ case ToBoolean: {
+ bool invert = false;
+ compileToBoolean(node, invert);
break;
+ }
+ case LogicalNot: {
+ bool invert = true;
+ compileToBoolean(node, invert);
+ break;
+ }
+
case CompareLess:
if (compare(node, JITCompiler::LessThan, JITCompiler::DoubleLessThanAndOrdered, operationCompareLess))
return;
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (274036 => 274037)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2021-03-06 14:31:56 UTC (rev 274036)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2021-03-06 14:42:37 UTC (rev 274037)
@@ -1800,7 +1800,7 @@
unblessedBooleanResult(resultGPR, node);
}
-void SpeculativeJIT::compileObjectOrOtherLogicalNot(Edge nodeUse)
+void SpeculativeJIT::compileToBooleanObjectOrOther(Edge nodeUse, bool invert)
{
JSValueOperand value(this, nodeUse, ManualOperandSpeculation);
GPRTemporary result(this);
@@ -1848,7 +1848,7 @@
isNotMasqueradesAsUndefined.link(&m_jit);
}
- m_jit.move(TrustedImm32(JSValue::ValueFalse), resultGPR);
+ m_jit.move(invert ? TrustedImm32(JSValue::ValueFalse) : TrustedImm32(JSValue::ValueTrue), resultGPR);
MacroAssembler::Jump done = m_jit.jump();
notCell.link(&m_jit);
@@ -1862,7 +1862,7 @@
resultGPR,
MacroAssembler::TrustedImm64(JSValue::ValueNull)));
}
- m_jit.move(TrustedImm32(JSValue::ValueTrue), resultGPR);
+ m_jit.move(invert ? TrustedImm32(JSValue::ValueTrue) : TrustedImm32(JSValue::ValueFalse), resultGPR);
done.link(&m_jit);
@@ -1869,11 +1869,11 @@
jsValueResult(resultGPR, m_currentNode, DataFormatJSBoolean);
}
-void SpeculativeJIT::compileLogicalNot(Node* node)
+void SpeculativeJIT::compileToBoolean(Node* node, bool invert)
{
switch (node->child1().useKind()) {
case ObjectOrOtherUse: {
- compileObjectOrOtherLogicalNot(node->child1());
+ compileToBooleanObjectOrOther(node->child1(), invert);
return;
}
@@ -1880,7 +1880,7 @@
case Int32Use: {
SpeculateInt32Operand value(this, node->child1());
GPRTemporary result(this, Reuse, value);
- m_jit.compare32(MacroAssembler::Equal, value.gpr(), MacroAssembler::TrustedImm32(0), result.gpr());
+ m_jit.compare32(invert ? MacroAssembler::Equal : MacroAssembler::NotEqual, value.gpr(), MacroAssembler::TrustedImm32(0), result.gpr());
m_jit.or32(TrustedImm32(JSValue::ValueFalse), result.gpr());
jsValueResult(result.gpr(), node, DataFormatJSBoolean);
return;
@@ -1890,9 +1890,9 @@
SpeculateDoubleOperand value(this, node->child1());
FPRTemporary scratch(this);
GPRTemporary result(this);
- m_jit.move(TrustedImm32(JSValue::ValueFalse), result.gpr());
+ m_jit.move(invert ? TrustedImm32(JSValue::ValueFalse) : TrustedImm32(JSValue::ValueTrue), result.gpr());
MacroAssembler::Jump nonZero = m_jit.branchDoubleNonZero(value.fpr(), scratch.fpr());
- m_jit.xor32(TrustedImm32(true), result.gpr());
+ m_jit.move(invert ? TrustedImm32(JSValue::ValueTrue) : TrustedImm32(JSValue::ValueFalse), result.gpr());
nonZero.link(&m_jit);
jsValueResult(result.gpr(), node, DataFormatJSBoolean);
return;
@@ -1904,8 +1904,10 @@
SpeculateBooleanOperand value(this, node->child1());
GPRTemporary result(this, Reuse, value);
- m_jit.move(value.gpr(), result.gpr());
- m_jit.xor64(TrustedImm32(true), result.gpr());
+ if (invert)
+ m_jit.xor32(TrustedImm32(1), value.gpr(), result.gpr());
+ else
+ m_jit.move(value.gpr(), result.gpr());
jsValueResult(result.gpr(), node, DataFormatJSBoolean);
return;
@@ -1919,7 +1921,7 @@
typeCheck(
JSValueRegs(value.gpr()), node->child1(), SpecBoolean, m_jit.branchTest64(
JITCompiler::NonZero, result.gpr(), TrustedImm32(static_cast<int32_t>(~1))));
- m_jit.xor64(TrustedImm32(JSValue::ValueTrue), result.gpr());
+ m_jit.xor64(invert ? TrustedImm32(JSValue::ValueTrue) : TrustedImm32(JSValue::ValueFalse), result.gpr());
// If we add a DataFormatBool, we should use it here.
jsValueResult(result.gpr(), node, DataFormatJSBoolean);
@@ -1944,17 +1946,18 @@
scratch.emplace(this);
scratchGPR = scratch->gpr();
}
- bool negateResult = true;
- m_jit.emitConvertValueToBoolean(vm(), JSValueRegs(arg1GPR), resultGPR, scratchGPR, valueFPR.fpr(), tempFPR.fpr(), shouldCheckMasqueradesAsUndefined, globalObject, negateResult);
+ m_jit.emitConvertValueToBoolean(vm(), JSValueRegs(arg1GPR), resultGPR, scratchGPR, valueFPR.fpr(), tempFPR.fpr(), shouldCheckMasqueradesAsUndefined, globalObject, invert);
m_jit.or32(TrustedImm32(JSValue::ValueFalse), resultGPR);
jsValueResult(resultGPR, node, DataFormatJSBoolean);
return;
}
case StringUse:
- return compileStringZeroLength(node);
+ compileToBooleanString(node, invert);
+ return;
case StringOrOtherUse:
- return compileLogicalNotStringOrOther(node);
+ compileToBooleanStringOrOther(node, invert);
+ return;
default:
DFG_CRASH(m_jit.graph(), node, "Bad use kind");
@@ -2529,10 +2532,18 @@
compileArithUnary(node);
break;
- case LogicalNot:
- compileLogicalNot(node);
+ case ToBoolean: {
+ bool invert = false;
+ compileToBoolean(node, invert);
break;
+ }
+ case LogicalNot: {
+ bool invert = true;
+ compileToBoolean(node, invert);
+ break;
+ }
+
case CompareLess:
if (compare(node, JITCompiler::LessThan, JITCompiler::DoubleLessThanAndOrdered, operationCompareLess))
return;
Modified: trunk/Source/_javascript_Core/dfg/DFGWatchpointCollectionPhase.cpp (274036 => 274037)
--- trunk/Source/_javascript_Core/dfg/DFGWatchpointCollectionPhase.cpp 2021-03-06 14:31:56 UTC (rev 274036)
+++ trunk/Source/_javascript_Core/dfg/DFGWatchpointCollectionPhase.cpp 2021-03-06 14:42:37 UTC (rev 274037)
@@ -83,6 +83,7 @@
handleMasqueradesAsUndefined();
break;
+ case ToBoolean:
case LogicalNot:
case Branch:
switch (m_node->child1().useKind()) {
Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (274036 => 274037)
--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2021-03-06 14:31:56 UTC (rev 274036)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2021-03-06 14:42:37 UTC (rev 274037)
@@ -195,6 +195,7 @@
case LoadVarargs:
case ValueToInt32:
case Branch:
+ case ToBoolean:
case LogicalNot:
case AssertInBounds:
case CheckInBounds:
Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (274036 => 274037)
--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2021-03-06 14:31:56 UTC (rev 274036)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2021-03-06 14:42:37 UTC (rev 274037)
@@ -1320,6 +1320,9 @@
case SameValue:
compileSameValue();
break;
+ case ToBoolean:
+ compileToBoolean();
+ break;
case LogicalNot:
compileLogicalNot();
break;
@@ -9637,6 +9640,11 @@
setBoolean(vmCall(Int32, operationSameValue, weakPointer(globalObject), lowJSValue(m_node->child1()), lowJSValue(m_node->child2())));
}
+ void compileToBoolean()
+ {
+ setBoolean(boolify(m_node->child1()));
+ }
+
void compileLogicalNot()
{
setBoolean(m_out.logicalNot(boolify(m_node->child1())));