haikuwebkit/JSTests/stress/compare-eq-on-null-and-unde...

175 lines
4.6 KiB
JavaScript
Raw Permalink Normal View History

[JSC] Optimize more cases of something-compared-to-null/undefined https://bugs.webkit.org/show_bug.cgi?id=148157 Patch by Benjamin Poulain <bpoulain@apple.com> on 2015-08-18 Reviewed by Geoffrey Garen and Filip Pizlo. Source/JavaScriptCore: CompareEq is fairly trivial if you assert one of the operands is either null or undefined. Under those conditions, the only way to have "true" is to have the other operand be null/undefined or have an object that masquerades to undefined. JSC already had a fast path in CompareEqConstant. With this patch, I generalize this fast path to more cases and try to eliminate the checks whenever possible. CompareEq now does the job of CompareEqConstant. If any operand can be proved to be undefined/other, its edge is set to OtherUse. Whenever any edge is OtherUse, we generate the fast code we had for CompareEqConstant. The AbstractInterpreter has additional checks to reduce the node to a constant whenever possible. There are two additional changes in this patch: -The Fixup Phase tries to set edges to OtherUse early. This is done correctly in ConstantFoldingPhase but setting it up early helps the phases relying on Clobberize. -The codegen for CompareEqConstant was improved. The reason is the comparison for ObjectOrOther could be faster just because the codegen was better. * dfg/DFGAbstractInterpreterInlines.h: (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGClobberize.h: (JSC::DFG::clobberize): Deleted. * dfg/DFGConstantFoldingPhase.cpp: (JSC::DFG::ConstantFoldingPhase::foldConstants): * dfg/DFGDoesGC.cpp: (JSC::DFG::doesGC): Deleted. * dfg/DFGFixupPhase.cpp: (JSC::DFG::FixupPhase::fixupNode): * dfg/DFGNode.h: (JSC::DFG::Node::isUndefinedOrNullConstant): * dfg/DFGNodeType.h: * dfg/DFGPredictionPropagationPhase.cpp: (JSC::DFG::PredictionPropagationPhase::propagate): Deleted. * dfg/DFGSafeToExecute.h: (JSC::DFG::safeToExecute): Deleted. * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compilePeepHoleBranch): (JSC::DFG::SpeculativeJIT::compare): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::isKnownNotOther): * dfg/DFGSpeculativeJIT32_64.cpp: (JSC::DFG::SpeculativeJIT::nonSpeculativeNonPeepholeCompareNullOrUndefined): (JSC::DFG::SpeculativeJIT::nonSpeculativePeepholeBranchNullOrUndefined): (JSC::DFG::SpeculativeJIT::nonSpeculativeNonPeepholeCompareNull): Deleted. (JSC::DFG::SpeculativeJIT::nonSpeculativePeepholeBranchNull): Deleted. (JSC::DFG::SpeculativeJIT::nonSpeculativeCompareNull): Deleted. (JSC::DFG::SpeculativeJIT::compile): Deleted. * dfg/DFGSpeculativeJIT64.cpp: (JSC::DFG::SpeculativeJIT::nonSpeculativeNonPeepholeCompareNullOrUndefined): (JSC::DFG::SpeculativeJIT::nonSpeculativePeepholeBranchNullOrUndefined): (JSC::DFG::SpeculativeJIT::nonSpeculativeNonPeepholeCompareNull): Deleted. (JSC::DFG::SpeculativeJIT::nonSpeculativePeepholeBranchNull): Deleted. (JSC::DFG::SpeculativeJIT::nonSpeculativeCompareNull): Deleted. (JSC::DFG::SpeculativeJIT::compile): Deleted. * dfg/DFGValidate.cpp: (JSC::DFG::Validate::validate): Deleted. * dfg/DFGWatchpointCollectionPhase.cpp: (JSC::DFG::WatchpointCollectionPhase::handle): * ftl/FTLCapabilities.cpp: (JSC::FTL::canCompile): * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCompareEq): (JSC::FTL::DFG::LowerDFGToLLVM::compileNode): Deleted. (JSC::FTL::DFG::LowerDFGToLLVM::compileCompareEqConstant): Deleted. * tests/stress/compare-eq-on-null-and-undefined-non-peephole.js: Added. (string_appeared_here.useForMath): (testUseForMath): * tests/stress/compare-eq-on-null-and-undefined-optimized-in-constant-folding.js: Added. (string_appeared_here.unreachableCodeTest): (inlinedCompareToNull): (inlinedComparedToUndefined): (warmupInlineFunctions): (testInlineFunctions): * tests/stress/compare-eq-on-null-and-undefined.js: Added. (string_appeared_here.compareConstants): (opaqueNull): (opaqueUndefined): (compareConstantsAndDynamicValues): (compareDynamicValues): (compareDynamicValueToItself): (arrayTesting): (opaqueCompare1): (testNullComparatorUpdate): (opaqueCompare2): (testUndefinedComparatorUpdate): (opaqueCompare3): (testNullAndUndefinedComparatorUpdate): LayoutTests: * js/dom/document-all-watchpoint-covers-eliminated-compare-eq-expected.txt: Added. * js/dom/document-all-watchpoint-covers-eliminated-compare-eq.html: Added. * js/dom/script-tests/document-all-watchpoint-covers-eliminated-compare-eq.js: Added. (compareFunction): Canonical link: https://commits.webkit.org/166282@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@188624 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-08-19 04:09:12 +00:00
"use strict"
// Trivial cases: everything is monomorphic and super predictable.
function compareConstants()
{
return (null == null) && (null == undefined) && (undefined == null);
}
noInline(compareConstants);
for (let i = 0; i < 1e4; ++i) {
if (!compareConstants())
throw "Failed to compareConstants().";
}
function opaqueNull() {
return null;
}
noInline(opaqueNull);
function opaqueUndefined() {
return undefined;
}
noInline(opaqueUndefined);
function compareConstantsAndDynamicValues()
{
return ((null == opaqueNull())
&& (opaqueNull() == null)
&& (undefined == opaqueNull())
&& (opaqueNull() == undefined)
&& (null == opaqueUndefined())
&& (opaqueUndefined() == null)
&& (undefined == opaqueUndefined())
&& (opaqueUndefined() == undefined));
}
noInline(compareConstantsAndDynamicValues);
for (let i = 1e4; i--;) {
if (!compareConstantsAndDynamicValues())
throw "Failed compareConstantsAndDynamicValues()";
}
function compareDynamicValues()
{
return ((opaqueNull() == opaqueNull())
&& (opaqueUndefined() == opaqueUndefined())
&& (opaqueNull() == opaqueUndefined())
&& (opaqueUndefined() == opaqueNull()));
}
noInline(compareDynamicValues);
for (let i = 0; i < 1e4; ++i) {
if (!compareDynamicValues())
throw "Failed compareDynamicValues()";
}
function compareDynamicValueToItself()
{
const value1 = opaqueNull();
const value2 = opaqueUndefined();
return value1 == value1 && value2 == value2;
}
noInline(compareDynamicValueToItself);
for (let i = 0; i < 1e4; ++i) {
if (!compareDynamicValueToItself())
throw "Failed compareDynamicValueToItself()";
}
// The case that interested us in the first place.
// Accessing an array with undecided shape always return undefined.
function arrayTesting()
{
let returnValue = true;
const array1 = new Array(2);
for (let i = 0; i < 3; ++i) {
returnValue = returnValue && (array1[i] == null);
returnValue = returnValue && (null == array1[i]);
returnValue = returnValue && (array1[i] == undefined);
returnValue = returnValue && (undefined == array1[i]);
}
const array2 = new Array(2);
for (let i = 0; i < 2; ++i) {
returnValue = returnValue && (array2[i] == opaqueNull());
returnValue = returnValue && (opaqueNull() == array2[i]);
returnValue = returnValue && (array2[i] == opaqueUndefined());
returnValue = returnValue && (opaqueUndefined() == array2[i]);
}
const array3 = new Array(2);
for (let i = 0; i < 3; ++i) {
returnValue = returnValue && (array3[i] == array3[i]);
returnValue = returnValue && (array1[i] == array3[i]);
returnValue = returnValue && (array3[i] == array1[i]);
returnValue = returnValue && (array2[i] == array3[i]);
returnValue = returnValue && (array3[i] == array2[i]);
}
return returnValue;
}
noInline(arrayTesting);
for (let i = 0; i < 1e4; ++i) {
if (!arrayTesting())
throw "Failed arrayTesting()";
}
// Let's make it polymorphic after optimization. We should fallback to a generic compare operation.
function opaqueCompare1(a, b) {
return a == b;
}
noInline(opaqueCompare1);
function testNullComparatorUpdate() {
for (let i = 0; i < 1e4; ++i) {
if (!opaqueCompare1(null, null))
throw "Failed opaqueCompare1(null, null)"
}
// Let's change types
for (let i = 0; i < 1e4; ++i) {
if (opaqueCompare1("foo", null))
throw "Failed opaqueCompare1(\"foo\", null)"
}
}
testNullComparatorUpdate();
function opaqueCompare2(a, b) {
return a == b;
}
noInline(opaqueCompare2);
function testUndefinedComparatorUpdate() {
for (let i = 0; i < 1e4; ++i) {
if (!opaqueCompare2(undefined, undefined))
throw "Failed opaqueCompare2(undefined, undefined)"
}
// Let's change types
for (let i = 0; i < 1e4; ++i) {
if (!opaqueCompare2("bar", "bar"))
throw "Failed opaqueCompare2(\"bar\", \"bar\")"
}
}
testUndefinedComparatorUpdate();
function opaqueCompare3(a, b) {
return a == b;
}
noInline(opaqueCompare3);
function testNullAndUndefinedComparatorUpdate() {
for (let i = 0; i < 1e4; ++i) {
if (!opaqueCompare3(undefined, null) || !opaqueCompare2(null, undefined))
throw "Failed opaqueCompare2(undefined/null, undefined/null)"
}
// Let's change types
for (let i = 0; i < 1e4; ++i) {
if (opaqueCompare3(undefined, "bar"))
throw "Failed opaqueCompare3(undefined, \"bar\")"
}
}
testNullAndUndefinedComparatorUpdate();