haikuwebkit/Source/WTF/wtf/BackwardsGraph.h

187 lines
6.2 KiB
C
Raw Permalink Normal View History

DFG::LICMPhase shouldn't hoist type checks unless it knows that the check will succeed at the loop pre-header https://bugs.webkit.org/show_bug.cgi?id=144527 Reviewed by Saam Barati. Source/JavaScriptCore: This adds a control flow equivalence analysis (called ControlEquivalenceAnalysis) based on dominator analysis over the backwards CFG. Two basic blocks are control flow equivalent if the execution of one implies that the other one must also execute. It means that the two blocks' forward and backward dominance are reciprocated: (A dom B and B backdom A) or (B dom A and A backdom B). LICM now uses it to become more conservative about hoisting checks, if this has caused problems in the past. If we hoist something that may exit from a block that was not control equivalent to the pre-header then it's possible that the node's speculation will fail even though it wouldn't have if it wasn't hoisted. So, we flag these nodes' origins as being "wasHoisted" and we track all of their exits as "HoistingFailed". LICM will turn off such speculative hoisting if the CodeBlock from which we are hoisting had the HoistingFailed exit kind. Note that this deliberately still allows us to hoist things that may exit even if they are not control equivalent to the pre-header. This is necessary because the profitability of hoisting is so huge in all of the cases that we're aware of that it's worth giving it a shot. This is neutral on macrobenchmarks since none of the benchmarks we track have a hoistable operation that would exit only if hoisted. I added microbenchmarks to illustrate the problem and two of them speed up by ~40% while one of them is neutral (Int52 saves us from having problems on that program even though LICM previously did the wrong thing). * JavaScriptCore.xcodeproj/project.pbxproj: * bytecode/ExitKind.cpp: (JSC::exitKindToString): * bytecode/ExitKind.h: * dfg/DFGAtTailAbstractState.h: (JSC::DFG::AtTailAbstractState::operator bool): (JSC::DFG::AtTailAbstractState::initializeTo): * dfg/DFGBackwardsCFG.h: Added. (JSC::DFG::BackwardsCFG::BackwardsCFG): * dfg/DFGBackwardsDominators.h: Added. (JSC::DFG::BackwardsDominators::BackwardsDominators): * dfg/DFGCommon.h: (JSC::DFG::checkAndSet): Deleted. * dfg/DFGControlEquivalenceAnalysis.h: Added. (JSC::DFG::ControlEquivalenceAnalysis::ControlEquivalenceAnalysis): (JSC::DFG::ControlEquivalenceAnalysis::dominatesEquivalently): (JSC::DFG::ControlEquivalenceAnalysis::areEquivalent): * dfg/DFGGraph.cpp: (JSC::DFG::Graph::dump): (JSC::DFG::Graph::dumpBlockHeader): (JSC::DFG::Graph::invalidateCFG): (JSC::DFG::Graph::substituteGetLocal): (JSC::DFG::Graph::handleAssertionFailure): (JSC::DFG::Graph::ensureDominators): (JSC::DFG::Graph::ensurePrePostNumbering): (JSC::DFG::Graph::ensureNaturalLoops): (JSC::DFG::Graph::ensureBackwardsCFG): (JSC::DFG::Graph::ensureBackwardsDominators): (JSC::DFG::Graph::ensureControlEquivalenceAnalysis): (JSC::DFG::Graph::methodOfGettingAValueProfileFor): * dfg/DFGGraph.h: (JSC::DFG::Graph::hasDebuggerEnabled): * dfg/DFGInPlaceAbstractState.h: (JSC::DFG::InPlaceAbstractState::operator bool): (JSC::DFG::InPlaceAbstractState::createValueForNode): (JSC::DFG::InPlaceAbstractState::forNode): * dfg/DFGLICMPhase.cpp: (JSC::DFG::LICMPhase::run): (JSC::DFG::LICMPhase::attemptHoist): * dfg/DFGMayExit.cpp: (JSC::DFG::mayExit): * dfg/DFGMayExit.h: * dfg/DFGNode.h: * dfg/DFGNodeOrigin.cpp: (JSC::DFG::NodeOrigin::dump): * dfg/DFGNodeOrigin.h: (JSC::DFG::NodeOrigin::takeValidExit): (JSC::DFG::NodeOrigin::withWasHoisted): (JSC::DFG::NodeOrigin::forInsertingAfter): * dfg/DFGNullAbstractState.h: Added. (JSC::DFG::NullAbstractState::NullAbstractState): (JSC::DFG::NullAbstractState::operator bool): (JSC::DFG::NullAbstractState::forNode): * dfg/DFGOSRExit.cpp: (JSC::DFG::OSRExit::OSRExit): * dfg/DFGOSRExitBase.cpp: (JSC::DFG::OSRExitBase::considerAddingAsFrequentExitSiteSlow): * dfg/DFGOSRExitBase.h: (JSC::DFG::OSRExitBase::OSRExitBase): * dfg/DFGTypeCheckHoistingPhase.cpp: (JSC::DFG::TypeCheckHoistingPhase::run): * ftl/FTLOSRExit.cpp: (JSC::FTL::OSRExitDescriptor::prepareOSRExitHandle): (JSC::FTL::OSRExit::OSRExit): * ftl/FTLOSRExit.h: Source/WTF: This adds an adaptor for graphs called BackwardsGraph. The WTF graph framework is based on passing around a Graph template argument that follows the protocol shared by DFG::CFG, B3::CFG, and Air::CFG. These graphs always have a single root node but may have many leaf nodes. This new BackwardsGraph adaptor reverses the graph by creating a synthetic return node that it uses as the root in the inverted graph. This currently may resort to some heuristics in programs that have an infinite loop, but other than that, it'll work well in the general case. This allows us to say Dominators<BackwardsGraph<some graph type>> as a way of computing backwards dominators, which then allows us to easily answer control flow equivalence queries. * CMakeLists.txt: * WTF.xcodeproj/project.pbxproj: * wtf/BackwardsGraph.h: Added. (WTF::BackwardsGraph::Node::Node): (WTF::BackwardsGraph::Node::root): (WTF::BackwardsGraph::Node::operator==): (WTF::BackwardsGraph::Node::operator!=): (WTF::BackwardsGraph::Node::operator bool): (WTF::BackwardsGraph::Node::isRoot): (WTF::BackwardsGraph::Node::node): (WTF::BackwardsGraph::Set::Set): (WTF::BackwardsGraph::Set::add): (WTF::BackwardsGraph::Set::remove): (WTF::BackwardsGraph::Set::contains): (WTF::BackwardsGraph::Set::dump): (WTF::BackwardsGraph::Map::Map): (WTF::BackwardsGraph::Map::clear): (WTF::BackwardsGraph::Map::size): (WTF::BackwardsGraph::Map::operator[]): (WTF::BackwardsGraph::BackwardsGraph): (WTF::BackwardsGraph::root): (WTF::BackwardsGraph::newMap): (WTF::BackwardsGraph::successors): (WTF::BackwardsGraph::predecessors): (WTF::BackwardsGraph::index): (WTF::BackwardsGraph::node): (WTF::BackwardsGraph::numNodes): (WTF::BackwardsGraph::dump): * wtf/Dominators.h: (WTF::Dominators::Dominators): (WTF::Dominators::dump): (WTF::Dominators::LengauerTarjan::computeDepthFirstPreNumbering): * wtf/GraphNodeWorklist.h: (WTF::GraphNodeWith::GraphNodeWith): (WTF::GraphNodeWith::operator bool): * wtf/StdLibExtras.h: (WTF::callStatelessLambda): (WTF::checkAndSet): LayoutTests: Add tests for LICM hoisting things that would only exit if hoisted. * js/regress/licm-dragons-expected.txt: Added. * js/regress/licm-dragons-out-of-bounds-expected.txt: Added. * js/regress/licm-dragons-out-of-bounds.html: Added. * js/regress/licm-dragons-overflow-expected.txt: Added. * js/regress/licm-dragons-overflow.html: Added. * js/regress/licm-dragons.html: Added. * js/regress/script-tests/licm-dragons-out-of-bounds.js: Added. (foo): * js/regress/script-tests/licm-dragons-overflow.js: Added. (foo): * js/regress/script-tests/licm-dragons.js: Added. (foo): Canonical link: https://commits.webkit.org/176032@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@201182 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2016-05-19 21:25:29 +00:00
/*
BackwardsGraph needs to consider back edges as the backward's root successor https://bugs.webkit.org/show_bug.cgi?id=195991 Reviewed by Filip Pizlo. JSTests: * stress/map-b3-licm-infinite-loop.js: Added. Source/JavaScriptCore: * b3/testb3.cpp: (JSC::B3::testInfiniteLoopDoesntCauseBadHoisting): (JSC::B3::run): Source/WTF: Previously, our backwards graph analysis was slightly wrong. The idea of backwards graph is that the root of the graph has edges to terminals in the original graph. And then the original directed edges in the graph are flipped. However, we weren't considering loops as a form of terminality. For example, we wouldn't consider an infinite loop as a terminal. So there were no edges from the root to a node in the infinite loop. This lead us to make mistakes when we used backwards dominators to compute control flow equivalence. This is better understood in an example: ``` preheader: while (1) { if (!isCell(v)) continue; load structure ID if (cond) continue; return } ``` In the previous version of this algorithm, the only edge from the backwards root would be to the block containing the return. This would lead us to believe that the loading of the structureID backwards dominates the preheader, leading us to believe it's control flow equivalent to preheader. This is obviously wrong, since we can loop forever if "v" isn't a cell. The solution here is to treat any backedge in the graph as a "terminal" node. Since a backedge implies the existence of a loop. In the above example, the backwards root now has an edge to both blocks with "continue". This prevents us from falsely claiming that the return is control flow equivalent with the preheader. This patch uses DFS spanning trees to compute back edges. An edge u->v is a back edge when u is a descendent of v in the DFS spanning tree of the Graph. * WTF.xcodeproj/project.pbxproj: * wtf/BackwardsGraph.h: (WTF::BackwardsGraph::BackwardsGraph): * wtf/SpanningTree.h: Added. (SpanningTree::SpanningTree): (SpanningTree::isDescendent): Canonical link: https://commits.webkit.org/210659@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@243639 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2019-03-29 04:42:42 +00:00
* Copyright (C) 2016-2019 Apple Inc. All rights reserved.
DFG::LICMPhase shouldn't hoist type checks unless it knows that the check will succeed at the loop pre-header https://bugs.webkit.org/show_bug.cgi?id=144527 Reviewed by Saam Barati. Source/JavaScriptCore: This adds a control flow equivalence analysis (called ControlEquivalenceAnalysis) based on dominator analysis over the backwards CFG. Two basic blocks are control flow equivalent if the execution of one implies that the other one must also execute. It means that the two blocks' forward and backward dominance are reciprocated: (A dom B and B backdom A) or (B dom A and A backdom B). LICM now uses it to become more conservative about hoisting checks, if this has caused problems in the past. If we hoist something that may exit from a block that was not control equivalent to the pre-header then it's possible that the node's speculation will fail even though it wouldn't have if it wasn't hoisted. So, we flag these nodes' origins as being "wasHoisted" and we track all of their exits as "HoistingFailed". LICM will turn off such speculative hoisting if the CodeBlock from which we are hoisting had the HoistingFailed exit kind. Note that this deliberately still allows us to hoist things that may exit even if they are not control equivalent to the pre-header. This is necessary because the profitability of hoisting is so huge in all of the cases that we're aware of that it's worth giving it a shot. This is neutral on macrobenchmarks since none of the benchmarks we track have a hoistable operation that would exit only if hoisted. I added microbenchmarks to illustrate the problem and two of them speed up by ~40% while one of them is neutral (Int52 saves us from having problems on that program even though LICM previously did the wrong thing). * JavaScriptCore.xcodeproj/project.pbxproj: * bytecode/ExitKind.cpp: (JSC::exitKindToString): * bytecode/ExitKind.h: * dfg/DFGAtTailAbstractState.h: (JSC::DFG::AtTailAbstractState::operator bool): (JSC::DFG::AtTailAbstractState::initializeTo): * dfg/DFGBackwardsCFG.h: Added. (JSC::DFG::BackwardsCFG::BackwardsCFG): * dfg/DFGBackwardsDominators.h: Added. (JSC::DFG::BackwardsDominators::BackwardsDominators): * dfg/DFGCommon.h: (JSC::DFG::checkAndSet): Deleted. * dfg/DFGControlEquivalenceAnalysis.h: Added. (JSC::DFG::ControlEquivalenceAnalysis::ControlEquivalenceAnalysis): (JSC::DFG::ControlEquivalenceAnalysis::dominatesEquivalently): (JSC::DFG::ControlEquivalenceAnalysis::areEquivalent): * dfg/DFGGraph.cpp: (JSC::DFG::Graph::dump): (JSC::DFG::Graph::dumpBlockHeader): (JSC::DFG::Graph::invalidateCFG): (JSC::DFG::Graph::substituteGetLocal): (JSC::DFG::Graph::handleAssertionFailure): (JSC::DFG::Graph::ensureDominators): (JSC::DFG::Graph::ensurePrePostNumbering): (JSC::DFG::Graph::ensureNaturalLoops): (JSC::DFG::Graph::ensureBackwardsCFG): (JSC::DFG::Graph::ensureBackwardsDominators): (JSC::DFG::Graph::ensureControlEquivalenceAnalysis): (JSC::DFG::Graph::methodOfGettingAValueProfileFor): * dfg/DFGGraph.h: (JSC::DFG::Graph::hasDebuggerEnabled): * dfg/DFGInPlaceAbstractState.h: (JSC::DFG::InPlaceAbstractState::operator bool): (JSC::DFG::InPlaceAbstractState::createValueForNode): (JSC::DFG::InPlaceAbstractState::forNode): * dfg/DFGLICMPhase.cpp: (JSC::DFG::LICMPhase::run): (JSC::DFG::LICMPhase::attemptHoist): * dfg/DFGMayExit.cpp: (JSC::DFG::mayExit): * dfg/DFGMayExit.h: * dfg/DFGNode.h: * dfg/DFGNodeOrigin.cpp: (JSC::DFG::NodeOrigin::dump): * dfg/DFGNodeOrigin.h: (JSC::DFG::NodeOrigin::takeValidExit): (JSC::DFG::NodeOrigin::withWasHoisted): (JSC::DFG::NodeOrigin::forInsertingAfter): * dfg/DFGNullAbstractState.h: Added. (JSC::DFG::NullAbstractState::NullAbstractState): (JSC::DFG::NullAbstractState::operator bool): (JSC::DFG::NullAbstractState::forNode): * dfg/DFGOSRExit.cpp: (JSC::DFG::OSRExit::OSRExit): * dfg/DFGOSRExitBase.cpp: (JSC::DFG::OSRExitBase::considerAddingAsFrequentExitSiteSlow): * dfg/DFGOSRExitBase.h: (JSC::DFG::OSRExitBase::OSRExitBase): * dfg/DFGTypeCheckHoistingPhase.cpp: (JSC::DFG::TypeCheckHoistingPhase::run): * ftl/FTLOSRExit.cpp: (JSC::FTL::OSRExitDescriptor::prepareOSRExitHandle): (JSC::FTL::OSRExit::OSRExit): * ftl/FTLOSRExit.h: Source/WTF: This adds an adaptor for graphs called BackwardsGraph. The WTF graph framework is based on passing around a Graph template argument that follows the protocol shared by DFG::CFG, B3::CFG, and Air::CFG. These graphs always have a single root node but may have many leaf nodes. This new BackwardsGraph adaptor reverses the graph by creating a synthetic return node that it uses as the root in the inverted graph. This currently may resort to some heuristics in programs that have an infinite loop, but other than that, it'll work well in the general case. This allows us to say Dominators<BackwardsGraph<some graph type>> as a way of computing backwards dominators, which then allows us to easily answer control flow equivalence queries. * CMakeLists.txt: * WTF.xcodeproj/project.pbxproj: * wtf/BackwardsGraph.h: Added. (WTF::BackwardsGraph::Node::Node): (WTF::BackwardsGraph::Node::root): (WTF::BackwardsGraph::Node::operator==): (WTF::BackwardsGraph::Node::operator!=): (WTF::BackwardsGraph::Node::operator bool): (WTF::BackwardsGraph::Node::isRoot): (WTF::BackwardsGraph::Node::node): (WTF::BackwardsGraph::Set::Set): (WTF::BackwardsGraph::Set::add): (WTF::BackwardsGraph::Set::remove): (WTF::BackwardsGraph::Set::contains): (WTF::BackwardsGraph::Set::dump): (WTF::BackwardsGraph::Map::Map): (WTF::BackwardsGraph::Map::clear): (WTF::BackwardsGraph::Map::size): (WTF::BackwardsGraph::Map::operator[]): (WTF::BackwardsGraph::BackwardsGraph): (WTF::BackwardsGraph::root): (WTF::BackwardsGraph::newMap): (WTF::BackwardsGraph::successors): (WTF::BackwardsGraph::predecessors): (WTF::BackwardsGraph::index): (WTF::BackwardsGraph::node): (WTF::BackwardsGraph::numNodes): (WTF::BackwardsGraph::dump): * wtf/Dominators.h: (WTF::Dominators::Dominators): (WTF::Dominators::dump): (WTF::Dominators::LengauerTarjan::computeDepthFirstPreNumbering): * wtf/GraphNodeWorklist.h: (WTF::GraphNodeWith::GraphNodeWith): (WTF::GraphNodeWith::operator bool): * wtf/StdLibExtras.h: (WTF::callStatelessLambda): (WTF::checkAndSet): LayoutTests: Add tests for LICM hoisting things that would only exit if hoisted. * js/regress/licm-dragons-expected.txt: Added. * js/regress/licm-dragons-out-of-bounds-expected.txt: Added. * js/regress/licm-dragons-out-of-bounds.html: Added. * js/regress/licm-dragons-overflow-expected.txt: Added. * js/regress/licm-dragons-overflow.html: Added. * js/regress/licm-dragons.html: Added. * js/regress/script-tests/licm-dragons-out-of-bounds.js: Added. (foo): * js/regress/script-tests/licm-dragons-overflow.js: Added. (foo): * js/regress/script-tests/licm-dragons.js: Added. (foo): Canonical link: https://commits.webkit.org/176032@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@201182 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2016-05-19 21:25:29 +00:00
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
Use pragma once in WTF https://bugs.webkit.org/show_bug.cgi?id=190527 Reviewed by Chris Dumez. Source/WTF: We also need to consistently include wtf headers from within wtf so we can build wtf without symbol redefinition errors from including the copy in Source and the copy in the build directory. * wtf/ASCIICType.h: * wtf/Assertions.cpp: * wtf/Assertions.h: * wtf/Atomics.h: * wtf/AutomaticThread.cpp: * wtf/AutomaticThread.h: * wtf/BackwardsGraph.h: * wtf/Bag.h: * wtf/BagToHashMap.h: * wtf/BitVector.cpp: * wtf/BitVector.h: * wtf/Bitmap.h: * wtf/BloomFilter.h: * wtf/Box.h: * wtf/BubbleSort.h: * wtf/BumpPointerAllocator.h: * wtf/ByteOrder.h: * wtf/CPUTime.cpp: * wtf/CallbackAggregator.h: * wtf/CheckedArithmetic.h: * wtf/CheckedBoolean.h: * wtf/ClockType.cpp: * wtf/ClockType.h: * wtf/CommaPrinter.h: * wtf/CompilationThread.cpp: * wtf/CompilationThread.h: * wtf/Compiler.h: * wtf/ConcurrentPtrHashSet.cpp: * wtf/ConcurrentVector.h: * wtf/Condition.h: * wtf/CountingLock.cpp: * wtf/CrossThreadTaskHandler.cpp: * wtf/CryptographicUtilities.cpp: * wtf/CryptographicUtilities.h: * wtf/CryptographicallyRandomNumber.cpp: * wtf/CryptographicallyRandomNumber.h: * wtf/CurrentTime.cpp: * wtf/DataLog.cpp: * wtf/DataLog.h: * wtf/DateMath.cpp: * wtf/DateMath.h: * wtf/DecimalNumber.cpp: * wtf/DecimalNumber.h: * wtf/Deque.h: * wtf/DisallowCType.h: * wtf/Dominators.h: * wtf/DoublyLinkedList.h: * wtf/FastBitVector.cpp: * wtf/FastMalloc.cpp: * wtf/FastMalloc.h: * wtf/FeatureDefines.h: * wtf/FilePrintStream.cpp: * wtf/FilePrintStream.h: * wtf/FlipBytes.h: * wtf/FunctionDispatcher.cpp: * wtf/FunctionDispatcher.h: * wtf/GetPtr.h: * wtf/Gigacage.cpp: * wtf/GlobalVersion.cpp: * wtf/GraphNodeWorklist.h: * wtf/GregorianDateTime.cpp: * wtf/GregorianDateTime.h: * wtf/HashFunctions.h: * wtf/HashMap.h: * wtf/HashMethod.h: * wtf/HashSet.h: * wtf/HashTable.cpp: * wtf/HashTraits.h: * wtf/Indenter.h: * wtf/IndexSparseSet.h: * wtf/InlineASM.h: * wtf/Insertion.h: * wtf/IteratorAdaptors.h: * wtf/IteratorRange.h: * wtf/JSONValues.cpp: * wtf/JSValueMalloc.cpp: * wtf/LEBDecoder.h: * wtf/Language.cpp: * wtf/ListDump.h: * wtf/Lock.cpp: * wtf/Lock.h: * wtf/LockAlgorithm.h: * wtf/LockedPrintStream.cpp: * wtf/Locker.h: * wtf/MD5.cpp: * wtf/MD5.h: * wtf/MainThread.cpp: * wtf/MainThread.h: * wtf/MallocPtr.h: * wtf/MathExtras.h: * wtf/MediaTime.cpp: * wtf/MediaTime.h: * wtf/MemoryPressureHandler.cpp: * wtf/MessageQueue.h: * wtf/MetaAllocator.cpp: * wtf/MetaAllocator.h: * wtf/MetaAllocatorHandle.h: * wtf/MonotonicTime.cpp: * wtf/MonotonicTime.h: * wtf/NakedPtr.h: * wtf/NoLock.h: * wtf/NoTailCalls.h: * wtf/Noncopyable.h: * wtf/NumberOfCores.cpp: * wtf/NumberOfCores.h: * wtf/OSAllocator.h: * wtf/OSAllocatorPosix.cpp: * wtf/OSRandomSource.cpp: * wtf/OSRandomSource.h: * wtf/ObjcRuntimeExtras.h: * wtf/OrderMaker.h: * wtf/PackedIntVector.h: * wtf/PageAllocation.h: * wtf/PageBlock.cpp: * wtf/PageBlock.h: * wtf/PageReservation.h: * wtf/ParallelHelperPool.cpp: * wtf/ParallelHelperPool.h: * wtf/ParallelJobs.h: * wtf/ParallelJobsLibdispatch.h: * wtf/ParallelVectorIterator.h: * wtf/ParkingLot.cpp: * wtf/ParkingLot.h: * wtf/Platform.h: * wtf/PointerComparison.h: * wtf/Poisoned.cpp: * wtf/PrintStream.cpp: * wtf/PrintStream.h: * wtf/ProcessID.h: * wtf/ProcessPrivilege.cpp: * wtf/RAMSize.cpp: * wtf/RAMSize.h: * wtf/RandomDevice.cpp: * wtf/RandomNumber.cpp: * wtf/RandomNumber.h: * wtf/RandomNumberSeed.h: * wtf/RangeSet.h: * wtf/RawPointer.h: * wtf/ReadWriteLock.cpp: * wtf/RedBlackTree.h: * wtf/Ref.h: * wtf/RefCountedArray.h: * wtf/RefCountedLeakCounter.cpp: * wtf/RefCountedLeakCounter.h: * wtf/RefCounter.h: * wtf/RefPtr.h: * wtf/RetainPtr.h: * wtf/RunLoop.cpp: * wtf/RunLoop.h: * wtf/RunLoopTimer.h: * wtf/RunLoopTimerCF.cpp: * wtf/SHA1.cpp: * wtf/SHA1.h: * wtf/SaturatedArithmetic.h: (saturatedSubtraction): * wtf/SchedulePair.h: * wtf/SchedulePairCF.cpp: * wtf/SchedulePairMac.mm: * wtf/ScopedLambda.h: * wtf/Seconds.cpp: * wtf/Seconds.h: * wtf/SegmentedVector.h: * wtf/SentinelLinkedList.h: * wtf/SharedTask.h: * wtf/SimpleStats.h: * wtf/SingleRootGraph.h: * wtf/SinglyLinkedList.h: * wtf/SixCharacterHash.cpp: * wtf/SixCharacterHash.h: * wtf/SmallPtrSet.h: * wtf/Spectrum.h: * wtf/StackBounds.cpp: * wtf/StackBounds.h: * wtf/StackStats.cpp: * wtf/StackStats.h: * wtf/StackTrace.cpp: * wtf/StdLibExtras.h: * wtf/StreamBuffer.h: * wtf/StringHashDumpContext.h: * wtf/StringPrintStream.cpp: * wtf/StringPrintStream.h: * wtf/ThreadGroup.cpp: * wtf/ThreadMessage.cpp: * wtf/ThreadSpecific.h: * wtf/Threading.cpp: * wtf/Threading.h: * wtf/ThreadingPrimitives.h: * wtf/ThreadingPthreads.cpp: * wtf/TimeWithDynamicClockType.cpp: * wtf/TimeWithDynamicClockType.h: * wtf/TimingScope.cpp: * wtf/TinyLRUCache.h: * wtf/TinyPtrSet.h: * wtf/TriState.h: * wtf/TypeCasts.h: * wtf/UUID.cpp: * wtf/UnionFind.h: * wtf/VMTags.h: * wtf/ValueCheck.h: * wtf/Vector.h: * wtf/VectorTraits.h: * wtf/WallTime.cpp: * wtf/WallTime.h: * wtf/WeakPtr.h: * wtf/WeakRandom.h: * wtf/WordLock.cpp: * wtf/WordLock.h: * wtf/WorkQueue.cpp: * wtf/WorkQueue.h: * wtf/WorkerPool.cpp: * wtf/cf/LanguageCF.cpp: * wtf/cf/RunLoopCF.cpp: * wtf/cocoa/Entitlements.mm: * wtf/cocoa/MachSendRight.cpp: * wtf/cocoa/MainThreadCocoa.mm: * wtf/cocoa/MemoryFootprintCocoa.cpp: * wtf/cocoa/WorkQueueCocoa.cpp: * wtf/dtoa.cpp: * wtf/dtoa.h: * wtf/ios/WebCoreThread.cpp: * wtf/ios/WebCoreThread.h: * wtf/mac/AppKitCompatibilityDeclarations.h: * wtf/mac/DeprecatedSymbolsUsedBySafari.mm: * wtf/mbmalloc.cpp: * wtf/persistence/PersistentCoders.cpp: * wtf/persistence/PersistentDecoder.cpp: * wtf/persistence/PersistentEncoder.cpp: * wtf/spi/cf/CFBundleSPI.h: * wtf/spi/darwin/CommonCryptoSPI.h: * wtf/text/ASCIIFastPath.h: * wtf/text/ASCIILiteral.cpp: * wtf/text/AtomicString.cpp: * wtf/text/AtomicString.h: * wtf/text/AtomicStringHash.h: * wtf/text/AtomicStringImpl.cpp: * wtf/text/AtomicStringImpl.h: * wtf/text/AtomicStringTable.cpp: * wtf/text/AtomicStringTable.h: * wtf/text/Base64.cpp: * wtf/text/CString.cpp: * wtf/text/CString.h: * wtf/text/ConversionMode.h: * wtf/text/ExternalStringImpl.cpp: * wtf/text/IntegerToStringConversion.h: * wtf/text/LChar.h: * wtf/text/LineEnding.cpp: * wtf/text/StringBuffer.h: * wtf/text/StringBuilder.cpp: * wtf/text/StringBuilder.h: * wtf/text/StringBuilderJSON.cpp: * wtf/text/StringCommon.h: * wtf/text/StringConcatenate.h: * wtf/text/StringHash.h: * wtf/text/StringImpl.cpp: * wtf/text/StringImpl.h: * wtf/text/StringOperators.h: * wtf/text/StringView.cpp: * wtf/text/StringView.h: * wtf/text/SymbolImpl.cpp: * wtf/text/SymbolRegistry.cpp: * wtf/text/SymbolRegistry.h: * wtf/text/TextBreakIterator.cpp: * wtf/text/TextBreakIterator.h: * wtf/text/TextBreakIteratorInternalICU.h: * wtf/text/TextPosition.h: * wtf/text/TextStream.cpp: * wtf/text/UniquedStringImpl.h: * wtf/text/WTFString.cpp: * wtf/text/WTFString.h: * wtf/text/cocoa/StringCocoa.mm: * wtf/text/cocoa/StringViewCocoa.mm: * wtf/text/cocoa/TextBreakIteratorInternalICUCocoa.cpp: * wtf/text/icu/UTextProvider.cpp: * wtf/text/icu/UTextProvider.h: * wtf/text/icu/UTextProviderLatin1.cpp: * wtf/text/icu/UTextProviderLatin1.h: * wtf/text/icu/UTextProviderUTF16.cpp: * wtf/text/icu/UTextProviderUTF16.h: * wtf/threads/BinarySemaphore.cpp: * wtf/threads/BinarySemaphore.h: * wtf/threads/Signals.cpp: * wtf/unicode/CharacterNames.h: * wtf/unicode/Collator.h: * wtf/unicode/CollatorDefault.cpp: * wtf/unicode/UTF8.cpp: * wtf/unicode/UTF8.h: Tools: Put WorkQueue in namespace DRT so it does not conflict with WTF::WorkQueue. * DumpRenderTree/TestRunner.cpp: (TestRunner::queueLoadHTMLString): (TestRunner::queueLoadAlternateHTMLString): (TestRunner::queueBackNavigation): (TestRunner::queueForwardNavigation): (TestRunner::queueLoadingScript): (TestRunner::queueNonLoadingScript): (TestRunner::queueReload): * DumpRenderTree/WorkQueue.cpp: (WorkQueue::singleton): Deleted. (WorkQueue::WorkQueue): Deleted. (WorkQueue::queue): Deleted. (WorkQueue::dequeue): Deleted. (WorkQueue::count): Deleted. (WorkQueue::clear): Deleted. (WorkQueue::processWork): Deleted. * DumpRenderTree/WorkQueue.h: (WorkQueue::setFrozen): Deleted. * DumpRenderTree/WorkQueueItem.h: * DumpRenderTree/mac/DumpRenderTree.mm: (runTest): * DumpRenderTree/mac/FrameLoadDelegate.mm: (-[FrameLoadDelegate processWork:]): (-[FrameLoadDelegate webView:locationChangeDone:forDataSource:]): * DumpRenderTree/mac/TestRunnerMac.mm: (TestRunner::notifyDone): (TestRunner::forceImmediateCompletion): (TestRunner::queueLoad): * DumpRenderTree/win/DumpRenderTree.cpp: (runTest): * DumpRenderTree/win/FrameLoadDelegate.cpp: (FrameLoadDelegate::processWork): (FrameLoadDelegate::locationChangeDone): * DumpRenderTree/win/TestRunnerWin.cpp: (TestRunner::notifyDone): (TestRunner::forceImmediateCompletion): (TestRunner::queueLoad): Canonical link: https://commits.webkit.org/205473@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@237099 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2018-10-15 14:24:49 +00:00
#pragma once
DFG::LICMPhase shouldn't hoist type checks unless it knows that the check will succeed at the loop pre-header https://bugs.webkit.org/show_bug.cgi?id=144527 Reviewed by Saam Barati. Source/JavaScriptCore: This adds a control flow equivalence analysis (called ControlEquivalenceAnalysis) based on dominator analysis over the backwards CFG. Two basic blocks are control flow equivalent if the execution of one implies that the other one must also execute. It means that the two blocks' forward and backward dominance are reciprocated: (A dom B and B backdom A) or (B dom A and A backdom B). LICM now uses it to become more conservative about hoisting checks, if this has caused problems in the past. If we hoist something that may exit from a block that was not control equivalent to the pre-header then it's possible that the node's speculation will fail even though it wouldn't have if it wasn't hoisted. So, we flag these nodes' origins as being "wasHoisted" and we track all of their exits as "HoistingFailed". LICM will turn off such speculative hoisting if the CodeBlock from which we are hoisting had the HoistingFailed exit kind. Note that this deliberately still allows us to hoist things that may exit even if they are not control equivalent to the pre-header. This is necessary because the profitability of hoisting is so huge in all of the cases that we're aware of that it's worth giving it a shot. This is neutral on macrobenchmarks since none of the benchmarks we track have a hoistable operation that would exit only if hoisted. I added microbenchmarks to illustrate the problem and two of them speed up by ~40% while one of them is neutral (Int52 saves us from having problems on that program even though LICM previously did the wrong thing). * JavaScriptCore.xcodeproj/project.pbxproj: * bytecode/ExitKind.cpp: (JSC::exitKindToString): * bytecode/ExitKind.h: * dfg/DFGAtTailAbstractState.h: (JSC::DFG::AtTailAbstractState::operator bool): (JSC::DFG::AtTailAbstractState::initializeTo): * dfg/DFGBackwardsCFG.h: Added. (JSC::DFG::BackwardsCFG::BackwardsCFG): * dfg/DFGBackwardsDominators.h: Added. (JSC::DFG::BackwardsDominators::BackwardsDominators): * dfg/DFGCommon.h: (JSC::DFG::checkAndSet): Deleted. * dfg/DFGControlEquivalenceAnalysis.h: Added. (JSC::DFG::ControlEquivalenceAnalysis::ControlEquivalenceAnalysis): (JSC::DFG::ControlEquivalenceAnalysis::dominatesEquivalently): (JSC::DFG::ControlEquivalenceAnalysis::areEquivalent): * dfg/DFGGraph.cpp: (JSC::DFG::Graph::dump): (JSC::DFG::Graph::dumpBlockHeader): (JSC::DFG::Graph::invalidateCFG): (JSC::DFG::Graph::substituteGetLocal): (JSC::DFG::Graph::handleAssertionFailure): (JSC::DFG::Graph::ensureDominators): (JSC::DFG::Graph::ensurePrePostNumbering): (JSC::DFG::Graph::ensureNaturalLoops): (JSC::DFG::Graph::ensureBackwardsCFG): (JSC::DFG::Graph::ensureBackwardsDominators): (JSC::DFG::Graph::ensureControlEquivalenceAnalysis): (JSC::DFG::Graph::methodOfGettingAValueProfileFor): * dfg/DFGGraph.h: (JSC::DFG::Graph::hasDebuggerEnabled): * dfg/DFGInPlaceAbstractState.h: (JSC::DFG::InPlaceAbstractState::operator bool): (JSC::DFG::InPlaceAbstractState::createValueForNode): (JSC::DFG::InPlaceAbstractState::forNode): * dfg/DFGLICMPhase.cpp: (JSC::DFG::LICMPhase::run): (JSC::DFG::LICMPhase::attemptHoist): * dfg/DFGMayExit.cpp: (JSC::DFG::mayExit): * dfg/DFGMayExit.h: * dfg/DFGNode.h: * dfg/DFGNodeOrigin.cpp: (JSC::DFG::NodeOrigin::dump): * dfg/DFGNodeOrigin.h: (JSC::DFG::NodeOrigin::takeValidExit): (JSC::DFG::NodeOrigin::withWasHoisted): (JSC::DFG::NodeOrigin::forInsertingAfter): * dfg/DFGNullAbstractState.h: Added. (JSC::DFG::NullAbstractState::NullAbstractState): (JSC::DFG::NullAbstractState::operator bool): (JSC::DFG::NullAbstractState::forNode): * dfg/DFGOSRExit.cpp: (JSC::DFG::OSRExit::OSRExit): * dfg/DFGOSRExitBase.cpp: (JSC::DFG::OSRExitBase::considerAddingAsFrequentExitSiteSlow): * dfg/DFGOSRExitBase.h: (JSC::DFG::OSRExitBase::OSRExitBase): * dfg/DFGTypeCheckHoistingPhase.cpp: (JSC::DFG::TypeCheckHoistingPhase::run): * ftl/FTLOSRExit.cpp: (JSC::FTL::OSRExitDescriptor::prepareOSRExitHandle): (JSC::FTL::OSRExit::OSRExit): * ftl/FTLOSRExit.h: Source/WTF: This adds an adaptor for graphs called BackwardsGraph. The WTF graph framework is based on passing around a Graph template argument that follows the protocol shared by DFG::CFG, B3::CFG, and Air::CFG. These graphs always have a single root node but may have many leaf nodes. This new BackwardsGraph adaptor reverses the graph by creating a synthetic return node that it uses as the root in the inverted graph. This currently may resort to some heuristics in programs that have an infinite loop, but other than that, it'll work well in the general case. This allows us to say Dominators<BackwardsGraph<some graph type>> as a way of computing backwards dominators, which then allows us to easily answer control flow equivalence queries. * CMakeLists.txt: * WTF.xcodeproj/project.pbxproj: * wtf/BackwardsGraph.h: Added. (WTF::BackwardsGraph::Node::Node): (WTF::BackwardsGraph::Node::root): (WTF::BackwardsGraph::Node::operator==): (WTF::BackwardsGraph::Node::operator!=): (WTF::BackwardsGraph::Node::operator bool): (WTF::BackwardsGraph::Node::isRoot): (WTF::BackwardsGraph::Node::node): (WTF::BackwardsGraph::Set::Set): (WTF::BackwardsGraph::Set::add): (WTF::BackwardsGraph::Set::remove): (WTF::BackwardsGraph::Set::contains): (WTF::BackwardsGraph::Set::dump): (WTF::BackwardsGraph::Map::Map): (WTF::BackwardsGraph::Map::clear): (WTF::BackwardsGraph::Map::size): (WTF::BackwardsGraph::Map::operator[]): (WTF::BackwardsGraph::BackwardsGraph): (WTF::BackwardsGraph::root): (WTF::BackwardsGraph::newMap): (WTF::BackwardsGraph::successors): (WTF::BackwardsGraph::predecessors): (WTF::BackwardsGraph::index): (WTF::BackwardsGraph::node): (WTF::BackwardsGraph::numNodes): (WTF::BackwardsGraph::dump): * wtf/Dominators.h: (WTF::Dominators::Dominators): (WTF::Dominators::dump): (WTF::Dominators::LengauerTarjan::computeDepthFirstPreNumbering): * wtf/GraphNodeWorklist.h: (WTF::GraphNodeWith::GraphNodeWith): (WTF::GraphNodeWith::operator bool): * wtf/StdLibExtras.h: (WTF::callStatelessLambda): (WTF::checkAndSet): LayoutTests: Add tests for LICM hoisting things that would only exit if hoisted. * js/regress/licm-dragons-expected.txt: Added. * js/regress/licm-dragons-out-of-bounds-expected.txt: Added. * js/regress/licm-dragons-out-of-bounds.html: Added. * js/regress/licm-dragons-overflow-expected.txt: Added. * js/regress/licm-dragons-overflow.html: Added. * js/regress/licm-dragons.html: Added. * js/regress/script-tests/licm-dragons-out-of-bounds.js: Added. (foo): * js/regress/script-tests/licm-dragons-overflow.js: Added. (foo): * js/regress/script-tests/licm-dragons.js: Added. (foo): Canonical link: https://commits.webkit.org/176032@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@201182 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2016-05-19 21:25:29 +00:00
#include <wtf/FastMalloc.h>
#include <wtf/GraphNodeWorklist.h>
#include <wtf/Noncopyable.h>
Support compiling catch in the DFG https://bugs.webkit.org/show_bug.cgi?id=174590 <rdar://problem/34047845> Reviewed by Filip Pizlo. JSTests: * microbenchmarks/delta-blue-try-catch.js: Added. (exception): (value): (OrderedCollection): (OrderedCollection.prototype.add): (OrderedCollection.prototype.at): (OrderedCollection.prototype.size): (OrderedCollection.prototype.removeFirst): (OrderedCollection.prototype.remove): (Strength): (Strength.stronger): (Strength.weaker): (Strength.weakestOf): (Strength.strongest): (Strength.prototype.nextWeaker): (Constraint): (Constraint.prototype.addConstraint): (Constraint.prototype.satisfy): (Constraint.prototype.destroyConstraint): (Constraint.prototype.isInput): (UnaryConstraint): (UnaryConstraint.prototype.addToGraph): (UnaryConstraint.prototype.chooseMethod): (UnaryConstraint.prototype.isSatisfied): (UnaryConstraint.prototype.markInputs): (UnaryConstraint.prototype.output): (UnaryConstraint.prototype.recalculate): (UnaryConstraint.prototype.markUnsatisfied): (UnaryConstraint.prototype.inputsKnown): (UnaryConstraint.prototype.removeFromGraph): (StayConstraint): (StayConstraint.prototype.execute): (EditConstraint.prototype.isInput): (EditConstraint.prototype.execute): (BinaryConstraint): (BinaryConstraint.prototype.chooseMethod): (BinaryConstraint.prototype.addToGraph): (BinaryConstraint.prototype.isSatisfied): (BinaryConstraint.prototype.markInputs): (BinaryConstraint.prototype.input): (BinaryConstraint.prototype.output): (BinaryConstraint.prototype.recalculate): (BinaryConstraint.prototype.markUnsatisfied): (BinaryConstraint.prototype.inputsKnown): (BinaryConstraint.prototype.removeFromGraph): (ScaleConstraint): (ScaleConstraint.prototype.addToGraph): (ScaleConstraint.prototype.removeFromGraph): (ScaleConstraint.prototype.markInputs): (ScaleConstraint.prototype.execute): (ScaleConstraint.prototype.recalculate): (EqualityConstraint): (EqualityConstraint.prototype.execute): (Variable): (Variable.prototype.addConstraint): (Variable.prototype.removeConstraint): (Planner): (Planner.prototype.incrementalAdd): (Planner.prototype.incrementalRemove): (Planner.prototype.newMark): (Planner.prototype.makePlan): (Planner.prototype.extractPlanFromConstraints): (Planner.prototype.addPropagate): (Planner.prototype.removePropagateFrom): (Planner.prototype.addConstraintsConsumingTo): (Plan): (Plan.prototype.addConstraint): (Plan.prototype.size): (Plan.prototype.constraintAt): (Plan.prototype.execute): (chainTest): (projectionTest): (change): (deltaBlue): * microbenchmarks/fake-iterators-that-throw-when-finished.js: Added. (assert): (Numbers): (Numbers.prototype.next): (return.Transpose): (return.Transpose.prototype.next): (transpose): (verifyEven): (verifyString): (foo): (runIterators): * microbenchmarks/try-catch-word-count.js: Added. (let.assert): (EOF): (let.texts): (let.o.apply): (foo): (bar): (f): (run): (test1): (test2): (test3): (fn): (A): (B): (A.prototype.getValue): (B.prototype.getParentValue): (strlen): (sum.0): (test): (result.test.o): (set add.set add): (set forEach): (stringHash): (set if): (testFunction): (set delete.set has.set add): * stress/catch-set-argument-speculation-failure.js: Added. (o): (e): (e2): (escape): (baz): (noInline.run): (noInline): * stress/osr-enter-to-catch-with-set-local-type-check-failure.js: Added. (foo): (e): (baz): (bar): Source/JavaScriptCore: This patch implements OSR entry into op_catch in the DFG. We will support OSR entry into the FTL in a followup: https://bugs.webkit.org/show_bug.cgi?id=175396 To implement catch in the DFG, this patch introduces the concept of multiple entrypoints into CPS/LoadStore DFG IR. A lot of this patch is stringing this concept through the DFG. Many phases used to assume that Graph::block(0) is the only root, and this patch contains many straight forward changes generalizing the code to handle more than one entrypoint. A main building block of this is moving to two CFG types: SSACFG and CPSCFG. SSACFG is the same CFG we used to have. CPSCFG is a new type that introduces a fake root that has an outgoing edge to all the entrypoints. This allows our existing graph algorithms to Just Work over CPSCFG. For example, there is now the concept of SSADominators vs CPSDominators, and SSANaturalLoops vs CPSNaturalLoops. The way we compile the catch entrypoint is by bootstrapping the state of the program by loading all live bytecode locals from a buffer. The OSR entry code will store all live values into that buffer before jumping to the entrypoint. The OSR entry code is also responsible for performing type proofs of the arguments before doing an OSR entry. If there is a type mismatch, it's not legal to OSR enter into the DFG compilation. Currently, each catch entrypoint knows the argument type proofs it must perform to enter into the DFG. Currently, all entrypoints' arguments flush format are unified via ArgumentPosition, but this is just an implementation detail. The code is written more generally to assume that each entrypoint may perform its own distinct proof. op_catch now performs value profiling for all live bytecode locals in the LLInt and baseline JIT. This information is then fed into the DFG via the ExtractCatchLocal node in the prediction propagation phase. This patch also changes how we generate op_catch in bytecode. All op_catches are now split out at the end of the program in bytecode. This ensures that no op_catch is inside a try block. This is needed to ensure correctness in the DFGLiveCatchVariablePreservationPhase. That phase only inserts flushes before SetLocals inside a try block. If an op_catch were in a try block, this would cause the phase to insert a Flush before one of the state bootstrapping SetLocals, which would generate invalid IR. Moving op_catch to be generated on its own at the end of a bytecode stream seemed like the most elegant solution since it better represents that we treat op_catch as an entrypoint. This is true both in the DFG and in the baseline and LLInt: we don't reach an op_catch via normal control flow. Because op_catch cannot throw, this will not break any previous semantics of op_catch. Logically, it'd be valid to split try blocks around any non-throwing bytecode operation. * CMakeLists.txt: * JavaScriptCore.xcodeproj/project.pbxproj: * bytecode/BytecodeDumper.cpp: (JSC::BytecodeDumper<Block>::dumpBytecode): * bytecode/BytecodeList.json: * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::finishCreation): (JSC::CodeBlock::ensureCatchLivenessIsComputedForBytecodeOffset): (JSC::CodeBlock::updateAllPredictionsAndCountLiveness): (JSC::CodeBlock::validate): * bytecode/CodeBlock.h: * bytecode/ValueProfile.h: (JSC::ValueProfile::ValueProfile): (JSC::ValueProfileAndOperandBuffer::ValueProfileAndOperandBuffer): (JSC::ValueProfileAndOperandBuffer::~ValueProfileAndOperandBuffer): (JSC::ValueProfileAndOperandBuffer::forEach): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::emitCatch): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: * bytecompiler/NodesCodegen.cpp: (JSC::TryNode::emitBytecode): * dfg/DFGAbstractInterpreterInlines.h: (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects): * dfg/DFGBackwardsCFG.h: (JSC::DFG::BackwardsCFG::BackwardsCFG): * dfg/DFGBasicBlock.cpp: (JSC::DFG::BasicBlock::BasicBlock): * dfg/DFGBasicBlock.h: (JSC::DFG::BasicBlock::findTerminal const): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::setDirect): (JSC::DFG::ByteCodeParser::flush): (JSC::DFG::ByteCodeParser::DelayedSetLocal::DelayedSetLocal): (JSC::DFG::ByteCodeParser::DelayedSetLocal::execute): (JSC::DFG::ByteCodeParser::parseBlock): (JSC::DFG::ByteCodeParser::parseCodeBlock): (JSC::DFG::ByteCodeParser::parse): * dfg/DFGCFG.h: (JSC::DFG::CFG::root): (JSC::DFG::CFG::roots): (JSC::DFG::CPSCFG::CPSCFG): (JSC::DFG::selectCFG): * dfg/DFGCPSRethreadingPhase.cpp: (JSC::DFG::CPSRethreadingPhase::specialCaseArguments): * dfg/DFGCSEPhase.cpp: * dfg/DFGClobberize.h: (JSC::DFG::clobberize): * dfg/DFGControlEquivalenceAnalysis.h: (JSC::DFG::ControlEquivalenceAnalysis::ControlEquivalenceAnalysis): * dfg/DFGDCEPhase.cpp: (JSC::DFG::DCEPhase::run): * dfg/DFGDisassembler.cpp: (JSC::DFG::Disassembler::createDumpList): * dfg/DFGDoesGC.cpp: (JSC::DFG::doesGC): * dfg/DFGDominators.h: (JSC::DFG::Dominators::Dominators): (JSC::DFG::ensureDominatorsForCFG): * dfg/DFGEdgeDominates.h: (JSC::DFG::EdgeDominates::EdgeDominates): (JSC::DFG::EdgeDominates::operator()): * dfg/DFGFixupPhase.cpp: (JSC::DFG::FixupPhase::fixupNode): (JSC::DFG::FixupPhase::fixupChecksInBlock): * dfg/DFGFlushFormat.h: * dfg/DFGGraph.cpp: (JSC::DFG::Graph::Graph): (JSC::DFG::unboxLoopNode): (JSC::DFG::Graph::dumpBlockHeader): (JSC::DFG::Graph::dump): (JSC::DFG::Graph::determineReachability): (JSC::DFG::Graph::invalidateCFG): (JSC::DFG::Graph::blocksInPreOrder): (JSC::DFG::Graph::blocksInPostOrder): (JSC::DFG::Graph::ensureCPSDominators): (JSC::DFG::Graph::ensureSSADominators): (JSC::DFG::Graph::ensureCPSNaturalLoops): (JSC::DFG::Graph::ensureSSANaturalLoops): (JSC::DFG::Graph::ensureBackwardsCFG): (JSC::DFG::Graph::ensureBackwardsDominators): (JSC::DFG::Graph::ensureControlEquivalenceAnalysis): (JSC::DFG::Graph::methodOfGettingAValueProfileFor): (JSC::DFG::Graph::clearCPSCFGData): (JSC::DFG::Graph::ensureDominators): Deleted. (JSC::DFG::Graph::ensurePrePostNumbering): Deleted. (JSC::DFG::Graph::ensureNaturalLoops): Deleted. * dfg/DFGGraph.h: (JSC::DFG::Graph::willCatchExceptionInMachineFrame): (JSC::DFG::Graph::isEntrypoint const): * dfg/DFGInPlaceAbstractState.cpp: (JSC::DFG::InPlaceAbstractState::initialize): (JSC::DFG::InPlaceAbstractState::mergeToSuccessors): * dfg/DFGJITCode.cpp: (JSC::DFG::JITCode::shrinkToFit): * dfg/DFGJITCode.h: (JSC::DFG::JITCode::catchOSREntryDataForBytecodeIndex): (JSC::DFG::JITCode::finalizeCatchOSREntrypoints): (JSC::DFG::JITCode::appendCatchEntrypoint): * dfg/DFGJITCompiler.cpp: (JSC::DFG::JITCompiler::compile): (JSC::DFG::JITCompiler::compileFunction): (JSC::DFG::JITCompiler::noticeCatchEntrypoint): (JSC::DFG::JITCompiler::noticeOSREntry): (JSC::DFG::JITCompiler::makeCatchOSREntryBuffer): * dfg/DFGJITCompiler.h: * dfg/DFGLICMPhase.cpp: (JSC::DFG::LICMPhase::run): (JSC::DFG::LICMPhase::attemptHoist): * dfg/DFGLiveCatchVariablePreservationPhase.cpp: (JSC::DFG::LiveCatchVariablePreservationPhase::run): (JSC::DFG::LiveCatchVariablePreservationPhase::isValidFlushLocation): (JSC::DFG::LiveCatchVariablePreservationPhase::handleBlockForTryCatch): (JSC::DFG::LiveCatchVariablePreservationPhase::newVariableAccessData): (JSC::DFG::LiveCatchVariablePreservationPhase::willCatchException): Deleted. (JSC::DFG::LiveCatchVariablePreservationPhase::handleBlock): Deleted. * dfg/DFGLoopPreHeaderCreationPhase.cpp: (JSC::DFG::createPreHeader): (JSC::DFG::LoopPreHeaderCreationPhase::run): * dfg/DFGMaximalFlushInsertionPhase.cpp: (JSC::DFG::MaximalFlushInsertionPhase::run): (JSC::DFG::MaximalFlushInsertionPhase::treatRegularBlock): (JSC::DFG::MaximalFlushInsertionPhase::treatRootBlock): * dfg/DFGMayExit.cpp: * dfg/DFGNaturalLoops.h: (JSC::DFG::NaturalLoops::NaturalLoops): * dfg/DFGNode.h: (JSC::DFG::Node::isSwitch const): (JSC::DFG::Node::successor): (JSC::DFG::Node::catchOSREntryIndex const): (JSC::DFG::Node::catchLocalPrediction): (JSC::DFG::Node::isSwitch): Deleted. * dfg/DFGNodeType.h: * dfg/DFGOSREntry.cpp: (JSC::DFG::prepareCatchOSREntry): * dfg/DFGOSREntry.h: * dfg/DFGOSREntrypointCreationPhase.cpp: (JSC::DFG::OSREntrypointCreationPhase::run): * dfg/DFGOSRExitCompilerCommon.cpp: (JSC::DFG::handleExitCounts): * dfg/DFGObjectAllocationSinkingPhase.cpp: * dfg/DFGPlan.cpp: (JSC::DFG::Plan::compileInThreadImpl): * dfg/DFGPrePostNumbering.cpp: (JSC::DFG::PrePostNumbering::PrePostNumbering): Deleted. (JSC::DFG::PrePostNumbering::~PrePostNumbering): Deleted. (WTF::printInternal): Deleted. * dfg/DFGPrePostNumbering.h: (): Deleted. (JSC::DFG::PrePostNumbering::preNumber const): Deleted. (JSC::DFG::PrePostNumbering::postNumber const): Deleted. (JSC::DFG::PrePostNumbering::isStrictAncestorOf const): Deleted. (JSC::DFG::PrePostNumbering::isAncestorOf const): Deleted. (JSC::DFG::PrePostNumbering::isStrictDescendantOf const): Deleted. (JSC::DFG::PrePostNumbering::isDescendantOf const): Deleted. (JSC::DFG::PrePostNumbering::edgeKind const): Deleted. * dfg/DFGPredictionInjectionPhase.cpp: (JSC::DFG::PredictionInjectionPhase::run): * dfg/DFGPredictionPropagationPhase.cpp: * dfg/DFGPutStackSinkingPhase.cpp: * dfg/DFGSSACalculator.cpp: (JSC::DFG::SSACalculator::nonLocalReachingDef): (JSC::DFG::SSACalculator::reachingDefAtTail): * dfg/DFGSSACalculator.h: (JSC::DFG::SSACalculator::computePhis): * dfg/DFGSSAConversionPhase.cpp: (JSC::DFG::SSAConversionPhase::run): (JSC::DFG::performSSAConversion): * dfg/DFGSafeToExecute.h: (JSC::DFG::safeToExecute): * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCurrentBlock): (JSC::DFG::SpeculativeJIT::checkArgumentTypes): (JSC::DFG::SpeculativeJIT::createOSREntries): (JSC::DFG::SpeculativeJIT::linkOSREntries): * dfg/DFGSpeculativeJIT32_64.cpp: (JSC::DFG::SpeculativeJIT::compile): * dfg/DFGSpeculativeJIT64.cpp: (JSC::DFG::SpeculativeJIT::compile): * dfg/DFGStaticExecutionCountEstimationPhase.cpp: (JSC::DFG::StaticExecutionCountEstimationPhase::run): * dfg/DFGStrengthReductionPhase.cpp: (JSC::DFG::StrengthReductionPhase::handleNode): * dfg/DFGTierUpCheckInjectionPhase.cpp: (JSC::DFG::TierUpCheckInjectionPhase::run): (JSC::DFG::TierUpCheckInjectionPhase::buildNaturalLoopToLoopHintMap): * dfg/DFGTypeCheckHoistingPhase.cpp: (JSC::DFG::TypeCheckHoistingPhase::run): * dfg/DFGValidate.cpp: * ftl/FTLLink.cpp: (JSC::FTL::link): * ftl/FTLLowerDFGToB3.cpp: (JSC::FTL::DFG::LowerDFGToB3::lower): (JSC::FTL::DFG::LowerDFGToB3::safelyInvalidateAfterTermination): (JSC::FTL::DFG::LowerDFGToB3::isValid): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_catch): * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_catch): * jit/JITOperations.cpp: * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: Source/WTF: This patch generalizes the BackwardsGraph fake root into a more generalizable class called SingleRootGraph. SingleRootGraph exposes the general graph interface used in Dominators and NaturalLoops. SingleRootGraph takes as input a graph with the normal graph interface, but also allows the input graph to contain more than one root. SingleRootGraph then exposes a single root, which it creates, that has an outgoing edge to all the roots in the original graph. * WTF.xcodeproj/project.pbxproj: * wtf/BackwardsGraph.h: (WTF::BackwardsGraph::dump const): (WTF::BackwardsGraph::rootName): Deleted. (WTF::BackwardsGraph::Node::Node): Deleted. (WTF::BackwardsGraph::Node::root): Deleted. (WTF::BackwardsGraph::Node::operator== const): Deleted. (WTF::BackwardsGraph::Node::operator!= const): Deleted. (WTF::BackwardsGraph::Node::operator bool const): Deleted. (WTF::BackwardsGraph::Node::isRoot const): Deleted. (WTF::BackwardsGraph::Node::node const): Deleted. (): Deleted. (WTF::BackwardsGraph::Set::Set): Deleted. (WTF::BackwardsGraph::Set::add): Deleted. (WTF::BackwardsGraph::Set::remove): Deleted. (WTF::BackwardsGraph::Set::contains): Deleted. (WTF::BackwardsGraph::Set::dump const): Deleted. (WTF::BackwardsGraph::Map::Map): Deleted. (WTF::BackwardsGraph::Map::clear): Deleted. (WTF::BackwardsGraph::Map::size const): Deleted. (WTF::BackwardsGraph::Map::operator[]): Deleted. (WTF::BackwardsGraph::Map::operator[] const): Deleted. * wtf/Dominators.h: (WTF::Dominators::Dominators): (WTF::Dominators::forAllBlocksInIteratedDominanceFrontierOf): (WTF::Dominators::forAllBlocksInPrunedIteratedDominanceFrontierOf): (WTF::Dominators::iteratedDominanceFrontierOf const): (WTF::Dominators::forAllBlocksInIteratedDominanceFrontierOfImpl const): * wtf/SingleRootGraph.h: Added. (WTF::SingleRootGraphNode::rootName): (WTF::SingleRootGraphNode::SingleRootGraphNode): (WTF::SingleRootGraphNode::root): (WTF::SingleRootGraphNode::operator== const): (WTF::SingleRootGraphNode::operator!= const): (WTF::SingleRootGraphNode::operator bool const): (WTF::SingleRootGraphNode::isRoot const): (WTF::SingleRootGraphNode::node const): (WTF::SingleRootGraphSet::add): (WTF::SingleRootGraphSet::remove): (WTF::SingleRootGraphSet::contains): (WTF::SingleRootGraphSet::dump const): (WTF::SingleRootMap::SingleRootMap): (WTF::SingleRootMap::clear): (WTF::SingleRootMap::size const): (WTF::SingleRootMap::operator[]): (WTF::SingleRootMap::operator[] const): (WTF::SingleRootGraph::SingleRootGraph): (WTF::SingleRootGraph::root const): (WTF::SingleRootGraph::newMap): (WTF::SingleRootGraph::successors const): (WTF::SingleRootGraph::predecessors const): (WTF::SingleRootGraph::index const): (WTF::SingleRootGraph::node const): (WTF::SingleRootGraph::numNodes const): (WTF::SingleRootGraph::dump const): (WTF::SingleRootGraph::assertIsConsistent const): Canonical link: https://commits.webkit.org/192644@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@221196 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2017-08-25 18:26:15 +00:00
#include <wtf/SingleRootGraph.h>
BackwardsGraph needs to consider back edges as the backward's root successor https://bugs.webkit.org/show_bug.cgi?id=195991 Reviewed by Filip Pizlo. JSTests: * stress/map-b3-licm-infinite-loop.js: Added. Source/JavaScriptCore: * b3/testb3.cpp: (JSC::B3::testInfiniteLoopDoesntCauseBadHoisting): (JSC::B3::run): Source/WTF: Previously, our backwards graph analysis was slightly wrong. The idea of backwards graph is that the root of the graph has edges to terminals in the original graph. And then the original directed edges in the graph are flipped. However, we weren't considering loops as a form of terminality. For example, we wouldn't consider an infinite loop as a terminal. So there were no edges from the root to a node in the infinite loop. This lead us to make mistakes when we used backwards dominators to compute control flow equivalence. This is better understood in an example: ``` preheader: while (1) { if (!isCell(v)) continue; load structure ID if (cond) continue; return } ``` In the previous version of this algorithm, the only edge from the backwards root would be to the block containing the return. This would lead us to believe that the loading of the structureID backwards dominates the preheader, leading us to believe it's control flow equivalent to preheader. This is obviously wrong, since we can loop forever if "v" isn't a cell. The solution here is to treat any backedge in the graph as a "terminal" node. Since a backedge implies the existence of a loop. In the above example, the backwards root now has an edge to both blocks with "continue". This prevents us from falsely claiming that the return is control flow equivalent with the preheader. This patch uses DFS spanning trees to compute back edges. An edge u->v is a back edge when u is a descendent of v in the DFS spanning tree of the Graph. * WTF.xcodeproj/project.pbxproj: * wtf/BackwardsGraph.h: (WTF::BackwardsGraph::BackwardsGraph): * wtf/SpanningTree.h: Added. (SpanningTree::SpanningTree): (SpanningTree::isDescendent): Canonical link: https://commits.webkit.org/210659@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@243639 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2019-03-29 04:42:42 +00:00
#include <wtf/SpanningTree.h>
DFG::LICMPhase shouldn't hoist type checks unless it knows that the check will succeed at the loop pre-header https://bugs.webkit.org/show_bug.cgi?id=144527 Reviewed by Saam Barati. Source/JavaScriptCore: This adds a control flow equivalence analysis (called ControlEquivalenceAnalysis) based on dominator analysis over the backwards CFG. Two basic blocks are control flow equivalent if the execution of one implies that the other one must also execute. It means that the two blocks' forward and backward dominance are reciprocated: (A dom B and B backdom A) or (B dom A and A backdom B). LICM now uses it to become more conservative about hoisting checks, if this has caused problems in the past. If we hoist something that may exit from a block that was not control equivalent to the pre-header then it's possible that the node's speculation will fail even though it wouldn't have if it wasn't hoisted. So, we flag these nodes' origins as being "wasHoisted" and we track all of their exits as "HoistingFailed". LICM will turn off such speculative hoisting if the CodeBlock from which we are hoisting had the HoistingFailed exit kind. Note that this deliberately still allows us to hoist things that may exit even if they are not control equivalent to the pre-header. This is necessary because the profitability of hoisting is so huge in all of the cases that we're aware of that it's worth giving it a shot. This is neutral on macrobenchmarks since none of the benchmarks we track have a hoistable operation that would exit only if hoisted. I added microbenchmarks to illustrate the problem and two of them speed up by ~40% while one of them is neutral (Int52 saves us from having problems on that program even though LICM previously did the wrong thing). * JavaScriptCore.xcodeproj/project.pbxproj: * bytecode/ExitKind.cpp: (JSC::exitKindToString): * bytecode/ExitKind.h: * dfg/DFGAtTailAbstractState.h: (JSC::DFG::AtTailAbstractState::operator bool): (JSC::DFG::AtTailAbstractState::initializeTo): * dfg/DFGBackwardsCFG.h: Added. (JSC::DFG::BackwardsCFG::BackwardsCFG): * dfg/DFGBackwardsDominators.h: Added. (JSC::DFG::BackwardsDominators::BackwardsDominators): * dfg/DFGCommon.h: (JSC::DFG::checkAndSet): Deleted. * dfg/DFGControlEquivalenceAnalysis.h: Added. (JSC::DFG::ControlEquivalenceAnalysis::ControlEquivalenceAnalysis): (JSC::DFG::ControlEquivalenceAnalysis::dominatesEquivalently): (JSC::DFG::ControlEquivalenceAnalysis::areEquivalent): * dfg/DFGGraph.cpp: (JSC::DFG::Graph::dump): (JSC::DFG::Graph::dumpBlockHeader): (JSC::DFG::Graph::invalidateCFG): (JSC::DFG::Graph::substituteGetLocal): (JSC::DFG::Graph::handleAssertionFailure): (JSC::DFG::Graph::ensureDominators): (JSC::DFG::Graph::ensurePrePostNumbering): (JSC::DFG::Graph::ensureNaturalLoops): (JSC::DFG::Graph::ensureBackwardsCFG): (JSC::DFG::Graph::ensureBackwardsDominators): (JSC::DFG::Graph::ensureControlEquivalenceAnalysis): (JSC::DFG::Graph::methodOfGettingAValueProfileFor): * dfg/DFGGraph.h: (JSC::DFG::Graph::hasDebuggerEnabled): * dfg/DFGInPlaceAbstractState.h: (JSC::DFG::InPlaceAbstractState::operator bool): (JSC::DFG::InPlaceAbstractState::createValueForNode): (JSC::DFG::InPlaceAbstractState::forNode): * dfg/DFGLICMPhase.cpp: (JSC::DFG::LICMPhase::run): (JSC::DFG::LICMPhase::attemptHoist): * dfg/DFGMayExit.cpp: (JSC::DFG::mayExit): * dfg/DFGMayExit.h: * dfg/DFGNode.h: * dfg/DFGNodeOrigin.cpp: (JSC::DFG::NodeOrigin::dump): * dfg/DFGNodeOrigin.h: (JSC::DFG::NodeOrigin::takeValidExit): (JSC::DFG::NodeOrigin::withWasHoisted): (JSC::DFG::NodeOrigin::forInsertingAfter): * dfg/DFGNullAbstractState.h: Added. (JSC::DFG::NullAbstractState::NullAbstractState): (JSC::DFG::NullAbstractState::operator bool): (JSC::DFG::NullAbstractState::forNode): * dfg/DFGOSRExit.cpp: (JSC::DFG::OSRExit::OSRExit): * dfg/DFGOSRExitBase.cpp: (JSC::DFG::OSRExitBase::considerAddingAsFrequentExitSiteSlow): * dfg/DFGOSRExitBase.h: (JSC::DFG::OSRExitBase::OSRExitBase): * dfg/DFGTypeCheckHoistingPhase.cpp: (JSC::DFG::TypeCheckHoistingPhase::run): * ftl/FTLOSRExit.cpp: (JSC::FTL::OSRExitDescriptor::prepareOSRExitHandle): (JSC::FTL::OSRExit::OSRExit): * ftl/FTLOSRExit.h: Source/WTF: This adds an adaptor for graphs called BackwardsGraph. The WTF graph framework is based on passing around a Graph template argument that follows the protocol shared by DFG::CFG, B3::CFG, and Air::CFG. These graphs always have a single root node but may have many leaf nodes. This new BackwardsGraph adaptor reverses the graph by creating a synthetic return node that it uses as the root in the inverted graph. This currently may resort to some heuristics in programs that have an infinite loop, but other than that, it'll work well in the general case. This allows us to say Dominators<BackwardsGraph<some graph type>> as a way of computing backwards dominators, which then allows us to easily answer control flow equivalence queries. * CMakeLists.txt: * WTF.xcodeproj/project.pbxproj: * wtf/BackwardsGraph.h: Added. (WTF::BackwardsGraph::Node::Node): (WTF::BackwardsGraph::Node::root): (WTF::BackwardsGraph::Node::operator==): (WTF::BackwardsGraph::Node::operator!=): (WTF::BackwardsGraph::Node::operator bool): (WTF::BackwardsGraph::Node::isRoot): (WTF::BackwardsGraph::Node::node): (WTF::BackwardsGraph::Set::Set): (WTF::BackwardsGraph::Set::add): (WTF::BackwardsGraph::Set::remove): (WTF::BackwardsGraph::Set::contains): (WTF::BackwardsGraph::Set::dump): (WTF::BackwardsGraph::Map::Map): (WTF::BackwardsGraph::Map::clear): (WTF::BackwardsGraph::Map::size): (WTF::BackwardsGraph::Map::operator[]): (WTF::BackwardsGraph::BackwardsGraph): (WTF::BackwardsGraph::root): (WTF::BackwardsGraph::newMap): (WTF::BackwardsGraph::successors): (WTF::BackwardsGraph::predecessors): (WTF::BackwardsGraph::index): (WTF::BackwardsGraph::node): (WTF::BackwardsGraph::numNodes): (WTF::BackwardsGraph::dump): * wtf/Dominators.h: (WTF::Dominators::Dominators): (WTF::Dominators::dump): (WTF::Dominators::LengauerTarjan::computeDepthFirstPreNumbering): * wtf/GraphNodeWorklist.h: (WTF::GraphNodeWith::GraphNodeWith): (WTF::GraphNodeWith::operator bool): * wtf/StdLibExtras.h: (WTF::callStatelessLambda): (WTF::checkAndSet): LayoutTests: Add tests for LICM hoisting things that would only exit if hoisted. * js/regress/licm-dragons-expected.txt: Added. * js/regress/licm-dragons-out-of-bounds-expected.txt: Added. * js/regress/licm-dragons-out-of-bounds.html: Added. * js/regress/licm-dragons-overflow-expected.txt: Added. * js/regress/licm-dragons-overflow.html: Added. * js/regress/licm-dragons.html: Added. * js/regress/script-tests/licm-dragons-out-of-bounds.js: Added. (foo): * js/regress/script-tests/licm-dragons-overflow.js: Added. (foo): * js/regress/script-tests/licm-dragons.js: Added. (foo): Canonical link: https://commits.webkit.org/176032@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@201182 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2016-05-19 21:25:29 +00:00
#include <wtf/StdLibExtras.h>
namespace WTF {
template<typename Graph>
class BackwardsGraph {
WTF_MAKE_NONCOPYABLE(BackwardsGraph);
WTF_MAKE_FAST_ALLOCATED;
public:
Support compiling catch in the DFG https://bugs.webkit.org/show_bug.cgi?id=174590 <rdar://problem/34047845> Reviewed by Filip Pizlo. JSTests: * microbenchmarks/delta-blue-try-catch.js: Added. (exception): (value): (OrderedCollection): (OrderedCollection.prototype.add): (OrderedCollection.prototype.at): (OrderedCollection.prototype.size): (OrderedCollection.prototype.removeFirst): (OrderedCollection.prototype.remove): (Strength): (Strength.stronger): (Strength.weaker): (Strength.weakestOf): (Strength.strongest): (Strength.prototype.nextWeaker): (Constraint): (Constraint.prototype.addConstraint): (Constraint.prototype.satisfy): (Constraint.prototype.destroyConstraint): (Constraint.prototype.isInput): (UnaryConstraint): (UnaryConstraint.prototype.addToGraph): (UnaryConstraint.prototype.chooseMethod): (UnaryConstraint.prototype.isSatisfied): (UnaryConstraint.prototype.markInputs): (UnaryConstraint.prototype.output): (UnaryConstraint.prototype.recalculate): (UnaryConstraint.prototype.markUnsatisfied): (UnaryConstraint.prototype.inputsKnown): (UnaryConstraint.prototype.removeFromGraph): (StayConstraint): (StayConstraint.prototype.execute): (EditConstraint.prototype.isInput): (EditConstraint.prototype.execute): (BinaryConstraint): (BinaryConstraint.prototype.chooseMethod): (BinaryConstraint.prototype.addToGraph): (BinaryConstraint.prototype.isSatisfied): (BinaryConstraint.prototype.markInputs): (BinaryConstraint.prototype.input): (BinaryConstraint.prototype.output): (BinaryConstraint.prototype.recalculate): (BinaryConstraint.prototype.markUnsatisfied): (BinaryConstraint.prototype.inputsKnown): (BinaryConstraint.prototype.removeFromGraph): (ScaleConstraint): (ScaleConstraint.prototype.addToGraph): (ScaleConstraint.prototype.removeFromGraph): (ScaleConstraint.prototype.markInputs): (ScaleConstraint.prototype.execute): (ScaleConstraint.prototype.recalculate): (EqualityConstraint): (EqualityConstraint.prototype.execute): (Variable): (Variable.prototype.addConstraint): (Variable.prototype.removeConstraint): (Planner): (Planner.prototype.incrementalAdd): (Planner.prototype.incrementalRemove): (Planner.prototype.newMark): (Planner.prototype.makePlan): (Planner.prototype.extractPlanFromConstraints): (Planner.prototype.addPropagate): (Planner.prototype.removePropagateFrom): (Planner.prototype.addConstraintsConsumingTo): (Plan): (Plan.prototype.addConstraint): (Plan.prototype.size): (Plan.prototype.constraintAt): (Plan.prototype.execute): (chainTest): (projectionTest): (change): (deltaBlue): * microbenchmarks/fake-iterators-that-throw-when-finished.js: Added. (assert): (Numbers): (Numbers.prototype.next): (return.Transpose): (return.Transpose.prototype.next): (transpose): (verifyEven): (verifyString): (foo): (runIterators): * microbenchmarks/try-catch-word-count.js: Added. (let.assert): (EOF): (let.texts): (let.o.apply): (foo): (bar): (f): (run): (test1): (test2): (test3): (fn): (A): (B): (A.prototype.getValue): (B.prototype.getParentValue): (strlen): (sum.0): (test): (result.test.o): (set add.set add): (set forEach): (stringHash): (set if): (testFunction): (set delete.set has.set add): * stress/catch-set-argument-speculation-failure.js: Added. (o): (e): (e2): (escape): (baz): (noInline.run): (noInline): * stress/osr-enter-to-catch-with-set-local-type-check-failure.js: Added. (foo): (e): (baz): (bar): Source/JavaScriptCore: This patch implements OSR entry into op_catch in the DFG. We will support OSR entry into the FTL in a followup: https://bugs.webkit.org/show_bug.cgi?id=175396 To implement catch in the DFG, this patch introduces the concept of multiple entrypoints into CPS/LoadStore DFG IR. A lot of this patch is stringing this concept through the DFG. Many phases used to assume that Graph::block(0) is the only root, and this patch contains many straight forward changes generalizing the code to handle more than one entrypoint. A main building block of this is moving to two CFG types: SSACFG and CPSCFG. SSACFG is the same CFG we used to have. CPSCFG is a new type that introduces a fake root that has an outgoing edge to all the entrypoints. This allows our existing graph algorithms to Just Work over CPSCFG. For example, there is now the concept of SSADominators vs CPSDominators, and SSANaturalLoops vs CPSNaturalLoops. The way we compile the catch entrypoint is by bootstrapping the state of the program by loading all live bytecode locals from a buffer. The OSR entry code will store all live values into that buffer before jumping to the entrypoint. The OSR entry code is also responsible for performing type proofs of the arguments before doing an OSR entry. If there is a type mismatch, it's not legal to OSR enter into the DFG compilation. Currently, each catch entrypoint knows the argument type proofs it must perform to enter into the DFG. Currently, all entrypoints' arguments flush format are unified via ArgumentPosition, but this is just an implementation detail. The code is written more generally to assume that each entrypoint may perform its own distinct proof. op_catch now performs value profiling for all live bytecode locals in the LLInt and baseline JIT. This information is then fed into the DFG via the ExtractCatchLocal node in the prediction propagation phase. This patch also changes how we generate op_catch in bytecode. All op_catches are now split out at the end of the program in bytecode. This ensures that no op_catch is inside a try block. This is needed to ensure correctness in the DFGLiveCatchVariablePreservationPhase. That phase only inserts flushes before SetLocals inside a try block. If an op_catch were in a try block, this would cause the phase to insert a Flush before one of the state bootstrapping SetLocals, which would generate invalid IR. Moving op_catch to be generated on its own at the end of a bytecode stream seemed like the most elegant solution since it better represents that we treat op_catch as an entrypoint. This is true both in the DFG and in the baseline and LLInt: we don't reach an op_catch via normal control flow. Because op_catch cannot throw, this will not break any previous semantics of op_catch. Logically, it'd be valid to split try blocks around any non-throwing bytecode operation. * CMakeLists.txt: * JavaScriptCore.xcodeproj/project.pbxproj: * bytecode/BytecodeDumper.cpp: (JSC::BytecodeDumper<Block>::dumpBytecode): * bytecode/BytecodeList.json: * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::finishCreation): (JSC::CodeBlock::ensureCatchLivenessIsComputedForBytecodeOffset): (JSC::CodeBlock::updateAllPredictionsAndCountLiveness): (JSC::CodeBlock::validate): * bytecode/CodeBlock.h: * bytecode/ValueProfile.h: (JSC::ValueProfile::ValueProfile): (JSC::ValueProfileAndOperandBuffer::ValueProfileAndOperandBuffer): (JSC::ValueProfileAndOperandBuffer::~ValueProfileAndOperandBuffer): (JSC::ValueProfileAndOperandBuffer::forEach): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::emitCatch): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: * bytecompiler/NodesCodegen.cpp: (JSC::TryNode::emitBytecode): * dfg/DFGAbstractInterpreterInlines.h: (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects): * dfg/DFGBackwardsCFG.h: (JSC::DFG::BackwardsCFG::BackwardsCFG): * dfg/DFGBasicBlock.cpp: (JSC::DFG::BasicBlock::BasicBlock): * dfg/DFGBasicBlock.h: (JSC::DFG::BasicBlock::findTerminal const): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::setDirect): (JSC::DFG::ByteCodeParser::flush): (JSC::DFG::ByteCodeParser::DelayedSetLocal::DelayedSetLocal): (JSC::DFG::ByteCodeParser::DelayedSetLocal::execute): (JSC::DFG::ByteCodeParser::parseBlock): (JSC::DFG::ByteCodeParser::parseCodeBlock): (JSC::DFG::ByteCodeParser::parse): * dfg/DFGCFG.h: (JSC::DFG::CFG::root): (JSC::DFG::CFG::roots): (JSC::DFG::CPSCFG::CPSCFG): (JSC::DFG::selectCFG): * dfg/DFGCPSRethreadingPhase.cpp: (JSC::DFG::CPSRethreadingPhase::specialCaseArguments): * dfg/DFGCSEPhase.cpp: * dfg/DFGClobberize.h: (JSC::DFG::clobberize): * dfg/DFGControlEquivalenceAnalysis.h: (JSC::DFG::ControlEquivalenceAnalysis::ControlEquivalenceAnalysis): * dfg/DFGDCEPhase.cpp: (JSC::DFG::DCEPhase::run): * dfg/DFGDisassembler.cpp: (JSC::DFG::Disassembler::createDumpList): * dfg/DFGDoesGC.cpp: (JSC::DFG::doesGC): * dfg/DFGDominators.h: (JSC::DFG::Dominators::Dominators): (JSC::DFG::ensureDominatorsForCFG): * dfg/DFGEdgeDominates.h: (JSC::DFG::EdgeDominates::EdgeDominates): (JSC::DFG::EdgeDominates::operator()): * dfg/DFGFixupPhase.cpp: (JSC::DFG::FixupPhase::fixupNode): (JSC::DFG::FixupPhase::fixupChecksInBlock): * dfg/DFGFlushFormat.h: * dfg/DFGGraph.cpp: (JSC::DFG::Graph::Graph): (JSC::DFG::unboxLoopNode): (JSC::DFG::Graph::dumpBlockHeader): (JSC::DFG::Graph::dump): (JSC::DFG::Graph::determineReachability): (JSC::DFG::Graph::invalidateCFG): (JSC::DFG::Graph::blocksInPreOrder): (JSC::DFG::Graph::blocksInPostOrder): (JSC::DFG::Graph::ensureCPSDominators): (JSC::DFG::Graph::ensureSSADominators): (JSC::DFG::Graph::ensureCPSNaturalLoops): (JSC::DFG::Graph::ensureSSANaturalLoops): (JSC::DFG::Graph::ensureBackwardsCFG): (JSC::DFG::Graph::ensureBackwardsDominators): (JSC::DFG::Graph::ensureControlEquivalenceAnalysis): (JSC::DFG::Graph::methodOfGettingAValueProfileFor): (JSC::DFG::Graph::clearCPSCFGData): (JSC::DFG::Graph::ensureDominators): Deleted. (JSC::DFG::Graph::ensurePrePostNumbering): Deleted. (JSC::DFG::Graph::ensureNaturalLoops): Deleted. * dfg/DFGGraph.h: (JSC::DFG::Graph::willCatchExceptionInMachineFrame): (JSC::DFG::Graph::isEntrypoint const): * dfg/DFGInPlaceAbstractState.cpp: (JSC::DFG::InPlaceAbstractState::initialize): (JSC::DFG::InPlaceAbstractState::mergeToSuccessors): * dfg/DFGJITCode.cpp: (JSC::DFG::JITCode::shrinkToFit): * dfg/DFGJITCode.h: (JSC::DFG::JITCode::catchOSREntryDataForBytecodeIndex): (JSC::DFG::JITCode::finalizeCatchOSREntrypoints): (JSC::DFG::JITCode::appendCatchEntrypoint): * dfg/DFGJITCompiler.cpp: (JSC::DFG::JITCompiler::compile): (JSC::DFG::JITCompiler::compileFunction): (JSC::DFG::JITCompiler::noticeCatchEntrypoint): (JSC::DFG::JITCompiler::noticeOSREntry): (JSC::DFG::JITCompiler::makeCatchOSREntryBuffer): * dfg/DFGJITCompiler.h: * dfg/DFGLICMPhase.cpp: (JSC::DFG::LICMPhase::run): (JSC::DFG::LICMPhase::attemptHoist): * dfg/DFGLiveCatchVariablePreservationPhase.cpp: (JSC::DFG::LiveCatchVariablePreservationPhase::run): (JSC::DFG::LiveCatchVariablePreservationPhase::isValidFlushLocation): (JSC::DFG::LiveCatchVariablePreservationPhase::handleBlockForTryCatch): (JSC::DFG::LiveCatchVariablePreservationPhase::newVariableAccessData): (JSC::DFG::LiveCatchVariablePreservationPhase::willCatchException): Deleted. (JSC::DFG::LiveCatchVariablePreservationPhase::handleBlock): Deleted. * dfg/DFGLoopPreHeaderCreationPhase.cpp: (JSC::DFG::createPreHeader): (JSC::DFG::LoopPreHeaderCreationPhase::run): * dfg/DFGMaximalFlushInsertionPhase.cpp: (JSC::DFG::MaximalFlushInsertionPhase::run): (JSC::DFG::MaximalFlushInsertionPhase::treatRegularBlock): (JSC::DFG::MaximalFlushInsertionPhase::treatRootBlock): * dfg/DFGMayExit.cpp: * dfg/DFGNaturalLoops.h: (JSC::DFG::NaturalLoops::NaturalLoops): * dfg/DFGNode.h: (JSC::DFG::Node::isSwitch const): (JSC::DFG::Node::successor): (JSC::DFG::Node::catchOSREntryIndex const): (JSC::DFG::Node::catchLocalPrediction): (JSC::DFG::Node::isSwitch): Deleted. * dfg/DFGNodeType.h: * dfg/DFGOSREntry.cpp: (JSC::DFG::prepareCatchOSREntry): * dfg/DFGOSREntry.h: * dfg/DFGOSREntrypointCreationPhase.cpp: (JSC::DFG::OSREntrypointCreationPhase::run): * dfg/DFGOSRExitCompilerCommon.cpp: (JSC::DFG::handleExitCounts): * dfg/DFGObjectAllocationSinkingPhase.cpp: * dfg/DFGPlan.cpp: (JSC::DFG::Plan::compileInThreadImpl): * dfg/DFGPrePostNumbering.cpp: (JSC::DFG::PrePostNumbering::PrePostNumbering): Deleted. (JSC::DFG::PrePostNumbering::~PrePostNumbering): Deleted. (WTF::printInternal): Deleted. * dfg/DFGPrePostNumbering.h: (): Deleted. (JSC::DFG::PrePostNumbering::preNumber const): Deleted. (JSC::DFG::PrePostNumbering::postNumber const): Deleted. (JSC::DFG::PrePostNumbering::isStrictAncestorOf const): Deleted. (JSC::DFG::PrePostNumbering::isAncestorOf const): Deleted. (JSC::DFG::PrePostNumbering::isStrictDescendantOf const): Deleted. (JSC::DFG::PrePostNumbering::isDescendantOf const): Deleted. (JSC::DFG::PrePostNumbering::edgeKind const): Deleted. * dfg/DFGPredictionInjectionPhase.cpp: (JSC::DFG::PredictionInjectionPhase::run): * dfg/DFGPredictionPropagationPhase.cpp: * dfg/DFGPutStackSinkingPhase.cpp: * dfg/DFGSSACalculator.cpp: (JSC::DFG::SSACalculator::nonLocalReachingDef): (JSC::DFG::SSACalculator::reachingDefAtTail): * dfg/DFGSSACalculator.h: (JSC::DFG::SSACalculator::computePhis): * dfg/DFGSSAConversionPhase.cpp: (JSC::DFG::SSAConversionPhase::run): (JSC::DFG::performSSAConversion): * dfg/DFGSafeToExecute.h: (JSC::DFG::safeToExecute): * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCurrentBlock): (JSC::DFG::SpeculativeJIT::checkArgumentTypes): (JSC::DFG::SpeculativeJIT::createOSREntries): (JSC::DFG::SpeculativeJIT::linkOSREntries): * dfg/DFGSpeculativeJIT32_64.cpp: (JSC::DFG::SpeculativeJIT::compile): * dfg/DFGSpeculativeJIT64.cpp: (JSC::DFG::SpeculativeJIT::compile): * dfg/DFGStaticExecutionCountEstimationPhase.cpp: (JSC::DFG::StaticExecutionCountEstimationPhase::run): * dfg/DFGStrengthReductionPhase.cpp: (JSC::DFG::StrengthReductionPhase::handleNode): * dfg/DFGTierUpCheckInjectionPhase.cpp: (JSC::DFG::TierUpCheckInjectionPhase::run): (JSC::DFG::TierUpCheckInjectionPhase::buildNaturalLoopToLoopHintMap): * dfg/DFGTypeCheckHoistingPhase.cpp: (JSC::DFG::TypeCheckHoistingPhase::run): * dfg/DFGValidate.cpp: * ftl/FTLLink.cpp: (JSC::FTL::link): * ftl/FTLLowerDFGToB3.cpp: (JSC::FTL::DFG::LowerDFGToB3::lower): (JSC::FTL::DFG::LowerDFGToB3::safelyInvalidateAfterTermination): (JSC::FTL::DFG::LowerDFGToB3::isValid): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_catch): * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_catch): * jit/JITOperations.cpp: * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: Source/WTF: This patch generalizes the BackwardsGraph fake root into a more generalizable class called SingleRootGraph. SingleRootGraph exposes the general graph interface used in Dominators and NaturalLoops. SingleRootGraph takes as input a graph with the normal graph interface, but also allows the input graph to contain more than one root. SingleRootGraph then exposes a single root, which it creates, that has an outgoing edge to all the roots in the original graph. * WTF.xcodeproj/project.pbxproj: * wtf/BackwardsGraph.h: (WTF::BackwardsGraph::dump const): (WTF::BackwardsGraph::rootName): Deleted. (WTF::BackwardsGraph::Node::Node): Deleted. (WTF::BackwardsGraph::Node::root): Deleted. (WTF::BackwardsGraph::Node::operator== const): Deleted. (WTF::BackwardsGraph::Node::operator!= const): Deleted. (WTF::BackwardsGraph::Node::operator bool const): Deleted. (WTF::BackwardsGraph::Node::isRoot const): Deleted. (WTF::BackwardsGraph::Node::node const): Deleted. (): Deleted. (WTF::BackwardsGraph::Set::Set): Deleted. (WTF::BackwardsGraph::Set::add): Deleted. (WTF::BackwardsGraph::Set::remove): Deleted. (WTF::BackwardsGraph::Set::contains): Deleted. (WTF::BackwardsGraph::Set::dump const): Deleted. (WTF::BackwardsGraph::Map::Map): Deleted. (WTF::BackwardsGraph::Map::clear): Deleted. (WTF::BackwardsGraph::Map::size const): Deleted. (WTF::BackwardsGraph::Map::operator[]): Deleted. (WTF::BackwardsGraph::Map::operator[] const): Deleted. * wtf/Dominators.h: (WTF::Dominators::Dominators): (WTF::Dominators::forAllBlocksInIteratedDominanceFrontierOf): (WTF::Dominators::forAllBlocksInPrunedIteratedDominanceFrontierOf): (WTF::Dominators::iteratedDominanceFrontierOf const): (WTF::Dominators::forAllBlocksInIteratedDominanceFrontierOfImpl const): * wtf/SingleRootGraph.h: Added. (WTF::SingleRootGraphNode::rootName): (WTF::SingleRootGraphNode::SingleRootGraphNode): (WTF::SingleRootGraphNode::root): (WTF::SingleRootGraphNode::operator== const): (WTF::SingleRootGraphNode::operator!= const): (WTF::SingleRootGraphNode::operator bool const): (WTF::SingleRootGraphNode::isRoot const): (WTF::SingleRootGraphNode::node const): (WTF::SingleRootGraphSet::add): (WTF::SingleRootGraphSet::remove): (WTF::SingleRootGraphSet::contains): (WTF::SingleRootGraphSet::dump const): (WTF::SingleRootMap::SingleRootMap): (WTF::SingleRootMap::clear): (WTF::SingleRootMap::size const): (WTF::SingleRootMap::operator[]): (WTF::SingleRootMap::operator[] const): (WTF::SingleRootGraph::SingleRootGraph): (WTF::SingleRootGraph::root const): (WTF::SingleRootGraph::newMap): (WTF::SingleRootGraph::successors const): (WTF::SingleRootGraph::predecessors const): (WTF::SingleRootGraph::index const): (WTF::SingleRootGraph::node const): (WTF::SingleRootGraph::numNodes const): (WTF::SingleRootGraph::dump const): (WTF::SingleRootGraph::assertIsConsistent const): Canonical link: https://commits.webkit.org/192644@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@221196 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2017-08-25 18:26:15 +00:00
using Node = SingleRootGraphNode<Graph>;
using Set = SingleRootGraphSet<Graph>;
template <typename T> using Map = SingleRootMap<T, Graph>;
DFG::LICMPhase shouldn't hoist type checks unless it knows that the check will succeed at the loop pre-header https://bugs.webkit.org/show_bug.cgi?id=144527 Reviewed by Saam Barati. Source/JavaScriptCore: This adds a control flow equivalence analysis (called ControlEquivalenceAnalysis) based on dominator analysis over the backwards CFG. Two basic blocks are control flow equivalent if the execution of one implies that the other one must also execute. It means that the two blocks' forward and backward dominance are reciprocated: (A dom B and B backdom A) or (B dom A and A backdom B). LICM now uses it to become more conservative about hoisting checks, if this has caused problems in the past. If we hoist something that may exit from a block that was not control equivalent to the pre-header then it's possible that the node's speculation will fail even though it wouldn't have if it wasn't hoisted. So, we flag these nodes' origins as being "wasHoisted" and we track all of their exits as "HoistingFailed". LICM will turn off such speculative hoisting if the CodeBlock from which we are hoisting had the HoistingFailed exit kind. Note that this deliberately still allows us to hoist things that may exit even if they are not control equivalent to the pre-header. This is necessary because the profitability of hoisting is so huge in all of the cases that we're aware of that it's worth giving it a shot. This is neutral on macrobenchmarks since none of the benchmarks we track have a hoistable operation that would exit only if hoisted. I added microbenchmarks to illustrate the problem and two of them speed up by ~40% while one of them is neutral (Int52 saves us from having problems on that program even though LICM previously did the wrong thing). * JavaScriptCore.xcodeproj/project.pbxproj: * bytecode/ExitKind.cpp: (JSC::exitKindToString): * bytecode/ExitKind.h: * dfg/DFGAtTailAbstractState.h: (JSC::DFG::AtTailAbstractState::operator bool): (JSC::DFG::AtTailAbstractState::initializeTo): * dfg/DFGBackwardsCFG.h: Added. (JSC::DFG::BackwardsCFG::BackwardsCFG): * dfg/DFGBackwardsDominators.h: Added. (JSC::DFG::BackwardsDominators::BackwardsDominators): * dfg/DFGCommon.h: (JSC::DFG::checkAndSet): Deleted. * dfg/DFGControlEquivalenceAnalysis.h: Added. (JSC::DFG::ControlEquivalenceAnalysis::ControlEquivalenceAnalysis): (JSC::DFG::ControlEquivalenceAnalysis::dominatesEquivalently): (JSC::DFG::ControlEquivalenceAnalysis::areEquivalent): * dfg/DFGGraph.cpp: (JSC::DFG::Graph::dump): (JSC::DFG::Graph::dumpBlockHeader): (JSC::DFG::Graph::invalidateCFG): (JSC::DFG::Graph::substituteGetLocal): (JSC::DFG::Graph::handleAssertionFailure): (JSC::DFG::Graph::ensureDominators): (JSC::DFG::Graph::ensurePrePostNumbering): (JSC::DFG::Graph::ensureNaturalLoops): (JSC::DFG::Graph::ensureBackwardsCFG): (JSC::DFG::Graph::ensureBackwardsDominators): (JSC::DFG::Graph::ensureControlEquivalenceAnalysis): (JSC::DFG::Graph::methodOfGettingAValueProfileFor): * dfg/DFGGraph.h: (JSC::DFG::Graph::hasDebuggerEnabled): * dfg/DFGInPlaceAbstractState.h: (JSC::DFG::InPlaceAbstractState::operator bool): (JSC::DFG::InPlaceAbstractState::createValueForNode): (JSC::DFG::InPlaceAbstractState::forNode): * dfg/DFGLICMPhase.cpp: (JSC::DFG::LICMPhase::run): (JSC::DFG::LICMPhase::attemptHoist): * dfg/DFGMayExit.cpp: (JSC::DFG::mayExit): * dfg/DFGMayExit.h: * dfg/DFGNode.h: * dfg/DFGNodeOrigin.cpp: (JSC::DFG::NodeOrigin::dump): * dfg/DFGNodeOrigin.h: (JSC::DFG::NodeOrigin::takeValidExit): (JSC::DFG::NodeOrigin::withWasHoisted): (JSC::DFG::NodeOrigin::forInsertingAfter): * dfg/DFGNullAbstractState.h: Added. (JSC::DFG::NullAbstractState::NullAbstractState): (JSC::DFG::NullAbstractState::operator bool): (JSC::DFG::NullAbstractState::forNode): * dfg/DFGOSRExit.cpp: (JSC::DFG::OSRExit::OSRExit): * dfg/DFGOSRExitBase.cpp: (JSC::DFG::OSRExitBase::considerAddingAsFrequentExitSiteSlow): * dfg/DFGOSRExitBase.h: (JSC::DFG::OSRExitBase::OSRExitBase): * dfg/DFGTypeCheckHoistingPhase.cpp: (JSC::DFG::TypeCheckHoistingPhase::run): * ftl/FTLOSRExit.cpp: (JSC::FTL::OSRExitDescriptor::prepareOSRExitHandle): (JSC::FTL::OSRExit::OSRExit): * ftl/FTLOSRExit.h: Source/WTF: This adds an adaptor for graphs called BackwardsGraph. The WTF graph framework is based on passing around a Graph template argument that follows the protocol shared by DFG::CFG, B3::CFG, and Air::CFG. These graphs always have a single root node but may have many leaf nodes. This new BackwardsGraph adaptor reverses the graph by creating a synthetic return node that it uses as the root in the inverted graph. This currently may resort to some heuristics in programs that have an infinite loop, but other than that, it'll work well in the general case. This allows us to say Dominators<BackwardsGraph<some graph type>> as a way of computing backwards dominators, which then allows us to easily answer control flow equivalence queries. * CMakeLists.txt: * WTF.xcodeproj/project.pbxproj: * wtf/BackwardsGraph.h: Added. (WTF::BackwardsGraph::Node::Node): (WTF::BackwardsGraph::Node::root): (WTF::BackwardsGraph::Node::operator==): (WTF::BackwardsGraph::Node::operator!=): (WTF::BackwardsGraph::Node::operator bool): (WTF::BackwardsGraph::Node::isRoot): (WTF::BackwardsGraph::Node::node): (WTF::BackwardsGraph::Set::Set): (WTF::BackwardsGraph::Set::add): (WTF::BackwardsGraph::Set::remove): (WTF::BackwardsGraph::Set::contains): (WTF::BackwardsGraph::Set::dump): (WTF::BackwardsGraph::Map::Map): (WTF::BackwardsGraph::Map::clear): (WTF::BackwardsGraph::Map::size): (WTF::BackwardsGraph::Map::operator[]): (WTF::BackwardsGraph::BackwardsGraph): (WTF::BackwardsGraph::root): (WTF::BackwardsGraph::newMap): (WTF::BackwardsGraph::successors): (WTF::BackwardsGraph::predecessors): (WTF::BackwardsGraph::index): (WTF::BackwardsGraph::node): (WTF::BackwardsGraph::numNodes): (WTF::BackwardsGraph::dump): * wtf/Dominators.h: (WTF::Dominators::Dominators): (WTF::Dominators::dump): (WTF::Dominators::LengauerTarjan::computeDepthFirstPreNumbering): * wtf/GraphNodeWorklist.h: (WTF::GraphNodeWith::GraphNodeWith): (WTF::GraphNodeWith::operator bool): * wtf/StdLibExtras.h: (WTF::callStatelessLambda): (WTF::checkAndSet): LayoutTests: Add tests for LICM hoisting things that would only exit if hoisted. * js/regress/licm-dragons-expected.txt: Added. * js/regress/licm-dragons-out-of-bounds-expected.txt: Added. * js/regress/licm-dragons-out-of-bounds.html: Added. * js/regress/licm-dragons-overflow-expected.txt: Added. * js/regress/licm-dragons-overflow.html: Added. * js/regress/licm-dragons.html: Added. * js/regress/script-tests/licm-dragons-out-of-bounds.js: Added. (foo): * js/regress/script-tests/licm-dragons-overflow.js: Added. (foo): * js/regress/script-tests/licm-dragons.js: Added. (foo): Canonical link: https://commits.webkit.org/176032@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@201182 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2016-05-19 21:25:29 +00:00
typedef Vector<Node, 4> List;
BackwardsGraph(Graph& graph)
: m_graph(graph)
{
GraphNodeWorklist<typename Graph::Node, typename Graph::Set> worklist;
auto addRootSuccessor = [&] (typename Graph::Node node) {
if (worklist.push(node)) {
m_rootSuccessorList.append(node);
m_rootSuccessorSet.add(node);
while (typename Graph::Node node = worklist.pop())
worklist.pushAll(graph.predecessors(node));
}
};
BackwardsGraph needs to consider back edges as the backward's root successor https://bugs.webkit.org/show_bug.cgi?id=195991 Reviewed by Filip Pizlo. JSTests: * stress/map-b3-licm-infinite-loop.js: Added. Source/JavaScriptCore: * b3/testb3.cpp: (JSC::B3::testInfiniteLoopDoesntCauseBadHoisting): (JSC::B3::run): Source/WTF: Previously, our backwards graph analysis was slightly wrong. The idea of backwards graph is that the root of the graph has edges to terminals in the original graph. And then the original directed edges in the graph are flipped. However, we weren't considering loops as a form of terminality. For example, we wouldn't consider an infinite loop as a terminal. So there were no edges from the root to a node in the infinite loop. This lead us to make mistakes when we used backwards dominators to compute control flow equivalence. This is better understood in an example: ``` preheader: while (1) { if (!isCell(v)) continue; load structure ID if (cond) continue; return } ``` In the previous version of this algorithm, the only edge from the backwards root would be to the block containing the return. This would lead us to believe that the loading of the structureID backwards dominates the preheader, leading us to believe it's control flow equivalent to preheader. This is obviously wrong, since we can loop forever if "v" isn't a cell. The solution here is to treat any backedge in the graph as a "terminal" node. Since a backedge implies the existence of a loop. In the above example, the backwards root now has an edge to both blocks with "continue". This prevents us from falsely claiming that the return is control flow equivalent with the preheader. This patch uses DFS spanning trees to compute back edges. An edge u->v is a back edge when u is a descendent of v in the DFS spanning tree of the Graph. * WTF.xcodeproj/project.pbxproj: * wtf/BackwardsGraph.h: (WTF::BackwardsGraph::BackwardsGraph): * wtf/SpanningTree.h: Added. (SpanningTree::SpanningTree): (SpanningTree::isDescendent): Canonical link: https://commits.webkit.org/210659@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@243639 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2019-03-29 04:42:42 +00:00
{
// Loops are a form of terminality (you can loop forever). To have a loop, you need to
// have a back edge. An edge u->v is a back edge when u is a descendent of v in the
// DFS spanning tree of the Graph.
SpanningTree<Graph> spanningTree(graph);
for (unsigned i = 0; i < graph.numNodes(); ++i) {
if (typename Graph::Node node = graph.node(i)) {
for (typename Graph::Node successor : graph.successors(node)) {
if (spanningTree.isDescendent(node, successor)) {
addRootSuccessor(node);
break;
}
}
}
}
}
DFG::LICMPhase shouldn't hoist type checks unless it knows that the check will succeed at the loop pre-header https://bugs.webkit.org/show_bug.cgi?id=144527 Reviewed by Saam Barati. Source/JavaScriptCore: This adds a control flow equivalence analysis (called ControlEquivalenceAnalysis) based on dominator analysis over the backwards CFG. Two basic blocks are control flow equivalent if the execution of one implies that the other one must also execute. It means that the two blocks' forward and backward dominance are reciprocated: (A dom B and B backdom A) or (B dom A and A backdom B). LICM now uses it to become more conservative about hoisting checks, if this has caused problems in the past. If we hoist something that may exit from a block that was not control equivalent to the pre-header then it's possible that the node's speculation will fail even though it wouldn't have if it wasn't hoisted. So, we flag these nodes' origins as being "wasHoisted" and we track all of their exits as "HoistingFailed". LICM will turn off such speculative hoisting if the CodeBlock from which we are hoisting had the HoistingFailed exit kind. Note that this deliberately still allows us to hoist things that may exit even if they are not control equivalent to the pre-header. This is necessary because the profitability of hoisting is so huge in all of the cases that we're aware of that it's worth giving it a shot. This is neutral on macrobenchmarks since none of the benchmarks we track have a hoistable operation that would exit only if hoisted. I added microbenchmarks to illustrate the problem and two of them speed up by ~40% while one of them is neutral (Int52 saves us from having problems on that program even though LICM previously did the wrong thing). * JavaScriptCore.xcodeproj/project.pbxproj: * bytecode/ExitKind.cpp: (JSC::exitKindToString): * bytecode/ExitKind.h: * dfg/DFGAtTailAbstractState.h: (JSC::DFG::AtTailAbstractState::operator bool): (JSC::DFG::AtTailAbstractState::initializeTo): * dfg/DFGBackwardsCFG.h: Added. (JSC::DFG::BackwardsCFG::BackwardsCFG): * dfg/DFGBackwardsDominators.h: Added. (JSC::DFG::BackwardsDominators::BackwardsDominators): * dfg/DFGCommon.h: (JSC::DFG::checkAndSet): Deleted. * dfg/DFGControlEquivalenceAnalysis.h: Added. (JSC::DFG::ControlEquivalenceAnalysis::ControlEquivalenceAnalysis): (JSC::DFG::ControlEquivalenceAnalysis::dominatesEquivalently): (JSC::DFG::ControlEquivalenceAnalysis::areEquivalent): * dfg/DFGGraph.cpp: (JSC::DFG::Graph::dump): (JSC::DFG::Graph::dumpBlockHeader): (JSC::DFG::Graph::invalidateCFG): (JSC::DFG::Graph::substituteGetLocal): (JSC::DFG::Graph::handleAssertionFailure): (JSC::DFG::Graph::ensureDominators): (JSC::DFG::Graph::ensurePrePostNumbering): (JSC::DFG::Graph::ensureNaturalLoops): (JSC::DFG::Graph::ensureBackwardsCFG): (JSC::DFG::Graph::ensureBackwardsDominators): (JSC::DFG::Graph::ensureControlEquivalenceAnalysis): (JSC::DFG::Graph::methodOfGettingAValueProfileFor): * dfg/DFGGraph.h: (JSC::DFG::Graph::hasDebuggerEnabled): * dfg/DFGInPlaceAbstractState.h: (JSC::DFG::InPlaceAbstractState::operator bool): (JSC::DFG::InPlaceAbstractState::createValueForNode): (JSC::DFG::InPlaceAbstractState::forNode): * dfg/DFGLICMPhase.cpp: (JSC::DFG::LICMPhase::run): (JSC::DFG::LICMPhase::attemptHoist): * dfg/DFGMayExit.cpp: (JSC::DFG::mayExit): * dfg/DFGMayExit.h: * dfg/DFGNode.h: * dfg/DFGNodeOrigin.cpp: (JSC::DFG::NodeOrigin::dump): * dfg/DFGNodeOrigin.h: (JSC::DFG::NodeOrigin::takeValidExit): (JSC::DFG::NodeOrigin::withWasHoisted): (JSC::DFG::NodeOrigin::forInsertingAfter): * dfg/DFGNullAbstractState.h: Added. (JSC::DFG::NullAbstractState::NullAbstractState): (JSC::DFG::NullAbstractState::operator bool): (JSC::DFG::NullAbstractState::forNode): * dfg/DFGOSRExit.cpp: (JSC::DFG::OSRExit::OSRExit): * dfg/DFGOSRExitBase.cpp: (JSC::DFG::OSRExitBase::considerAddingAsFrequentExitSiteSlow): * dfg/DFGOSRExitBase.h: (JSC::DFG::OSRExitBase::OSRExitBase): * dfg/DFGTypeCheckHoistingPhase.cpp: (JSC::DFG::TypeCheckHoistingPhase::run): * ftl/FTLOSRExit.cpp: (JSC::FTL::OSRExitDescriptor::prepareOSRExitHandle): (JSC::FTL::OSRExit::OSRExit): * ftl/FTLOSRExit.h: Source/WTF: This adds an adaptor for graphs called BackwardsGraph. The WTF graph framework is based on passing around a Graph template argument that follows the protocol shared by DFG::CFG, B3::CFG, and Air::CFG. These graphs always have a single root node but may have many leaf nodes. This new BackwardsGraph adaptor reverses the graph by creating a synthetic return node that it uses as the root in the inverted graph. This currently may resort to some heuristics in programs that have an infinite loop, but other than that, it'll work well in the general case. This allows us to say Dominators<BackwardsGraph<some graph type>> as a way of computing backwards dominators, which then allows us to easily answer control flow equivalence queries. * CMakeLists.txt: * WTF.xcodeproj/project.pbxproj: * wtf/BackwardsGraph.h: Added. (WTF::BackwardsGraph::Node::Node): (WTF::BackwardsGraph::Node::root): (WTF::BackwardsGraph::Node::operator==): (WTF::BackwardsGraph::Node::operator!=): (WTF::BackwardsGraph::Node::operator bool): (WTF::BackwardsGraph::Node::isRoot): (WTF::BackwardsGraph::Node::node): (WTF::BackwardsGraph::Set::Set): (WTF::BackwardsGraph::Set::add): (WTF::BackwardsGraph::Set::remove): (WTF::BackwardsGraph::Set::contains): (WTF::BackwardsGraph::Set::dump): (WTF::BackwardsGraph::Map::Map): (WTF::BackwardsGraph::Map::clear): (WTF::BackwardsGraph::Map::size): (WTF::BackwardsGraph::Map::operator[]): (WTF::BackwardsGraph::BackwardsGraph): (WTF::BackwardsGraph::root): (WTF::BackwardsGraph::newMap): (WTF::BackwardsGraph::successors): (WTF::BackwardsGraph::predecessors): (WTF::BackwardsGraph::index): (WTF::BackwardsGraph::node): (WTF::BackwardsGraph::numNodes): (WTF::BackwardsGraph::dump): * wtf/Dominators.h: (WTF::Dominators::Dominators): (WTF::Dominators::dump): (WTF::Dominators::LengauerTarjan::computeDepthFirstPreNumbering): * wtf/GraphNodeWorklist.h: (WTF::GraphNodeWith::GraphNodeWith): (WTF::GraphNodeWith::operator bool): * wtf/StdLibExtras.h: (WTF::callStatelessLambda): (WTF::checkAndSet): LayoutTests: Add tests for LICM hoisting things that would only exit if hoisted. * js/regress/licm-dragons-expected.txt: Added. * js/regress/licm-dragons-out-of-bounds-expected.txt: Added. * js/regress/licm-dragons-out-of-bounds.html: Added. * js/regress/licm-dragons-overflow-expected.txt: Added. * js/regress/licm-dragons-overflow.html: Added. * js/regress/licm-dragons.html: Added. * js/regress/script-tests/licm-dragons-out-of-bounds.js: Added. (foo): * js/regress/script-tests/licm-dragons-overflow.js: Added. (foo): * js/regress/script-tests/licm-dragons.js: Added. (foo): Canonical link: https://commits.webkit.org/176032@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@201182 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2016-05-19 21:25:29 +00:00
for (unsigned i = 0; i < graph.numNodes(); ++i) {
if (typename Graph::Node node = graph.node(i)) {
if (!graph.successors(node).size())
addRootSuccessor(node);
}
}
// At this point there will be some nodes in the graph that aren't known to the worklist. We
// could add any or all of them to the root successors list. Adding all of them would be a bad
// pessimisation. Ideally we would pick the ones that have backward edges but no forward
// edges. That would require thinking, so we just use a rough heuristic: add the highest
// numbered nodes first, which is totally fine if the input program is already sorted nicely.
for (unsigned i = graph.numNodes(); i--;) {
if (typename Graph::Node node = graph.node(i))
addRootSuccessor(node);
}
}
Node root() { return Node::root(); }
template<typename T>
Map<T> newMap() { return Map<T>(m_graph); }
List successors(const Node& node) const
{
if (node.isRoot())
return m_rootSuccessorList;
List result;
for (typename Graph::Node predecessor : m_graph.predecessors(node.node()))
result.append(predecessor);
return result;
}
List predecessors(const Node& node) const
{
if (node.isRoot())
return { };
List result;
if (m_rootSuccessorSet.contains(node.node()))
result.append(Node::root());
for (typename Graph::Node successor : m_graph.successors(node.node()))
result.append(successor);
return result;
}
unsigned index(const Node& node) const
{
if (node.isRoot())
return 0;
return m_graph.index(node.node()) + 1;
}
Node node(unsigned index) const
{
if (!index)
return Node::root();
return m_graph.node(index - 1);
}
unsigned numNodes() const
{
return m_graph.numNodes() + 1;
}
CString dump(Node node) const
{
StringPrintStream out;
if (!node)
out.print("<null>");
else if (node.isRoot())
Support compiling catch in the DFG https://bugs.webkit.org/show_bug.cgi?id=174590 <rdar://problem/34047845> Reviewed by Filip Pizlo. JSTests: * microbenchmarks/delta-blue-try-catch.js: Added. (exception): (value): (OrderedCollection): (OrderedCollection.prototype.add): (OrderedCollection.prototype.at): (OrderedCollection.prototype.size): (OrderedCollection.prototype.removeFirst): (OrderedCollection.prototype.remove): (Strength): (Strength.stronger): (Strength.weaker): (Strength.weakestOf): (Strength.strongest): (Strength.prototype.nextWeaker): (Constraint): (Constraint.prototype.addConstraint): (Constraint.prototype.satisfy): (Constraint.prototype.destroyConstraint): (Constraint.prototype.isInput): (UnaryConstraint): (UnaryConstraint.prototype.addToGraph): (UnaryConstraint.prototype.chooseMethod): (UnaryConstraint.prototype.isSatisfied): (UnaryConstraint.prototype.markInputs): (UnaryConstraint.prototype.output): (UnaryConstraint.prototype.recalculate): (UnaryConstraint.prototype.markUnsatisfied): (UnaryConstraint.prototype.inputsKnown): (UnaryConstraint.prototype.removeFromGraph): (StayConstraint): (StayConstraint.prototype.execute): (EditConstraint.prototype.isInput): (EditConstraint.prototype.execute): (BinaryConstraint): (BinaryConstraint.prototype.chooseMethod): (BinaryConstraint.prototype.addToGraph): (BinaryConstraint.prototype.isSatisfied): (BinaryConstraint.prototype.markInputs): (BinaryConstraint.prototype.input): (BinaryConstraint.prototype.output): (BinaryConstraint.prototype.recalculate): (BinaryConstraint.prototype.markUnsatisfied): (BinaryConstraint.prototype.inputsKnown): (BinaryConstraint.prototype.removeFromGraph): (ScaleConstraint): (ScaleConstraint.prototype.addToGraph): (ScaleConstraint.prototype.removeFromGraph): (ScaleConstraint.prototype.markInputs): (ScaleConstraint.prototype.execute): (ScaleConstraint.prototype.recalculate): (EqualityConstraint): (EqualityConstraint.prototype.execute): (Variable): (Variable.prototype.addConstraint): (Variable.prototype.removeConstraint): (Planner): (Planner.prototype.incrementalAdd): (Planner.prototype.incrementalRemove): (Planner.prototype.newMark): (Planner.prototype.makePlan): (Planner.prototype.extractPlanFromConstraints): (Planner.prototype.addPropagate): (Planner.prototype.removePropagateFrom): (Planner.prototype.addConstraintsConsumingTo): (Plan): (Plan.prototype.addConstraint): (Plan.prototype.size): (Plan.prototype.constraintAt): (Plan.prototype.execute): (chainTest): (projectionTest): (change): (deltaBlue): * microbenchmarks/fake-iterators-that-throw-when-finished.js: Added. (assert): (Numbers): (Numbers.prototype.next): (return.Transpose): (return.Transpose.prototype.next): (transpose): (verifyEven): (verifyString): (foo): (runIterators): * microbenchmarks/try-catch-word-count.js: Added. (let.assert): (EOF): (let.texts): (let.o.apply): (foo): (bar): (f): (run): (test1): (test2): (test3): (fn): (A): (B): (A.prototype.getValue): (B.prototype.getParentValue): (strlen): (sum.0): (test): (result.test.o): (set add.set add): (set forEach): (stringHash): (set if): (testFunction): (set delete.set has.set add): * stress/catch-set-argument-speculation-failure.js: Added. (o): (e): (e2): (escape): (baz): (noInline.run): (noInline): * stress/osr-enter-to-catch-with-set-local-type-check-failure.js: Added. (foo): (e): (baz): (bar): Source/JavaScriptCore: This patch implements OSR entry into op_catch in the DFG. We will support OSR entry into the FTL in a followup: https://bugs.webkit.org/show_bug.cgi?id=175396 To implement catch in the DFG, this patch introduces the concept of multiple entrypoints into CPS/LoadStore DFG IR. A lot of this patch is stringing this concept through the DFG. Many phases used to assume that Graph::block(0) is the only root, and this patch contains many straight forward changes generalizing the code to handle more than one entrypoint. A main building block of this is moving to two CFG types: SSACFG and CPSCFG. SSACFG is the same CFG we used to have. CPSCFG is a new type that introduces a fake root that has an outgoing edge to all the entrypoints. This allows our existing graph algorithms to Just Work over CPSCFG. For example, there is now the concept of SSADominators vs CPSDominators, and SSANaturalLoops vs CPSNaturalLoops. The way we compile the catch entrypoint is by bootstrapping the state of the program by loading all live bytecode locals from a buffer. The OSR entry code will store all live values into that buffer before jumping to the entrypoint. The OSR entry code is also responsible for performing type proofs of the arguments before doing an OSR entry. If there is a type mismatch, it's not legal to OSR enter into the DFG compilation. Currently, each catch entrypoint knows the argument type proofs it must perform to enter into the DFG. Currently, all entrypoints' arguments flush format are unified via ArgumentPosition, but this is just an implementation detail. The code is written more generally to assume that each entrypoint may perform its own distinct proof. op_catch now performs value profiling for all live bytecode locals in the LLInt and baseline JIT. This information is then fed into the DFG via the ExtractCatchLocal node in the prediction propagation phase. This patch also changes how we generate op_catch in bytecode. All op_catches are now split out at the end of the program in bytecode. This ensures that no op_catch is inside a try block. This is needed to ensure correctness in the DFGLiveCatchVariablePreservationPhase. That phase only inserts flushes before SetLocals inside a try block. If an op_catch were in a try block, this would cause the phase to insert a Flush before one of the state bootstrapping SetLocals, which would generate invalid IR. Moving op_catch to be generated on its own at the end of a bytecode stream seemed like the most elegant solution since it better represents that we treat op_catch as an entrypoint. This is true both in the DFG and in the baseline and LLInt: we don't reach an op_catch via normal control flow. Because op_catch cannot throw, this will not break any previous semantics of op_catch. Logically, it'd be valid to split try blocks around any non-throwing bytecode operation. * CMakeLists.txt: * JavaScriptCore.xcodeproj/project.pbxproj: * bytecode/BytecodeDumper.cpp: (JSC::BytecodeDumper<Block>::dumpBytecode): * bytecode/BytecodeList.json: * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::finishCreation): (JSC::CodeBlock::ensureCatchLivenessIsComputedForBytecodeOffset): (JSC::CodeBlock::updateAllPredictionsAndCountLiveness): (JSC::CodeBlock::validate): * bytecode/CodeBlock.h: * bytecode/ValueProfile.h: (JSC::ValueProfile::ValueProfile): (JSC::ValueProfileAndOperandBuffer::ValueProfileAndOperandBuffer): (JSC::ValueProfileAndOperandBuffer::~ValueProfileAndOperandBuffer): (JSC::ValueProfileAndOperandBuffer::forEach): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::emitCatch): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: * bytecompiler/NodesCodegen.cpp: (JSC::TryNode::emitBytecode): * dfg/DFGAbstractInterpreterInlines.h: (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects): * dfg/DFGBackwardsCFG.h: (JSC::DFG::BackwardsCFG::BackwardsCFG): * dfg/DFGBasicBlock.cpp: (JSC::DFG::BasicBlock::BasicBlock): * dfg/DFGBasicBlock.h: (JSC::DFG::BasicBlock::findTerminal const): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::setDirect): (JSC::DFG::ByteCodeParser::flush): (JSC::DFG::ByteCodeParser::DelayedSetLocal::DelayedSetLocal): (JSC::DFG::ByteCodeParser::DelayedSetLocal::execute): (JSC::DFG::ByteCodeParser::parseBlock): (JSC::DFG::ByteCodeParser::parseCodeBlock): (JSC::DFG::ByteCodeParser::parse): * dfg/DFGCFG.h: (JSC::DFG::CFG::root): (JSC::DFG::CFG::roots): (JSC::DFG::CPSCFG::CPSCFG): (JSC::DFG::selectCFG): * dfg/DFGCPSRethreadingPhase.cpp: (JSC::DFG::CPSRethreadingPhase::specialCaseArguments): * dfg/DFGCSEPhase.cpp: * dfg/DFGClobberize.h: (JSC::DFG::clobberize): * dfg/DFGControlEquivalenceAnalysis.h: (JSC::DFG::ControlEquivalenceAnalysis::ControlEquivalenceAnalysis): * dfg/DFGDCEPhase.cpp: (JSC::DFG::DCEPhase::run): * dfg/DFGDisassembler.cpp: (JSC::DFG::Disassembler::createDumpList): * dfg/DFGDoesGC.cpp: (JSC::DFG::doesGC): * dfg/DFGDominators.h: (JSC::DFG::Dominators::Dominators): (JSC::DFG::ensureDominatorsForCFG): * dfg/DFGEdgeDominates.h: (JSC::DFG::EdgeDominates::EdgeDominates): (JSC::DFG::EdgeDominates::operator()): * dfg/DFGFixupPhase.cpp: (JSC::DFG::FixupPhase::fixupNode): (JSC::DFG::FixupPhase::fixupChecksInBlock): * dfg/DFGFlushFormat.h: * dfg/DFGGraph.cpp: (JSC::DFG::Graph::Graph): (JSC::DFG::unboxLoopNode): (JSC::DFG::Graph::dumpBlockHeader): (JSC::DFG::Graph::dump): (JSC::DFG::Graph::determineReachability): (JSC::DFG::Graph::invalidateCFG): (JSC::DFG::Graph::blocksInPreOrder): (JSC::DFG::Graph::blocksInPostOrder): (JSC::DFG::Graph::ensureCPSDominators): (JSC::DFG::Graph::ensureSSADominators): (JSC::DFG::Graph::ensureCPSNaturalLoops): (JSC::DFG::Graph::ensureSSANaturalLoops): (JSC::DFG::Graph::ensureBackwardsCFG): (JSC::DFG::Graph::ensureBackwardsDominators): (JSC::DFG::Graph::ensureControlEquivalenceAnalysis): (JSC::DFG::Graph::methodOfGettingAValueProfileFor): (JSC::DFG::Graph::clearCPSCFGData): (JSC::DFG::Graph::ensureDominators): Deleted. (JSC::DFG::Graph::ensurePrePostNumbering): Deleted. (JSC::DFG::Graph::ensureNaturalLoops): Deleted. * dfg/DFGGraph.h: (JSC::DFG::Graph::willCatchExceptionInMachineFrame): (JSC::DFG::Graph::isEntrypoint const): * dfg/DFGInPlaceAbstractState.cpp: (JSC::DFG::InPlaceAbstractState::initialize): (JSC::DFG::InPlaceAbstractState::mergeToSuccessors): * dfg/DFGJITCode.cpp: (JSC::DFG::JITCode::shrinkToFit): * dfg/DFGJITCode.h: (JSC::DFG::JITCode::catchOSREntryDataForBytecodeIndex): (JSC::DFG::JITCode::finalizeCatchOSREntrypoints): (JSC::DFG::JITCode::appendCatchEntrypoint): * dfg/DFGJITCompiler.cpp: (JSC::DFG::JITCompiler::compile): (JSC::DFG::JITCompiler::compileFunction): (JSC::DFG::JITCompiler::noticeCatchEntrypoint): (JSC::DFG::JITCompiler::noticeOSREntry): (JSC::DFG::JITCompiler::makeCatchOSREntryBuffer): * dfg/DFGJITCompiler.h: * dfg/DFGLICMPhase.cpp: (JSC::DFG::LICMPhase::run): (JSC::DFG::LICMPhase::attemptHoist): * dfg/DFGLiveCatchVariablePreservationPhase.cpp: (JSC::DFG::LiveCatchVariablePreservationPhase::run): (JSC::DFG::LiveCatchVariablePreservationPhase::isValidFlushLocation): (JSC::DFG::LiveCatchVariablePreservationPhase::handleBlockForTryCatch): (JSC::DFG::LiveCatchVariablePreservationPhase::newVariableAccessData): (JSC::DFG::LiveCatchVariablePreservationPhase::willCatchException): Deleted. (JSC::DFG::LiveCatchVariablePreservationPhase::handleBlock): Deleted. * dfg/DFGLoopPreHeaderCreationPhase.cpp: (JSC::DFG::createPreHeader): (JSC::DFG::LoopPreHeaderCreationPhase::run): * dfg/DFGMaximalFlushInsertionPhase.cpp: (JSC::DFG::MaximalFlushInsertionPhase::run): (JSC::DFG::MaximalFlushInsertionPhase::treatRegularBlock): (JSC::DFG::MaximalFlushInsertionPhase::treatRootBlock): * dfg/DFGMayExit.cpp: * dfg/DFGNaturalLoops.h: (JSC::DFG::NaturalLoops::NaturalLoops): * dfg/DFGNode.h: (JSC::DFG::Node::isSwitch const): (JSC::DFG::Node::successor): (JSC::DFG::Node::catchOSREntryIndex const): (JSC::DFG::Node::catchLocalPrediction): (JSC::DFG::Node::isSwitch): Deleted. * dfg/DFGNodeType.h: * dfg/DFGOSREntry.cpp: (JSC::DFG::prepareCatchOSREntry): * dfg/DFGOSREntry.h: * dfg/DFGOSREntrypointCreationPhase.cpp: (JSC::DFG::OSREntrypointCreationPhase::run): * dfg/DFGOSRExitCompilerCommon.cpp: (JSC::DFG::handleExitCounts): * dfg/DFGObjectAllocationSinkingPhase.cpp: * dfg/DFGPlan.cpp: (JSC::DFG::Plan::compileInThreadImpl): * dfg/DFGPrePostNumbering.cpp: (JSC::DFG::PrePostNumbering::PrePostNumbering): Deleted. (JSC::DFG::PrePostNumbering::~PrePostNumbering): Deleted. (WTF::printInternal): Deleted. * dfg/DFGPrePostNumbering.h: (): Deleted. (JSC::DFG::PrePostNumbering::preNumber const): Deleted. (JSC::DFG::PrePostNumbering::postNumber const): Deleted. (JSC::DFG::PrePostNumbering::isStrictAncestorOf const): Deleted. (JSC::DFG::PrePostNumbering::isAncestorOf const): Deleted. (JSC::DFG::PrePostNumbering::isStrictDescendantOf const): Deleted. (JSC::DFG::PrePostNumbering::isDescendantOf const): Deleted. (JSC::DFG::PrePostNumbering::edgeKind const): Deleted. * dfg/DFGPredictionInjectionPhase.cpp: (JSC::DFG::PredictionInjectionPhase::run): * dfg/DFGPredictionPropagationPhase.cpp: * dfg/DFGPutStackSinkingPhase.cpp: * dfg/DFGSSACalculator.cpp: (JSC::DFG::SSACalculator::nonLocalReachingDef): (JSC::DFG::SSACalculator::reachingDefAtTail): * dfg/DFGSSACalculator.h: (JSC::DFG::SSACalculator::computePhis): * dfg/DFGSSAConversionPhase.cpp: (JSC::DFG::SSAConversionPhase::run): (JSC::DFG::performSSAConversion): * dfg/DFGSafeToExecute.h: (JSC::DFG::safeToExecute): * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCurrentBlock): (JSC::DFG::SpeculativeJIT::checkArgumentTypes): (JSC::DFG::SpeculativeJIT::createOSREntries): (JSC::DFG::SpeculativeJIT::linkOSREntries): * dfg/DFGSpeculativeJIT32_64.cpp: (JSC::DFG::SpeculativeJIT::compile): * dfg/DFGSpeculativeJIT64.cpp: (JSC::DFG::SpeculativeJIT::compile): * dfg/DFGStaticExecutionCountEstimationPhase.cpp: (JSC::DFG::StaticExecutionCountEstimationPhase::run): * dfg/DFGStrengthReductionPhase.cpp: (JSC::DFG::StrengthReductionPhase::handleNode): * dfg/DFGTierUpCheckInjectionPhase.cpp: (JSC::DFG::TierUpCheckInjectionPhase::run): (JSC::DFG::TierUpCheckInjectionPhase::buildNaturalLoopToLoopHintMap): * dfg/DFGTypeCheckHoistingPhase.cpp: (JSC::DFG::TypeCheckHoistingPhase::run): * dfg/DFGValidate.cpp: * ftl/FTLLink.cpp: (JSC::FTL::link): * ftl/FTLLowerDFGToB3.cpp: (JSC::FTL::DFG::LowerDFGToB3::lower): (JSC::FTL::DFG::LowerDFGToB3::safelyInvalidateAfterTermination): (JSC::FTL::DFG::LowerDFGToB3::isValid): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_catch): * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_catch): * jit/JITOperations.cpp: * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: Source/WTF: This patch generalizes the BackwardsGraph fake root into a more generalizable class called SingleRootGraph. SingleRootGraph exposes the general graph interface used in Dominators and NaturalLoops. SingleRootGraph takes as input a graph with the normal graph interface, but also allows the input graph to contain more than one root. SingleRootGraph then exposes a single root, which it creates, that has an outgoing edge to all the roots in the original graph. * WTF.xcodeproj/project.pbxproj: * wtf/BackwardsGraph.h: (WTF::BackwardsGraph::dump const): (WTF::BackwardsGraph::rootName): Deleted. (WTF::BackwardsGraph::Node::Node): Deleted. (WTF::BackwardsGraph::Node::root): Deleted. (WTF::BackwardsGraph::Node::operator== const): Deleted. (WTF::BackwardsGraph::Node::operator!= const): Deleted. (WTF::BackwardsGraph::Node::operator bool const): Deleted. (WTF::BackwardsGraph::Node::isRoot const): Deleted. (WTF::BackwardsGraph::Node::node const): Deleted. (): Deleted. (WTF::BackwardsGraph::Set::Set): Deleted. (WTF::BackwardsGraph::Set::add): Deleted. (WTF::BackwardsGraph::Set::remove): Deleted. (WTF::BackwardsGraph::Set::contains): Deleted. (WTF::BackwardsGraph::Set::dump const): Deleted. (WTF::BackwardsGraph::Map::Map): Deleted. (WTF::BackwardsGraph::Map::clear): Deleted. (WTF::BackwardsGraph::Map::size const): Deleted. (WTF::BackwardsGraph::Map::operator[]): Deleted. (WTF::BackwardsGraph::Map::operator[] const): Deleted. * wtf/Dominators.h: (WTF::Dominators::Dominators): (WTF::Dominators::forAllBlocksInIteratedDominanceFrontierOf): (WTF::Dominators::forAllBlocksInPrunedIteratedDominanceFrontierOf): (WTF::Dominators::iteratedDominanceFrontierOf const): (WTF::Dominators::forAllBlocksInIteratedDominanceFrontierOfImpl const): * wtf/SingleRootGraph.h: Added. (WTF::SingleRootGraphNode::rootName): (WTF::SingleRootGraphNode::SingleRootGraphNode): (WTF::SingleRootGraphNode::root): (WTF::SingleRootGraphNode::operator== const): (WTF::SingleRootGraphNode::operator!= const): (WTF::SingleRootGraphNode::operator bool const): (WTF::SingleRootGraphNode::isRoot const): (WTF::SingleRootGraphNode::node const): (WTF::SingleRootGraphSet::add): (WTF::SingleRootGraphSet::remove): (WTF::SingleRootGraphSet::contains): (WTF::SingleRootGraphSet::dump const): (WTF::SingleRootMap::SingleRootMap): (WTF::SingleRootMap::clear): (WTF::SingleRootMap::size const): (WTF::SingleRootMap::operator[]): (WTF::SingleRootMap::operator[] const): (WTF::SingleRootGraph::SingleRootGraph): (WTF::SingleRootGraph::root const): (WTF::SingleRootGraph::newMap): (WTF::SingleRootGraph::successors const): (WTF::SingleRootGraph::predecessors const): (WTF::SingleRootGraph::index const): (WTF::SingleRootGraph::node const): (WTF::SingleRootGraph::numNodes const): (WTF::SingleRootGraph::dump const): (WTF::SingleRootGraph::assertIsConsistent const): Canonical link: https://commits.webkit.org/192644@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@221196 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2017-08-25 18:26:15 +00:00
out.print(Node::rootName());
DFG::LICMPhase shouldn't hoist type checks unless it knows that the check will succeed at the loop pre-header https://bugs.webkit.org/show_bug.cgi?id=144527 Reviewed by Saam Barati. Source/JavaScriptCore: This adds a control flow equivalence analysis (called ControlEquivalenceAnalysis) based on dominator analysis over the backwards CFG. Two basic blocks are control flow equivalent if the execution of one implies that the other one must also execute. It means that the two blocks' forward and backward dominance are reciprocated: (A dom B and B backdom A) or (B dom A and A backdom B). LICM now uses it to become more conservative about hoisting checks, if this has caused problems in the past. If we hoist something that may exit from a block that was not control equivalent to the pre-header then it's possible that the node's speculation will fail even though it wouldn't have if it wasn't hoisted. So, we flag these nodes' origins as being "wasHoisted" and we track all of their exits as "HoistingFailed". LICM will turn off such speculative hoisting if the CodeBlock from which we are hoisting had the HoistingFailed exit kind. Note that this deliberately still allows us to hoist things that may exit even if they are not control equivalent to the pre-header. This is necessary because the profitability of hoisting is so huge in all of the cases that we're aware of that it's worth giving it a shot. This is neutral on macrobenchmarks since none of the benchmarks we track have a hoistable operation that would exit only if hoisted. I added microbenchmarks to illustrate the problem and two of them speed up by ~40% while one of them is neutral (Int52 saves us from having problems on that program even though LICM previously did the wrong thing). * JavaScriptCore.xcodeproj/project.pbxproj: * bytecode/ExitKind.cpp: (JSC::exitKindToString): * bytecode/ExitKind.h: * dfg/DFGAtTailAbstractState.h: (JSC::DFG::AtTailAbstractState::operator bool): (JSC::DFG::AtTailAbstractState::initializeTo): * dfg/DFGBackwardsCFG.h: Added. (JSC::DFG::BackwardsCFG::BackwardsCFG): * dfg/DFGBackwardsDominators.h: Added. (JSC::DFG::BackwardsDominators::BackwardsDominators): * dfg/DFGCommon.h: (JSC::DFG::checkAndSet): Deleted. * dfg/DFGControlEquivalenceAnalysis.h: Added. (JSC::DFG::ControlEquivalenceAnalysis::ControlEquivalenceAnalysis): (JSC::DFG::ControlEquivalenceAnalysis::dominatesEquivalently): (JSC::DFG::ControlEquivalenceAnalysis::areEquivalent): * dfg/DFGGraph.cpp: (JSC::DFG::Graph::dump): (JSC::DFG::Graph::dumpBlockHeader): (JSC::DFG::Graph::invalidateCFG): (JSC::DFG::Graph::substituteGetLocal): (JSC::DFG::Graph::handleAssertionFailure): (JSC::DFG::Graph::ensureDominators): (JSC::DFG::Graph::ensurePrePostNumbering): (JSC::DFG::Graph::ensureNaturalLoops): (JSC::DFG::Graph::ensureBackwardsCFG): (JSC::DFG::Graph::ensureBackwardsDominators): (JSC::DFG::Graph::ensureControlEquivalenceAnalysis): (JSC::DFG::Graph::methodOfGettingAValueProfileFor): * dfg/DFGGraph.h: (JSC::DFG::Graph::hasDebuggerEnabled): * dfg/DFGInPlaceAbstractState.h: (JSC::DFG::InPlaceAbstractState::operator bool): (JSC::DFG::InPlaceAbstractState::createValueForNode): (JSC::DFG::InPlaceAbstractState::forNode): * dfg/DFGLICMPhase.cpp: (JSC::DFG::LICMPhase::run): (JSC::DFG::LICMPhase::attemptHoist): * dfg/DFGMayExit.cpp: (JSC::DFG::mayExit): * dfg/DFGMayExit.h: * dfg/DFGNode.h: * dfg/DFGNodeOrigin.cpp: (JSC::DFG::NodeOrigin::dump): * dfg/DFGNodeOrigin.h: (JSC::DFG::NodeOrigin::takeValidExit): (JSC::DFG::NodeOrigin::withWasHoisted): (JSC::DFG::NodeOrigin::forInsertingAfter): * dfg/DFGNullAbstractState.h: Added. (JSC::DFG::NullAbstractState::NullAbstractState): (JSC::DFG::NullAbstractState::operator bool): (JSC::DFG::NullAbstractState::forNode): * dfg/DFGOSRExit.cpp: (JSC::DFG::OSRExit::OSRExit): * dfg/DFGOSRExitBase.cpp: (JSC::DFG::OSRExitBase::considerAddingAsFrequentExitSiteSlow): * dfg/DFGOSRExitBase.h: (JSC::DFG::OSRExitBase::OSRExitBase): * dfg/DFGTypeCheckHoistingPhase.cpp: (JSC::DFG::TypeCheckHoistingPhase::run): * ftl/FTLOSRExit.cpp: (JSC::FTL::OSRExitDescriptor::prepareOSRExitHandle): (JSC::FTL::OSRExit::OSRExit): * ftl/FTLOSRExit.h: Source/WTF: This adds an adaptor for graphs called BackwardsGraph. The WTF graph framework is based on passing around a Graph template argument that follows the protocol shared by DFG::CFG, B3::CFG, and Air::CFG. These graphs always have a single root node but may have many leaf nodes. This new BackwardsGraph adaptor reverses the graph by creating a synthetic return node that it uses as the root in the inverted graph. This currently may resort to some heuristics in programs that have an infinite loop, but other than that, it'll work well in the general case. This allows us to say Dominators<BackwardsGraph<some graph type>> as a way of computing backwards dominators, which then allows us to easily answer control flow equivalence queries. * CMakeLists.txt: * WTF.xcodeproj/project.pbxproj: * wtf/BackwardsGraph.h: Added. (WTF::BackwardsGraph::Node::Node): (WTF::BackwardsGraph::Node::root): (WTF::BackwardsGraph::Node::operator==): (WTF::BackwardsGraph::Node::operator!=): (WTF::BackwardsGraph::Node::operator bool): (WTF::BackwardsGraph::Node::isRoot): (WTF::BackwardsGraph::Node::node): (WTF::BackwardsGraph::Set::Set): (WTF::BackwardsGraph::Set::add): (WTF::BackwardsGraph::Set::remove): (WTF::BackwardsGraph::Set::contains): (WTF::BackwardsGraph::Set::dump): (WTF::BackwardsGraph::Map::Map): (WTF::BackwardsGraph::Map::clear): (WTF::BackwardsGraph::Map::size): (WTF::BackwardsGraph::Map::operator[]): (WTF::BackwardsGraph::BackwardsGraph): (WTF::BackwardsGraph::root): (WTF::BackwardsGraph::newMap): (WTF::BackwardsGraph::successors): (WTF::BackwardsGraph::predecessors): (WTF::BackwardsGraph::index): (WTF::BackwardsGraph::node): (WTF::BackwardsGraph::numNodes): (WTF::BackwardsGraph::dump): * wtf/Dominators.h: (WTF::Dominators::Dominators): (WTF::Dominators::dump): (WTF::Dominators::LengauerTarjan::computeDepthFirstPreNumbering): * wtf/GraphNodeWorklist.h: (WTF::GraphNodeWith::GraphNodeWith): (WTF::GraphNodeWith::operator bool): * wtf/StdLibExtras.h: (WTF::callStatelessLambda): (WTF::checkAndSet): LayoutTests: Add tests for LICM hoisting things that would only exit if hoisted. * js/regress/licm-dragons-expected.txt: Added. * js/regress/licm-dragons-out-of-bounds-expected.txt: Added. * js/regress/licm-dragons-out-of-bounds.html: Added. * js/regress/licm-dragons-overflow-expected.txt: Added. * js/regress/licm-dragons-overflow.html: Added. * js/regress/licm-dragons.html: Added. * js/regress/script-tests/licm-dragons-out-of-bounds.js: Added. (foo): * js/regress/script-tests/licm-dragons-overflow.js: Added. (foo): * js/regress/script-tests/licm-dragons.js: Added. (foo): Canonical link: https://commits.webkit.org/176032@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@201182 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2016-05-19 21:25:29 +00:00
else
out.print(m_graph.dump(node.node()));
return out.toCString();
}
void dump(PrintStream& out) const
{
for (unsigned i = 0; i < numNodes(); ++i) {
Node node = this->node(i);
if (!node)
continue;
out.print(dump(node), ":\n");
out.print(" Preds: ");
CommaPrinter comma;
for (Node predecessor : predecessors(node))
out.print(comma, dump(predecessor));
out.print("\n");
out.print(" Succs: ");
comma = CommaPrinter();
for (Node successor : successors(node))
out.print(comma, dump(successor));
out.print("\n");
}
}
private:
Graph& m_graph;
List m_rootSuccessorList;
typename Graph::Set m_rootSuccessorSet;
};
} // namespace WTF
using WTF::BackwardsGraph;