WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
Patch
bug-143073-20150615122526.patch (text/plain), 86.24 KB, created by
Basile Clement
on 2015-06-15 12:26:00 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Basile Clement
Created:
2015-06-15 12:26:00 PDT
Size:
86.24 KB
patch
obsolete
>Subversion Revision: 185552 >diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index e43e8449aa9385b2873c6899a6ac115d4de8f5eb..7eeb61c59841d7c589687ec52672941bdbe53bc8 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,3 +1,88 @@ >+2015-06-15 Basile Clement <basile_clement@apple.com> >+ >+ Object cycles should not prevent allocation elimination/sinking >+ https://bugs.webkit.org/show_bug.cgi?id=143073 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * CMakeLists.txt: >+ * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: >+ * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: >+ * JavaScriptCore.xcodeproj/project.pbxproj: >+ * dfg/DFGLazyNode.cpp: >+ (JSC::DFG::LazyNode::dump): >+ * dfg/DFGObjectAllocationEliminationPhase.cpp: Added. >+ (JSC::DFG::AbstractPointer::AbstractPointer): >+ (JSC::DFG::AbstractPointer::merge): >+ (JSC::DFG::AbstractPointer::operator==): >+ (JSC::DFG::AbstractPointer::operator!=): >+ (JSC::DFG::AbstractPointer::forAllAliases): >+ (JSC::DFG::AbstractPointer::dump): >+ (JSC::DFG::AbstractPointer::top): >+ (JSC::DFG::AbstractLocalAllocation::AbstractLocalAllocation): >+ (JSC::DFG::AbstractLocalAllocation::mergeInHeap): >+ (JSC::DFG::AbstractLocalAllocation::prune): >+ (JSC::DFG::AbstractLocalAllocation::escape): >+ (JSC::DFG::AbstractLocalAllocation::escaped): >+ (JSC::DFG::AbstractLocalAllocation::isValid): >+ (JSC::DFG::AbstractLocalAllocation::mustHave): >+ (JSC::DFG::AbstractLocalAllocation::setStructures): >+ (JSC::DFG::AbstractLocalAllocation::filterStructures): >+ (JSC::DFG::AbstractLocalAllocation::operator==): >+ (JSC::DFG::AbstractLocalAllocation::operator!=): >+ (JSC::DFG::AbstractLocalAllocation::get): >+ (JSC::DFG::AbstractLocalAllocation::set): >+ (JSC::DFG::AbstractLocalAllocation::forAllFields): >+ (JSC::DFG::AbstractLocalAllocation::dump): >+ (JSC::DFG::AbstractLocalHeap::newAllocation): >+ (JSC::DFG::AbstractLocalHeap::mergeAllocation): >+ (JSC::DFG::AbstractLocalHeap::isAllocation): >+ (JSC::DFG::AbstractLocalHeap::pointer): >+ (JSC::DFG::AbstractLocalHeap::forAllAllocations): >+ (JSC::DFG::AbstractLocalHeap::forAllAliases): >+ (JSC::DFG::AbstractLocalHeap::onlyAllocation): >+ (JSC::DFG::AbstractLocalHeap::onlyLocalAllocation): >+ (JSC::DFG::AbstractLocalHeap::follow): >+ (JSC::DFG::AbstractLocalHeap::escape): >+ (JSC::DFG::AbstractLocalHeap::operator==): >+ (JSC::DFG::AbstractLocalHeap::operator!=): >+ (JSC::DFG::AbstractLocalHeap::isValid): >+ (JSC::DFG::AbstractLocalHeap::prune): >+ (JSC::DFG::AbstractLocalHeap::dump): >+ (JSC::DFG::AbstractPointer::onlyAlias): >+ (JSC::DFG::AbstractPointer::escape): >+ (JSC::DFG::AbstractPointer::isValid): >+ (JSC::DFG::AbstractPointer::prune): >+ (JSC::DFG::isAllocation): >+ (JSC::DFG::ObjectAllocationEliminationPhase::ObjectAllocationEliminationPhase): >+ (JSC::DFG::ObjectAllocationEliminationPhase::run): >+ (JSC::DFG::ObjectAllocationEliminationPhase::performSinking): >+ (JSC::DFG::ObjectAllocationEliminationPhase::performAnalysis): >+ (JSC::DFG::ObjectAllocationEliminationPhase::executeNode): >+ (JSC::DFG::ObjectAllocationEliminationPhase::endBlock): >+ (JSC::DFG::ObjectAllocationEliminationPhase::determineEliminationCandidates): >+ (JSC::DFG::ObjectAllocationEliminationPhase::lowerNonReadingOperationsOnPhantomAllocations): >+ (JSC::DFG::ObjectAllocationEliminationPhase::promoteSunkenFields): >+ (JSC::DFG::ObjectAllocationEliminationPhase::resolve): >+ (JSC::DFG::performObjectAllocationElimination): >+ * dfg/DFGObjectAllocationEliminationPhase.h: Copied from Source/JavaScriptCore/dfg/DFGLazyNode.cpp. >+ * dfg/DFGPlan.cpp: >+ (JSC::DFG::Plan::compileInThreadImpl): >+ * dfg/DFGPromotedHeapLocation.h: >+ (JSC::DFG::PromotedLocationDescriptor::PromotedLocationDescriptor): >+ (JSC::DFG::PromotedLocationDescriptorHash::hash): >+ (JSC::DFG::PromotedLocationDescriptorHash::equal): >+ (JSC::DFG::PromotedHeapLocation::operator!=): >+ * dfg/DFGValidate.cpp: >+ (JSC::DFG::Validate::validateSSA): >+ * ftl/FTLOSRExitCompiler.cpp: >+ (JSC::FTL::neededForMaterialization): >+ (JSC::FTL::compileStub): >+ * ftl/FTLOperations.cpp: >+ (JSC::FTL::operationPopulateObjectInOSR): >+ (JSC::FTL::operationMaterializeObjectInOSR): >+ * ftl/FTLOperations.h: >+ > 2015-06-13 Joseph Pecoraro <pecoraro@apple.com> > > Web Inspector: console.table() with a list of objects no longer works >diff --git a/Source/WTF/ChangeLog b/Source/WTF/ChangeLog >index 9917d9c49058475044cbe5905b9e85dc0d5feac0..a7bd45ab5ae57a320c124981b3a54c9e6aff6df3 100644 >--- a/Source/WTF/ChangeLog >+++ b/Source/WTF/ChangeLog >@@ -1,3 +1,17 @@ >+2015-06-15 Basile Clement <basile_clement@apple.com> >+ >+ Object cycles should not prevent allocation elimination/sinking >+ https://bugs.webkit.org/show_bug.cgi?id=143073 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Add operator!= to WTF::HashSet and WTF::TinyPtrSet. >+ >+ * wtf/HashSet.h: >+ (WTF::HashSet::operator!=): Added. >+ * wtf/TinyPtrSet.h: >+ (WTF::TinyPtrSet::operator!=): Added. >+ > 2015-06-12 Zan Dobersek <zdobersek@igalia.com> > > [GLib] Move files under Source/WTF/wtf/gobject to Source/WTF/wtf/glib >diff --git a/Source/JavaScriptCore/CMakeLists.txt b/Source/JavaScriptCore/CMakeLists.txt >index 15280b08435d1059f78d41d6656004882992c732..18af976d4b85a8583bce33c90f7a7c7dbd69ca18 100644 >--- a/Source/JavaScriptCore/CMakeLists.txt >+++ b/Source/JavaScriptCore/CMakeLists.txt >@@ -210,6 +210,7 @@ set(JavaScriptCore_SOURCES > dfg/DFGOSRExitCompilerCommon.cpp > dfg/DFGOSRExitJumpPlaceholder.cpp > dfg/DFGOSRExitPreparation.cpp >+ dfg/DFGObjectAllocationEliminationPhase.cpp > dfg/DFGObjectAllocationSinkingPhase.cpp > dfg/DFGObjectMaterializationData.cpp > dfg/DFGOperations.cpp >diff --git a/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj b/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj >index fc17d16e526b7fa47e33f316d8a41c9296842817..4e7add10e84d4ad57e17650d35c56c17e24c4b04 100644 >--- a/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj >+++ b/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj >@@ -448,6 +448,7 @@ > <ClCompile Include="..\dfg\DFGOSRExitCompilerCommon.cpp" /> > <ClCompile Include="..\dfg\DFGOSRExitJumpPlaceholder.cpp" /> > <ClCompile Include="..\dfg\DFGOSRExitPreparation.cpp" /> >+ <ClCompile Include="..\dfg\DFGObjectAllocationEliminationPhase.cpp" /> > <ClCompile Include="..\dfg\DFGObjectAllocationSinkingPhase.cpp" /> > <ClCompile Include="..\dfg\DFGObjectMaterializationData.cpp" /> > <ClCompile Include="..\dfg\DFGPhantomInsertionPhase.cpp" /> >@@ -1136,6 +1137,7 @@ > <ClInclude Include="..\dfg\DFGNodeFlags.h" /> > <ClInclude Include="..\dfg\DFGNodeOrigin.h" /> > <ClInclude Include="..\dfg\DFGNodeType.h" /> >+ <ClInclude Include="..\dfg\DFGObjectAllocationEliminationPhase.h" /> > <ClInclude Include="..\dfg\DFGObjectAllocationSinkingPhase.h" /> > <ClInclude Include="..\dfg\DFGObjectMaterializationData.h" /> > <ClInclude Include="..\dfg\DFGOperations.h" /> >diff --git a/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters b/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters >index 7ec422d38cf2394b51480d1a04787664b3263e79..01da46322585ad8eaf98fe876c805a598fc5cf49 100644 >--- a/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters >+++ b/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters >@@ -1731,6 +1731,9 @@ > <ClCompile Include="..\dfg\DFGNaiveDominators.cpp"> > <Filter>dfg</Filter> > </ClCompile> >+ <ClCompile Include="..\dfg\DFGObjectAllocationEliminationPhase.cpp"> >+ <Filter>dfg</Filter> >+ </ClCompile> > <ClCompile Include="..\dfg\DFGObjectAllocationSinkingPhase.cpp"> > <Filter>dfg</Filter> > </ClCompile> >@@ -4232,6 +4235,9 @@ > <ClInclude Include="..\dfg\DFGNaiveDominators.h"> > <Filter>dfg</Filter> > </ClInclude> >+ <ClInclude Include="..\dfg\DFGObjectAllocationEliminationPhase.h"> >+ <Filter>dfg</Filter> >+ </ClInclude> > <ClInclude Include="..\dfg\DFGObjectAllocationSinkingPhase.h"> > <Filter>dfg</Filter> > </ClInclude> >diff --git a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >index 83fa221ce28f6718c21afe32787b0ca0de96360b..4872989e4a29482e42f067269f382b6cc83fb880 100644 >--- a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >+++ b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >@@ -960,6 +960,8 @@ > 5DBB151B131D0B310056AD36 /* testapi.js in Copy Support Script */ = {isa = PBXBuildFile; fileRef = 14D857740A4696C80032146C /* testapi.js */; }; > 5DBB1525131D0BD70056AD36 /* minidom.js in Copy Support Script */ = {isa = PBXBuildFile; fileRef = 1412110D0A48788700480255 /* minidom.js */; }; > 5DE6E5B30E1728EC00180407 /* create_hash_table in Headers */ = {isa = PBXBuildFile; fileRef = F692A8540255597D01FF60F7 /* create_hash_table */; settings = {ATTRIBUTES = (); }; }; >+ 6258A1D81B28CBD300C5E100 /* DFGObjectAllocationEliminationPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6258A1D61B28CBD300C5E100 /* DFGObjectAllocationEliminationPhase.cpp */; }; >+ 6258A1D91B28CBD300C5E100 /* DFGObjectAllocationEliminationPhase.h in Headers */ = {isa = PBXBuildFile; fileRef = 6258A1D71B28CBD300C5E100 /* DFGObjectAllocationEliminationPhase.h */; }; > 62D2D38F1ADF103F000206C1 /* FunctionRareData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 62D2D38D1ADF103F000206C1 /* FunctionRareData.cpp */; }; > 62D2D3901ADF103F000206C1 /* FunctionRareData.h in Headers */ = {isa = PBXBuildFile; fileRef = 62D2D38E1ADF103F000206C1 /* FunctionRareData.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 62F2AA371B0BEDE300610C7A /* DFGLazyNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 62A9A29E1B0BED4800BD54CA /* DFGLazyNode.cpp */; }; >@@ -2680,6 +2682,8 @@ > 5DAFD6CB146B686300FBEFB4 /* JSC.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = JSC.xcconfig; sourceTree = "<group>"; }; > 5DDDF44614FEE72200B4FB4D /* LLIntDesiredOffsets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LLIntDesiredOffsets.h; path = LLIntOffsets/LLIntDesiredOffsets.h; sourceTree = BUILT_PRODUCTS_DIR; }; > 5DE3D0F40DD8DDFB00468714 /* WebKitAvailability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebKitAvailability.h; sourceTree = "<group>"; }; >+ 6258A1D61B28CBD300C5E100 /* DFGObjectAllocationEliminationPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGObjectAllocationEliminationPhase.cpp; path = dfg/DFGObjectAllocationEliminationPhase.cpp; sourceTree = "<group>"; }; >+ 6258A1D71B28CBD300C5E100 /* DFGObjectAllocationEliminationPhase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGObjectAllocationEliminationPhase.h; path = dfg/DFGObjectAllocationEliminationPhase.h; sourceTree = "<group>"; }; > 62A9A29E1B0BED4800BD54CA /* DFGLazyNode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGLazyNode.cpp; path = dfg/DFGLazyNode.cpp; sourceTree = "<group>"; }; > 62A9A29F1B0BED4800BD54CA /* DFGLazyNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGLazyNode.h; path = dfg/DFGLazyNode.h; sourceTree = "<group>"; }; > 62D2D38D1ADF103F000206C1 /* FunctionRareData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FunctionRareData.cpp; sourceTree = "<group>"; }; >@@ -5032,6 +5036,8 @@ > 0FA581B8150E952A00B9A2D9 /* DFGNodeFlags.h */, > 0F300B7718AB051100A6D72E /* DFGNodeOrigin.h */, > 0FA581B9150E952A00B9A2D9 /* DFGNodeType.h */, >+ 6258A1D61B28CBD300C5E100 /* DFGObjectAllocationEliminationPhase.cpp */, >+ 6258A1D71B28CBD300C5E100 /* DFGObjectAllocationEliminationPhase.h */, > 0F2B9CDA19D0BA7D00B1D1B5 /* DFGObjectAllocationSinkingPhase.cpp */, > 0F2B9CDB19D0BA7D00B1D1B5 /* DFGObjectAllocationSinkingPhase.h */, > 0F2B9CDC19D0BA7D00B1D1B5 /* DFGObjectMaterializationData.cpp */, >@@ -6241,6 +6247,7 @@ > 0FE0502D1AA9095600D33B33 /* VarOffset.h in Headers */, > 0F2B670117B6B5AB00A7AE3F /* JSUint8ClampedArray.h in Headers */, > 86E3C612167BABD7006D760A /* JSValue.h in Headers */, >+ 6258A1D91B28CBD300C5E100 /* DFGObjectAllocationEliminationPhase.h in Headers */, > 86E3C61B167BABEE006D760A /* JSValueInternal.h in Headers */, > BC18C42C0E16F5CD00B34460 /* JSValueRef.h in Headers */, > BC18C42D0E16F5CD00B34460 /* JSEnvironmentRecord.h in Headers */, >@@ -7500,6 +7507,7 @@ > 14469DE4107EC7E700650446 /* NumberPrototype.cpp in Sources */, > 86F3EEBE168CDE930077B92A /* ObjCCallbackFunction.mm in Sources */, > 14469DE5107EC7E700650446 /* ObjectConstructor.cpp in Sources */, >+ 6258A1D81B28CBD300C5E100 /* DFGObjectAllocationEliminationPhase.cpp in Sources */, > 14469DE6107EC7E700650446 /* ObjectPrototype.cpp in Sources */, > E124A8F80E555775003091F1 /* OpaqueJSString.cpp in Sources */, > 969A079A0ED1D3AE00F1F681 /* Opcode.cpp in Sources */, >diff --git a/Source/JavaScriptCore/dfg/DFGLazyNode.cpp b/Source/JavaScriptCore/dfg/DFGLazyNode.cpp >index 954ed9855d5ee2a5362a9c17b7a57314b2a25469..c8d0940e77f3fdb911baa44db5c4dee2085912c2 100644 >--- a/Source/JavaScriptCore/dfg/DFGLazyNode.cpp >+++ b/Source/JavaScriptCore/dfg/DFGLazyNode.cpp >@@ -38,8 +38,7 @@ void LazyNode::dump(PrintStream& out) const > if (isNode()) > out.print("LazyNode:@", asNode()->index()); > else >- out.print("LazyNode:FrozenValue:", Graph::opName(op()), ", ", pointerDump(asValue())); >- out.print(")"); >+ out.print("LazyNode:FrozenValue(", Graph::opName(op()), ", ", pointerDump(asValue()), ")"); > } > } > >diff --git a/Source/JavaScriptCore/dfg/DFGObjectAllocationEliminationPhase.cpp b/Source/JavaScriptCore/dfg/DFGObjectAllocationEliminationPhase.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..713b7f7dd65ca948057762d476a3a7ea58f9bd47 >--- /dev/null >+++ b/Source/JavaScriptCore/dfg/DFGObjectAllocationEliminationPhase.cpp >@@ -0,0 +1,1324 @@ >+/* >+ * Copyright (C) 2015 Apple Inc. All rights reserved. >+ * >+ * 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. >+ */ >+ >+#include "config.h" >+#include "DFGObjectAllocationEliminationPhase.h" >+ >+#if ENABLE(DFG_JIT) >+ >+#include "DFGBlockMapInlines.h" >+#include "DFGGraph.h" >+#include "DFGInsertionSet.h" >+#include "DFGPhase.h" >+#include "DFGPhiChildren.h" >+#include "DFGPromoteHeapAccess.h" >+#include "DFGSSACalculator.h" >+#include "DFGValidate.h" >+#include "JSCInlines.h" >+ >+namespace JSC { namespace DFG { >+ >+static bool verbose = false; >+ >+class AbstractLocalHeap; >+ >+class AbstractPointer { >+public: >+ AbstractPointer() >+ { >+ } >+ >+ explicit AbstractPointer(Node* node) >+ : m_aliases { node } >+ { >+ } >+ >+ void merge(const AbstractPointer& other) >+ { >+ m_top |= other.m_top; >+ m_aliases.add(other.m_aliases.begin(), other.m_aliases.end()); >+ } >+ >+ Node* onlyAlias(const AbstractLocalHeap&) const; >+ >+ void escape(AbstractLocalHeap&); >+ >+ bool isValid(const AbstractLocalHeap&) const; >+ >+ bool operator==(const AbstractPointer& other) const >+ { >+ return m_top == other.m_top && m_aliases == other.m_aliases; >+ } >+ >+ bool operator!=(const AbstractPointer& other) const >+ { >+ return !(*this == other); >+ } >+ >+ template<typename Functor> >+ void forAllAliases(const Functor& callback) >+ { >+ for (Node* node : m_aliases) >+ callback(node); >+ } >+ >+ template<typename Functor> >+ void forAllAliases(const Functor& callback) const >+ { >+ for (Node* node : m_aliases) >+ callback(node); >+ } >+ >+ void dump(PrintStream& out) const >+ { >+ out.print(listDump(m_aliases, ", ")); >+ if (m_top) >+ out.print(m_aliases.isEmpty() ? "" : ", ", "<heap>"); >+ } >+ >+ // This ensures that we are only keeping track of unescaped local >+ // allocations for the current heap. Pruning is needed at the head >+ // of blocks so that we don't confuse an allocation in a loop with >+ // a previous version of itself. >+ // Technically, we only need to prune at the head of blocks that >+ // have incoming backwards edges, and we could avoid pruning >+ // entirely by having reverse pointers from the allocations to the >+ // pointers that can point to it, and performing the removal at >+ // the moment we escape. >+ // If we did this, we could check that all the aliases are >+ // unescaped allocations (instead of merely allocations) in >+ // isValid(). >+ void prune(AbstractLocalHeap&); >+ >+ static AbstractPointer top() >+ { >+ return AbstractPointer(true); >+ } >+ >+private: >+ AbstractPointer(bool top) >+ : m_top(top) >+ { >+ } >+ >+ // These are the local allocations this can point to. >+ // Except at the head of a block, it may contain escaped local >+ // allocations. >+ HashSet<Node*> m_aliases; >+ // This tells us whether the pointer can point to something other >+ // than an unescaped local allocation >+ bool m_top = false; >+}; >+ >+class AbstractLocalAllocation { >+public: >+ AbstractLocalAllocation() >+ { >+ } >+ >+ void mergeInHeap(const AbstractLocalAllocation& other, AbstractLocalHeap& heap) >+ { >+ if (m_escaped) { >+ for (auto pair : other.m_fields) >+ pair.value.escape(heap); >+ } else if (other.m_escaped) >+ escape(heap); >+ else { >+ for (auto pair : other.m_fields) >+ m_fields.add(pair.key, AbstractPointer()).iterator->value.merge(pair.value); >+ } >+ } >+ >+ void prune(AbstractLocalHeap& heap) >+ { >+ for (auto& pair : m_fields) >+ pair.value.prune(heap); >+ } >+ >+ void escape(AbstractLocalHeap& heap) >+ { >+ if (m_escaped) >+ return; >+ >+ m_escaped = true; >+ for (auto pair : m_fields) >+ pair.value.escape(heap); >+ m_fields.clear(); >+ m_structures.clear(); >+ } >+ >+ bool escaped() const >+ { >+ return m_escaped; >+ } >+ >+ bool isValid(const AbstractLocalHeap& heap) const >+ { >+ if (m_escaped) >+ return m_fields.isEmpty() && m_structures.isEmpty(); >+ >+ for (auto pair : m_fields) { >+ if (!pair.value.isValid(heap)) >+ return false; >+ } >+ >+ return true; >+ } >+ >+ >+ template<typename... Params> >+ bool mustHave(Graph& graph, Params... params) >+ { >+ return mustHave(graph, PromotedLocationDescriptor(params...)); >+ } >+ >+ bool mustHave(Graph& graph, PromotedLocationDescriptor descriptor) >+ { >+ switch (descriptor.kind()) { >+ case NamedPropertyPLoc: { >+ // Note that this returns true if we have no structures. >+ UniquedStringImpl* uid = graph.identifiers()[descriptor.info()]; >+ for (Structure* structure : m_structures) { >+ if (!isValidOffset(structure->getConcurrently(uid))) >+ return false; >+ } >+ return true; >+ } >+ >+ case ClosureVarPLoc: >+ return m_fields.contains(descriptor); >+ >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ } >+ } >+ >+ void setStructures(const StructureSet& structures) >+ { >+ ASSERT(!structures.isEmpty()); >+ m_structures = structures; >+ } >+ >+ void filterStructures(const StructureSet& structures) >+ { >+ m_structures.filter(structures); >+ } >+ >+ bool operator==(const AbstractLocalAllocation& other) const >+ { >+ return m_escaped == other.m_escaped >+ && m_fields == other.m_fields >+ && m_structures == other.m_structures; >+ } >+ >+ bool operator!=(const AbstractLocalAllocation& other) const >+ { >+ return !(*this == other); >+ } >+ >+ >+ template<typename... Params> >+ AbstractPointer get(Params... params) const >+ { >+ return get(PromotedLocationDescriptor(params...)); >+ } >+ >+ AbstractPointer get(PromotedLocationDescriptor descriptor) const >+ { >+ ASSERT(m_structures.isEmpty() || m_fields.contains(descriptor)); >+ return m_fields.get(descriptor); >+ } >+ >+ void set(PromotedLocationDescriptor descriptor, AbstractPointer pointer) >+ { >+ m_fields.set(descriptor, pointer); >+ } >+ >+ template<typename Functor> >+ void forAllFields(const Functor& callback) >+ { >+ for (auto& pair : m_fields) >+ callback(pair.key, pair.value); >+ } >+ >+ template<typename Functor> >+ void forAllFields(const Functor& callback) const >+ { >+ for (const auto& pair : m_fields) >+ callback(pair.key, pair.value); >+ } >+ >+ void dump(PrintStream& out) const >+ { >+ if (m_escaped) >+ out.print("escaped"); >+ out.print("\n ", mapDump(m_fields, " => ", "\n "), "\n"); >+ } >+ >+private: >+ bool m_escaped = false; >+ HashMap<PromotedLocationDescriptor, AbstractPointer> m_fields; >+ StructureSet m_structures; >+}; >+ >+class AbstractLocalHeap { >+public: >+ AbstractLocalAllocation& newAllocation(Node* node) >+ { >+ ASSERT(!m_allocations.contains(node)); >+ return m_allocations.add(node, AbstractLocalAllocation()).iterator->value; >+ } >+ >+ void mergeAllocation(Node* node, const AbstractLocalAllocation& allocation) >+ { >+ m_allocations.add( >+ node, AbstractLocalAllocation()).iterator->value.mergeInHeap(allocation, *this); >+ } >+ >+ bool isAllocation(Node* node) const >+ { >+ return m_allocations.contains(node); >+ } >+ >+ AbstractPointer& pointer(Node* node) >+ { >+ return m_aliases.add(node, AbstractPointer()).iterator->value; >+ } >+ >+ template<typename Functor> >+ void forAllAllocations(const Functor& callback) >+ { >+ for (auto& pair : m_allocations) >+ callback(pair.key, pair.value); >+ } >+ >+ template<typename Functor> >+ void forAllAllocations(const Functor& callback) const >+ { >+ for (const auto& pair : m_allocations) >+ callback(pair.key, pair.value); >+ } >+ >+ template<typename Functor> >+ void forAllAliases(const Functor& callback) >+ { >+ for (auto& pair : m_aliases) >+ callback(pair.key, pair.value); >+ } >+ >+ template<typename Functor> >+ void forAllAliases(const Functor& callback) const >+ { >+ for (const auto& pair : m_aliases) >+ callback(pair.key, pair.value); >+ } >+ >+ WTF::KeyValuePair<Node*, const AbstractLocalAllocation*> onlyAllocation(Node* node) const >+ { >+ { >+ auto iter = m_allocations.find(node); >+ if (iter != m_allocations.end()) >+ return { node, &iter->value }; >+ } >+ { >+ auto iter = m_aliases.find(node); >+ if (iter == m_aliases.end()) >+ return { nullptr, nullptr }; >+ Node* alias = iter->value.onlyAlias(*this); >+ if (!alias) >+ return { nullptr, nullptr }; >+ ASSERT(m_allocations.contains(alias)); >+ return { alias, &m_allocations.find(alias)->value }; >+ } >+ } >+ >+ WTF::KeyValuePair<Node*, AbstractLocalAllocation*> onlyAllocation(Node* node) >+ { >+ { >+ auto iter = m_allocations.find(node); >+ if (iter != m_allocations.end()) >+ return { node, &iter->value }; >+ } >+ { >+ auto iter = m_aliases.find(node); >+ if (iter == m_aliases.end()) >+ return { nullptr, nullptr }; >+ Node* alias = iter->value.onlyAlias(*this); >+ if (!alias) >+ return { nullptr, nullptr }; >+ ASSERT(m_allocations.contains(alias)); >+ return { alias, &m_allocations.find(alias)->value }; >+ } >+ } >+ >+ WTF::KeyValuePair<Node*, AbstractLocalAllocation*> onlyLocalAllocation(Node* node) >+ { >+ WTF::KeyValuePair<Node*, AbstractLocalAllocation*> result = onlyAllocation(node); >+ if (result.value && !result.value->escaped()) >+ return result; >+ return { nullptr, nullptr }; >+ } >+ >+ AbstractPointer follow(Node* node) const >+ { >+ { >+ auto iter = m_allocations.find(node); >+ if (iter != m_allocations.end()) >+ return iter->value.escaped() ? AbstractPointer::top() : AbstractPointer(node); >+ } >+ { >+ auto iter = m_aliases.find(node); >+ return iter == m_aliases.end() ? AbstractPointer::top() : iter->value; >+ } >+ } >+ >+ void escape(Node* node) >+ { >+ { >+ auto iter = m_allocations.find(node); >+ if (iter != m_allocations.end()) >+ iter->value.escape(*this); >+ } >+ { >+ auto iter = m_aliases.find(node); >+ if (iter != m_aliases.end()) >+ iter->value.escape(*this); >+ } >+ } >+ >+ bool operator==(const AbstractLocalHeap& other) >+ { >+ return m_allocations == other.m_allocations >+ && m_aliases == other.m_aliases; >+ } >+ >+ bool operator!=(const AbstractLocalHeap& other) >+ { >+ return !(*this == other); >+ } >+ >+ bool isValid() const >+ { >+ for (auto pair : m_allocations) { >+ if (!pair.value.isValid(*this)) >+ return false; >+ } >+ >+ for (auto pair : m_aliases) { >+ if (!pair.value.isValid(*this)) >+ return false; >+ } >+ >+ return true; >+ } >+ >+ void prune() >+ { >+ for (auto& pair : m_allocations) >+ pair.value.prune(*this); >+ for (auto& pair : m_aliases) >+ pair.value.prune(*this); >+ } >+ >+ void dump(PrintStream& out = WTF::dataFile()) const >+ { >+ out.print(" Allocations:\n ", mapDump(m_allocations, " => ", "\n "), "\n"); >+ out.print(" Aliases:\n ", mapDump(m_aliases, " => ", "\n "), "\n"); >+ } >+ >+private: >+ HashMap<Node*, AbstractLocalAllocation> m_allocations; >+ HashMap<Node*, AbstractPointer> m_aliases; >+}; >+ >+Node* AbstractPointer::onlyAlias(const AbstractLocalHeap& heap) const >+{ >+ if (m_top || m_aliases.size() != 1) >+ return nullptr; >+ >+ Node* alias = *m_aliases.begin(); >+ ASSERT(heap.isAllocation(alias)); >+ if (heap.onlyAllocation(alias).value->escaped()) >+ return nullptr; >+ return alias; >+} >+ >+void AbstractPointer::escape(AbstractLocalHeap& heap) >+{ >+ for (Node* alias : m_aliases) >+ heap.onlyAllocation(alias).value->escape(heap); >+ if (m_aliases.size() > 0) >+ m_top = true; >+ m_aliases.clear(); >+} >+ >+bool AbstractPointer::isValid(const AbstractLocalHeap& heap) const >+{ >+ for (Node* node : m_aliases) { >+ if (!heap.isAllocation(node)) >+ return false; >+ } >+ >+ return true; >+} >+ >+void AbstractPointer::prune(AbstractLocalHeap& heap) >+{ >+ Vector<Node*> toRemove; >+ for (Node* node : m_aliases) { >+ AbstractLocalAllocation* allocation = heap.onlyAllocation(node).value; >+ if (!allocation || allocation->escaped()) >+ toRemove.append(node); >+ } >+ for (Node* node : toRemove) >+ m_aliases.remove(node); >+ if (toRemove.size() > 0) >+ m_top = true; >+ ASSERT(isValid(heap)); >+} >+ >+static bool isAllocation(Node* node) >+{ >+ switch (node->op()) { >+ case NewObject: >+ case NewFunction: >+ case MaterializeNewObject: >+ case CreateActivation: >+ case MaterializeCreateActivation: >+ return true; >+ >+ default: >+ return false; >+ } >+} >+ >+class ObjectAllocationEliminationPhase : public Phase { >+public: >+ ObjectAllocationEliminationPhase(Graph& graph) >+ : Phase(graph, "object allocation elimination") >+ , m_ssaCalculator(graph) >+ , m_insertionSet(graph) >+ { >+ } >+ >+ bool run() >+ { >+ ASSERT(m_graph.m_form == SSA); >+ ASSERT(m_graph.m_fixpointState == FixpointNotConverged); >+ >+ if (!performSinking()) >+ return false; >+ >+ while (performSinking()) { } >+ >+ if (verbose) { >+ dataLog("Graph after sinking:\n"); >+ m_graph.dump(); >+ } >+ >+ return true; >+ } >+ >+private: >+ bool performSinking() >+ { >+ m_graph.computeRefCounts(); >+ m_graph.initializeNodeOwners(); >+ m_phiChildren = PhiChildren(m_graph); >+ >+ CString graphBeforeSinking; >+ if (Options::verboseValidationFailure() && Options::validateGraphAtEachPhase()) { >+ StringPrintStream out; >+ m_graph.dump(out); >+ graphBeforeSinking = out.toCString(); >+ } >+ >+ if (verbose) { >+ dataLog("Graph before sinking:\n"); >+ m_graph.dump(); >+ } >+ >+ performAnalysis(); >+ determineEliminationCandidates(); >+ >+ if (m_eliminationCandidates.isEmpty()) >+ return false; >+ >+ lowerNonReadingOperationsOnPhantomAllocations(); >+ promoteSunkenFields(); >+ >+ if (Options::validateGraphAtEachPhase()) >+ validate(m_graph, DumpGraph, graphBeforeSinking); >+ >+ if (verbose) >+ dataLog("Sinking iteration changed the graph.\n"); >+ return true; >+ } >+ >+ void performAnalysis() >+ { >+ // The premise of this pass is that if there exists a point in the program where some >+ // path from a phantom allocation site to that point causes materialization, then *all* >+ // paths cause materialization. This should mean that there are never any redundant >+ // materializations. >+ >+ m_heapAtHead = BlockMap<AbstractLocalHeap>(m_graph); >+ m_heapAtTail = BlockMap<AbstractLocalHeap>(m_graph); >+ >+ bool changed; >+ do { >+ if (verbose) >+ dataLog("Doing iteration of escape analysis.\n"); >+ changed = false; >+ >+ for (BasicBlock* block : m_graph.blocksInPreOrder()) { >+ m_heap = m_heapAtHead[block]; >+ >+ for (Node* node : *block) >+ executeNode(node); >+ >+ endBlock(block); >+ >+ if (m_heap == m_heapAtTail[block]) >+ continue; >+ >+ m_heapAtTail[block] = m_heap; >+ changed = true; >+ >+ ASSERT(m_heapAtTail[block].isValid()); >+ >+ // FIXME: We should be doing liveness pruning so that >+ // we only propagate things that are alive. >+ // For now, we use the strict-dominance approximation, >+ // but the correct way of doing things is to propagate >+ // only unsescaped things that are alive, or >+ // (recursively) stored into a field of an alive node. >+ >+ for (BasicBlock* successorBlock : block->successors()) { >+ m_heap.forAllAllocations( >+ [&] (Node* node, const AbstractLocalAllocation& allocation) { >+ if (!m_graph.m_dominators.strictlyDominates(node->owner, successorBlock)) >+ return; >+ >+ m_heapAtHead[successorBlock].mergeAllocation(node, allocation); >+ }); >+ m_heap.forAllAliases( >+ [&] (Node* node, const AbstractPointer& pointer) { >+ if (!m_graph.m_dominators.strictlyDominates(node->owner, successorBlock)) >+ return; >+ >+ m_heapAtHead[successorBlock].pointer(node).merge(pointer); >+ }); >+ m_heapAtHead[successorBlock].prune(); >+ >+ ASSERT(m_heapAtHead[successorBlock].isValid()); >+ } >+ } >+ } while (changed); >+ >+ // Redirect the edges so that subsequent steps don't need to >+ // run the points-to analysis to know which allocation a Put* >+ // node is writing to. >+ // FIXME: This is also when we should be recording escape points >+ for (BasicBlock* block : m_graph.blocksInNaturalOrder()) { >+ if (verbose) >+ dataLog("Heap at head of ", *block, ": \n", m_heapAtHead[block]); >+ >+ m_heap = m_heapAtHead[block]; >+ for (Node* node : *block) { >+ m_graph.doToChildren( >+ node, >+ [&] (Edge& edge) { >+ if (Node* allocation = m_heap.onlyLocalAllocation(edge.node()).key) >+ edge.setNode(allocation); >+ }); >+ executeNode(node); >+ } >+ >+ if (verbose) >+ dataLog("Heap at tail of ", *block, ": \n", m_heapAtTail[block]); >+ } >+ } >+ >+ void executeNode(Node* node) >+ { >+ ASSERT(m_heap.isValid()); >+ switch (node->op()) { >+ case NewObject: >+ m_heap.newAllocation(node).setStructures(node->structure()); >+ break; >+ >+ case MaterializeNewObject: { >+ StructureSet structures; >+ m_phiChildren.forAllTransitiveIncomingValues( >+ m_graph.varArgChild(node, 0).node(), >+ [&] (Node* structure) { >+ ASSERT(structure->isConstant()); >+ structures.add(structure->castConstant<Structure*>()); >+ }); >+ AbstractLocalAllocation& allocation = m_heap.newAllocation(node); >+ allocation.setStructures(structures); >+ for (unsigned i = 0; i < node->objectMaterializationData().m_properties.size(); ++i) { >+ allocation.set( >+ PromotedLocationDescriptor( >+ NamedPropertyPLoc, >+ node->objectMaterializationData().m_properties[i].m_identifierNumber), >+ m_heap.follow(m_graph.varArgChild(node, i + 1).node())); >+ } >+ break; >+ } >+ >+ case NewFunction: { >+ if (node->castOperand<FunctionExecutable*>()->singletonFunction()->isStillValid()) { >+ m_heap.escape(node->child1().node()); >+ break; >+ } >+ // No need to set the FunctionExecutablePLoc since it is a constant >+ m_heap.newAllocation(node).set(FunctionActivationPLoc, m_heap.follow(node->child1().node())); >+ break; >+ } >+ >+ case CreateActivation: { >+ if (node->castOperand<SymbolTable*>()->singletonScope()->isStillValid()) { >+ m_heap.escape(node->child1().node()); >+ break; >+ } >+ AbstractLocalAllocation& allocation = m_heap.newAllocation(node); >+ // No need to set the SymbolTablePLoc since it is a constant >+ allocation.set(ActivationScopePLoc, m_heap.follow(node->child1().node())); >+ { >+ SymbolTable* symbolTable = node->castOperand<SymbolTable*>(); >+ ConcurrentJITLocker locker(symbolTable->m_lock); >+ for (auto iter = symbolTable->begin(locker), end = symbolTable->end(locker); iter != end; ++iter) { >+ allocation.set( >+ PromotedLocationDescriptor(ClosureVarPLoc, iter->value.scopeOffset().offset()), >+ AbstractPointer::top()); >+ } >+ } >+ break; >+ } >+ >+ case MaterializeCreateActivation: { >+ // We have sunk this once already - there is no way the >+ // symbol table's singleton scope is still valid. >+ ASSERT(!node->castOperand<SymbolTable*>()->singletonScope()->isStillValid()); >+ AbstractLocalAllocation& allocation = m_heap.newAllocation(node); >+ // No need to set the SymbolTablePLoc since it is a >+ // constant >+ allocation.set(ActivationScopePLoc, m_heap.follow(m_graph.varArgChild(node, 0).node())); >+ for (unsigned i = 0; i < node->objectMaterializationData().m_properties.size(); ++i) { >+ allocation.set( >+ PromotedLocationDescriptor( >+ ClosureVarPLoc, >+ node->objectMaterializationData().m_properties[i].m_identifierNumber), >+ m_heap.follow(m_graph.varArgChild(node, i + 1).node())); >+ } >+ break; >+ } >+ >+ case PutStructure: { >+ auto pair = m_heap.onlyLocalAllocation(node->child1().node()); >+ if (pair.key && pair.key->isObjectAllocation()) >+ pair.value->setStructures(node->transition()->next); >+ else >+ m_heap.escape(node->child1().node()); >+ break; >+ } >+ >+ case CheckStructure: { >+ auto pair = m_heap.onlyLocalAllocation(node->child1().node()); >+ if (pair.key && pair.key->isObjectAllocation()) >+ pair.value->filterStructures(node->structureSet()); >+ else >+ m_heap.escape(node->child1().node()); >+ break; >+ } >+ >+ case GetByOffset: >+ case GetGetterSetterByOffset: { >+ auto pair = m_heap.onlyLocalAllocation(node->child2().node()); >+ if (pair.key && pair.key->isObjectAllocation()) { >+ unsigned identifierNumber = node->storageAccessData().identifierNumber; >+ ASSERT(pair.value->mustHave(m_graph, NamedPropertyPLoc, identifierNumber)); >+ m_heap.pointer(node).merge(pair.value->get(NamedPropertyPLoc, identifierNumber)); >+ } else { >+ m_heap.escape(node->child1().node()); >+ m_heap.escape(node->child2().node()); >+ } >+ break; >+ } >+ >+ case MultiGetByOffset: { >+ auto pair = m_heap.onlyLocalAllocation(node->child1().node()); >+ if (pair.key && pair.key->isObjectAllocation()) { >+ unsigned identifierNumber = node->storageAccessData().identifierNumber; >+ ASSERT(pair.value->mustHave(m_graph, NamedPropertyPLoc, identifierNumber)); >+ m_heap.pointer(node).merge(pair.value->get(NamedPropertyPLoc, identifierNumber)); >+ } else >+ m_heap.escape(node->child1().node()); >+ break; >+ } >+ >+ case PutByOffset: { >+ auto pair = m_heap.onlyLocalAllocation(node->child2().node()); >+ if (pair.key && pair.key->isObjectAllocation()) { >+ unsigned identifierNumber = node->storageAccessData().identifierNumber; >+ pair.value->set( >+ PromotedLocationDescriptor(NamedPropertyPLoc, identifierNumber), >+ m_heap.follow(node->child3().node())); >+ } else { >+ m_heap.escape(node->child1().node()); >+ m_heap.escape(node->child2().node()); >+ m_heap.escape(node->child3().node()); >+ } >+ break; >+ } >+ >+ case GetClosureVar: { >+ auto pair = m_heap.onlyLocalAllocation(node->child1().node()); >+ if (pair.key && pair.key->isActivationAllocation()) { >+ ASSERT(pair.value->mustHave(m_graph, ClosureVarPLoc, node->scopeOffset().offset())); >+ m_heap.pointer(node).merge(pair.value->get(ClosureVarPLoc, node->scopeOffset().offset())); >+ } else >+ m_heap.escape(node->child1().node()); >+ break; >+ } >+ >+ case PutClosureVar: { >+ auto pair = m_heap.onlyLocalAllocation(node->child1().node()); >+ if (pair.key && pair.key->isActivationAllocation()) { >+ pair.value->set( >+ PromotedLocationDescriptor(ClosureVarPLoc, node->scopeOffset().offset()), >+ m_heap.follow(node->child2().node())); >+ } else { >+ m_heap.escape(node->child1().node()); >+ m_heap.escape(node->child2().node()); >+ } >+ break; >+ } >+ >+ case Check: >+ m_graph.doToChildren( >+ node, >+ [&] (Edge edge) { >+ if (edge.willNotHaveCheck()) >+ return; >+ >+ if (alreadyChecked(edge.useKind(), SpecObject)) >+ return; >+ >+ m_heap.escape(edge.node()); >+ }); >+ break; >+ >+ case MovHint: >+ case PutHint: >+ break; >+ >+ default: >+ m_graph.doToChildren( >+ node, >+ [&] (Edge edge) { >+ m_heap.escape(edge.node()); >+ }); >+ break; >+ } >+ ASSERT(m_heap.isValid()); >+ } >+ >+ void endBlock(BasicBlock* block) >+ { >+ // Emulate Upsilon nodes when needed ; we need an escape >+ // because OSR exit >+ // FIXME: The correct way of handling this is that is a >+ // non-dominated local allocation is put into a *live* local >+ // allocation, it should be escaped. "Live" in this sense >+ // needs to consider the possibility that a dead allocation >+ // can actually be live because it may be pointed to either by >+ // a field of another allocation, or by an alias. >+ if (block->numSuccessors() == 1) { >+ for (BasicBlock* successorBlock : block->successors()) { >+ HashSet<Node*> toEscape; >+ m_heap.forAllAllocations( >+ [&] (Node* node, AbstractLocalAllocation& allocation) { >+ if (m_graph.m_dominators.strictlyDominates(node->owner, successorBlock)) { >+ allocation.forAllFields( >+ [&] (PromotedLocationDescriptor, AbstractPointer& pointer) { >+ pointer.forAllAliases( >+ [&] (Node* alias) { >+ if (!m_graph.m_dominators.strictlyDominates(alias->owner, successorBlock)) >+ toEscape.add(alias); >+ }); >+ }); >+ } >+ }); >+ for (Node* node : toEscape) >+ m_heap.onlyAllocation(node).value->escape(m_heap); >+ } >+ } >+#if !ASSERT_DISABLED >+ else { >+ // We have broken critical edges so the dominators should not change >+ for (BasicBlock* successorBlock : block->successors()) { >+ m_heap.forAllAllocations( >+ [&] (Node* node, const AbstractLocalAllocation& allocation) { >+ ASSERT(m_graph.m_dominators.strictlyDominates(node->owner, successorBlock)); >+ ASSERT(allocation.isValid(m_heap)); >+ }); >+ } >+ } >+#endif // !ASSERT_DISABLED >+ } >+ >+ void determineEliminationCandidates() >+ { >+ m_eliminationCandidates.clear(); >+ >+ // The elimination candidates are the object allocations that >+ // never escapes, and are never put into escaping objects. >+ HashMap<Node*, bool> escaping; >+ HashMap<Node*, Vector<Node*>> deps; >+ for (BasicBlock* block : m_graph.blocksInNaturalOrder()) { >+ for (Node* node : *block) { >+ switch (node->op()) { >+ case MaterializeNewObject: >+ for (unsigned i = 0; i < node->objectMaterializationData().m_properties.size(); ++i) { >+ if (isAllocation(m_graph.varArgChild(node, i + 1).node())) { >+ deps.add(node, >+ Vector<Node*>()).iterator->value.append( >+ m_graph.varArgChild(node, i + 1).node()); >+ } >+ } >+ break; >+ >+ case NewFunction: >+ if (isAllocation(node->child1().node())) >+ deps.add(node, Vector<Node*>()).iterator->value.append(node->child1().node()); >+ break; >+ >+ case CreateActivation: >+ if (isAllocation(node->child1().node())) >+ deps.add(node, Vector<Node*>()).iterator->value.append(node->child1().node()); >+ break; >+ >+ case MaterializeCreateActivation: >+ m_graph.doToChildren( >+ node, >+ [&] (const Edge& edge) { >+ if (isAllocation(edge.node())) >+ deps.add(node, Vector<Node*>()).iterator->value.append(edge.node()); >+ }); >+ break; >+ >+ case PutByOffset: >+ if (node->child2()->isObjectAllocation() && isAllocation(node->child3().node())) { >+ deps.add( >+ node->child2().node(), >+ Vector<Node*>()).iterator->value.append(node->child3().node()); >+ } >+ break; >+ >+ case PutClosureVar: >+ if (node->child1()->isActivationAllocation() && isAllocation(node->child2().node())) { >+ deps.add( >+ node->child1().node(), >+ Vector<Node*>()).iterator->value.append(node->child2().node()); >+ } >+ break; >+ >+ default: >+ break; >+ } >+ } >+ m_heapAtTail[block].forAllAllocations( >+ [&] (Node* node, const AbstractLocalAllocation& allocation) { >+ escaping.add(node, false).iterator->value |= allocation.escaped(); >+ }); >+ } >+ >+ Vector<Node*> worklist; >+ for (auto pair : escaping) { >+ if (pair.value) >+ worklist.append(pair.key); >+ } >+ >+ while (!worklist.isEmpty()) { >+ for (Node* node : deps.get(worklist.takeLast())) { >+ if (escaping.contains(node) && !escaping.get(node)) { >+ escaping.set(node, true); >+ worklist.append(node); >+ } >+ } >+ } >+ >+ for (auto pair : escaping) { >+ if (!pair.value) >+ m_eliminationCandidates.add(pair.key); >+ } >+ >+ if (verbose) >+ dataLog("Candidates: ", listDump(m_eliminationCandidates), "\n"); >+ } >+ >+ void lowerNonReadingOperationsOnPhantomAllocations() >+ { >+ // Lower everything but reading operations on phantom allocations. We absolutely have to >+ // lower all writes so as to reveal them to the SSA calculator. We cannot lower reads >+ // because the whole point is that those go away completely. >+ >+ for (BasicBlock* block : m_graph.blocksInPreOrder()) { >+ m_heap = m_heapAtHead[block]; >+ for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) { >+ Node* node = block->at(nodeIndex); >+ switch (node->op()) { >+ case NewObject: >+ if (m_eliminationCandidates.contains(node)) { >+ Node* structure = m_insertionSet.insertConstant( >+ nodeIndex + 1, node->origin, JSValue(node->structure())); >+ m_insertionSet.insert( >+ nodeIndex + 1, >+ PromotedHeapLocation(StructurePLoc, node).createHint( >+ m_graph, node->origin, structure)); >+ node->convertToPhantomNewObject(); >+ } >+ break; >+ >+ case MaterializeNewObject: >+ if (m_eliminationCandidates.contains(node)) { >+ m_insertionSet.insert( >+ nodeIndex + 1, >+ PromotedHeapLocation(StructurePLoc, node).createHint( >+ m_graph, node->origin, m_graph.varArgChild(node, 0).node())); >+ for (unsigned i = 0; i < node->objectMaterializationData().m_properties.size(); ++i) { >+ unsigned identifierNumber = >+ node->objectMaterializationData().m_properties[i].m_identifierNumber; >+ m_insertionSet.insert( >+ nodeIndex + 1, >+ PromotedHeapLocation( >+ NamedPropertyPLoc, node, identifierNumber).createHint( >+ m_graph, node->origin, >+ m_graph.varArgChild(node, i + 1).node())); >+ } >+ node->convertToPhantomNewObject(); >+ } >+ break; >+ >+ case NewFunction: >+ if (m_eliminationCandidates.contains(node)) { >+ Node* executable = m_insertionSet.insertConstant( >+ nodeIndex + 1, node->origin, node->cellOperand()); >+ m_insertionSet.insert( >+ nodeIndex + 1, >+ PromotedHeapLocation(FunctionExecutablePLoc, node).createHint( >+ m_graph, node->origin, executable)); >+ m_insertionSet.insert( >+ nodeIndex + 1, >+ PromotedHeapLocation(FunctionActivationPLoc, node).createHint( >+ m_graph, node->origin, node->child1().node())); >+ node->convertToPhantomNewFunction(); >+ } >+ break; >+ >+ case CreateActivation: >+ if (m_eliminationCandidates.contains(node)) { >+ Node* undefined = m_insertionSet.insertConstant( >+ nodeIndex + 1, node->origin, jsUndefined()); >+ Node* symbolTableNode = m_insertionSet.insertConstant( >+ nodeIndex + 1, node->origin, node->cellOperand()); >+ m_insertionSet.insert( >+ nodeIndex + 1, >+ PromotedHeapLocation(ActivationSymbolTablePLoc, node).createHint( >+ m_graph, node->origin, symbolTableNode)); >+ m_insertionSet.insert( >+ nodeIndex + 1, >+ PromotedHeapLocation(ActivationScopePLoc, node).createHint( >+ m_graph, node->origin, node->child1().node())); >+ { >+ SymbolTable* symbolTable = node->castOperand<SymbolTable*>(); >+ ConcurrentJITLocker locker(symbolTable->m_lock); >+ for (auto iter = symbolTable->begin(locker), end = symbolTable->end(locker); iter != end; ++iter) { >+ m_insertionSet.insert( >+ nodeIndex + 1, >+ PromotedHeapLocation( >+ ClosureVarPLoc, node, iter->value.scopeOffset().offset()).createHint( >+ m_graph, node->origin, undefined)); >+ } >+ } >+ node->convertToPhantomCreateActivation(); >+ } >+ break; >+ >+ case MaterializeCreateActivation: >+ if (m_eliminationCandidates.contains(node)) { >+ Node* symbolTableNode = m_insertionSet.insertConstant( >+ nodeIndex + 1, node->origin, node->cellOperand()); >+ m_insertionSet.insert( >+ nodeIndex + 1, >+ PromotedHeapLocation(ActivationSymbolTablePLoc, node).createHint( >+ m_graph, node->origin, symbolTableNode)); >+ m_insertionSet.insert( >+ nodeIndex + 1, >+ PromotedHeapLocation(ActivationScopePLoc, node).createHint( >+ m_graph, node->origin, node->child1().node())); >+ for (unsigned i = 0; i < node->objectMaterializationData().m_properties.size(); ++i) { >+ unsigned identifierNumber = >+ node->objectMaterializationData().m_properties[i].m_identifierNumber; >+ m_insertionSet.insert( >+ nodeIndex + 1, >+ PromotedHeapLocation( >+ ClosureVarPLoc, node, identifierNumber).createHint( >+ m_graph, node->origin, m_graph.varArgChild(node, i + 1).node())); >+ } >+ node->convertToPhantomCreateActivation(); >+ } >+ break; >+ >+ case PutStructure: >+ if (m_eliminationCandidates.contains(node->child1().node())) { >+ ASSERT(node->child1()->isPhantomObjectAllocation()); >+ Node* structure = m_insertionSet.insertConstant( >+ nodeIndex, node->origin, JSValue(node->transition()->next)); >+ node->convertToPutStructureHint(structure); >+ } >+ break; >+ >+ case PutByOffset: >+ if (m_eliminationCandidates.contains(node->child2().node())) { >+ ASSERT(node->child2()->isPhantomObjectAllocation()); >+ node->convertToPutByOffsetHint(); >+ } >+ break; >+ >+ case PutClosureVar: >+ if (m_eliminationCandidates.contains(node->child1().node())) { >+ ASSERT(node->child1()->isPhantomActivationAllocation()); >+ node->convertToPutClosureVarHint(); >+ } >+ break; >+ >+ case StoreBarrier: { >+ DFG_CRASH(m_graph, node, "Unexpected store barrier during sinking."); >+ break; >+ } >+ >+ default: >+ break; >+ } >+ } >+ m_insertionSet.execute(block); >+ } >+ } >+ >+ void promoteSunkenFields() >+ { >+ // Collect the set of heap locations that we will be operating over. >+ HashSet<PromotedHeapLocation> locations; >+ for (BasicBlock* block : m_graph.blocksInNaturalOrder()) { >+ for (Node* node : *block) { >+ promoteHeapAccess( >+ node, >+ [&] (PromotedHeapLocation location, Edge) { >+ if (m_eliminationCandidates.contains(location.base())) >+ locations.add(location); >+ }, >+ [&] (PromotedHeapLocation location) { >+ if (m_eliminationCandidates.contains(location.base())) >+ locations.add(location); >+ }); >+ } >+ } >+ >+ // Figure out which locations belong to which allocations. >+ m_locationsForAllocation.clear(); >+ for (PromotedHeapLocation location : locations) { >+ auto result = m_locationsForAllocation.add(location.base(), Vector<PromotedHeapLocation>()); >+ ASSERT(!result.iterator->value.contains(location)); >+ result.iterator->value.append(location); >+ } >+ >+ // When the allocation is created we need to create Bottom values for >+ // all of its fields because some fields may be added >+ // conditionnaly. >+ Node* bottom = nullptr; >+ for (BasicBlock* block : m_graph.blocksInNaturalOrder()) { >+ if (block == m_graph.block(0)) >+ bottom = m_insertionSet.insertConstant(0, NodeOrigin(), jsNumber(1927)); >+ for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) { >+ Node* node = block->at(nodeIndex); >+ for (PromotedHeapLocation location : m_locationsForAllocation.get(node)) { >+ m_insertionSet.insert( >+ nodeIndex + 1, location.createHint(m_graph, node->origin, bottom)); >+ } >+ } >+ m_insertionSet.execute(block); >+ } >+ >+ m_ssaCalculator.reset(); >+ >+ // Collect the set of "variables" that we will be sinking. >+ m_locationToVariable.clear(); >+ m_indexToLocation.clear(); >+ for (PromotedHeapLocation location : locations) { >+ SSACalculator::Variable* variable = m_ssaCalculator.newVariable(); >+ m_locationToVariable.add(location, variable); >+ ASSERT(m_indexToLocation.size() == variable->index()); >+ m_indexToLocation.append(location); >+ } >+ >+ // Create Defs from the existing hints. >+ for (BasicBlock* block : m_graph.blocksInNaturalOrder()) { >+ for (Node* node : *block) { >+ promoteHeapAccess( >+ node, >+ [&] (PromotedHeapLocation location, Edge value) { >+ if (!m_eliminationCandidates.contains(location.base())) >+ return; >+ >+ if (verbose) >+ dataLog("Defining ", location, " = ", value.node(), " at ", node, "\n"); >+ >+ SSACalculator::Variable* variable = m_locationToVariable.get(location); >+ m_ssaCalculator.newDef(variable, block, value.node()); >+ }, >+ [&] (PromotedHeapLocation) { }); >+ } >+ } >+ >+ // OMG run the SSA calculator to create Phis! >+ m_ssaCalculator.computePhis( >+ [&] (SSACalculator::Variable* variable, BasicBlock* block) -> Node* { >+ PromotedHeapLocation location = m_indexToLocation[variable->index()]; >+ if (!m_graph.m_dominators.strictlyDominates(location.base()->owner, block)) >+ return nullptr; >+ >+ Node* phiNode = m_graph.addNode(SpecHeapTop, Phi, NodeOrigin()); >+ phiNode->mergeFlags(NodeResultJS); >+ return phiNode; >+ }); >+ >+ // Place Phis in the right places, replace all uses of any load with the appropriate >+ // value, and create the appropriate Upsilon nodes. >+ m_graph.clearReplacements(); >+ for (BasicBlock* block : m_graph.blocksInPreOrder()) { >+ // This mapping table is intended to be lazy. If something is omitted from the table, >+ // it means that there haven't been any local stores to that promoted heap location. >+ m_localMapping.clear(); >+ >+ // Insert the Phi functions that we had previously created. >+ for (SSACalculator::Def* phiDef : m_ssaCalculator.phisForBlock(block)) { >+ PromotedHeapLocation location = m_indexToLocation[phiDef->variable()->index()]; >+ >+ m_insertionSet.insert(0, phiDef->value()); >+ m_insertionSet.insert( >+ 0, location.createHint(m_graph, NodeOrigin(), phiDef->value())); >+ m_localMapping.add(location, phiDef->value()); >+ } >+ >+ if (verbose) >+ dataLog("Local mapping at ", pointerDump(block), ": ", mapDump(m_localMapping), "\n"); >+ >+ // Process the block and replace all uses of loads with the promoted value. >+ for (Node* node : *block) { >+ m_graph.performSubstitution(node); >+ >+ promoteHeapAccess( >+ node, >+ [&] (PromotedHeapLocation location, Edge value) { >+ if (m_eliminationCandidates.contains(location.base())) >+ m_localMapping.set(location, value.node()); >+ }, >+ [&] (PromotedHeapLocation location) { >+ if (m_eliminationCandidates.contains(location.base())) { >+ switch (node->op()) { >+ case CheckStructure: >+ node->convertToCheckStructureImmediate(resolve(block, location)); >+ break; >+ >+ default: >+ node->replaceWith(resolve(block, location)); >+ break; >+ } >+ } >+ }); >+ } >+ >+ // Gotta drop some Upsilons. >+ NodeAndIndex terminal = block->findTerminal(); >+ size_t upsilonInsertionPoint = terminal.index; >+ NodeOrigin upsilonOrigin = terminal.node->origin; >+ for (BasicBlock* successorBlock : block->successors()) { >+ for (SSACalculator::Def* phiDef : m_ssaCalculator.phisForBlock(successorBlock)) { >+ Node* phiNode = phiDef->value(); >+ SSACalculator::Variable* variable = phiDef->variable(); >+ PromotedHeapLocation location = m_indexToLocation[variable->index()]; >+ Node* incoming = resolve(block, location); >+ >+ m_insertionSet.insertNode( >+ upsilonInsertionPoint, SpecNone, Upsilon, upsilonOrigin, >+ OpInfo(phiNode), incoming->defaultEdge()); >+ } >+ } >+ >+ m_insertionSet.execute(block); >+ } >+ } >+ >+ Node* resolve(BasicBlock* block, PromotedHeapLocation location) >+ { >+ if (Node* result = m_localMapping.get(location)) >+ return result; >+ >+ // This implies that there is no local mapping. Find a non-local mapping. >+ SSACalculator::Def* def = m_ssaCalculator.nonLocalReachingDef( >+ block, m_locationToVariable.get(location)); >+ DFG_ASSERT(m_graph, block, def); >+ ASSERT(def->value()); >+ m_localMapping.add(location, def->value()); >+ return def->value(); >+ } >+ >+ SSACalculator m_ssaCalculator; >+ HashSet<Node*> m_eliminationCandidates; >+ HashMap<Node*, Vector<PromotedHeapLocation>> m_locationsForAllocation; >+ HashMap<PromotedHeapLocation, SSACalculator::Variable*> m_locationToVariable; >+ Vector<PromotedHeapLocation> m_indexToLocation; >+ HashMap<PromotedHeapLocation, Node*> m_localMapping; >+ InsertionSet m_insertionSet; >+ PhiChildren m_phiChildren; >+ >+ BlockMap<AbstractLocalHeap> m_heapAtHead; >+ BlockMap<AbstractLocalHeap> m_heapAtTail; >+ AbstractLocalHeap m_heap; >+}; >+ >+bool performObjectAllocationElimination(Graph& graph) >+{ >+ SamplingRegion samplingRegion("DFG Object Allocation Elimination Phase"); >+ return runPhase<ObjectAllocationEliminationPhase>(graph); >+} >+ >+} } // namespace JSC::DFG >+ >+#endif // ENABLE(DFG_JIT) >diff --git a/Source/JavaScriptCore/dfg/DFGObjectAllocationEliminationPhase.h b/Source/JavaScriptCore/dfg/DFGObjectAllocationEliminationPhase.h >new file mode 100644 >index 0000000000000000000000000000000000000000..3aead2de9dbe7ad16bd078ee4b76342780957f89 >--- /dev/null >+++ b/Source/JavaScriptCore/dfg/DFGObjectAllocationEliminationPhase.h >@@ -0,0 +1,47 @@ >+/* >+ * Copyright (C) 2015 Apple Inc. All rights reserved. >+ * >+ * 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. >+ */ >+ >+#ifndef DFGObjectAllocationEliminationPhase_h >+#define DFGObjectAllocationEliminationPhase_h >+ >+#if ENABLE(DFG_JIT) >+ >+namespace JSC { namespace DFG { >+ >+class Graph; >+ >+// Eliminates object allocations that are never used except locally. This will >+// insert phantom allocations and store hints so that OSR exit can materialize >+// the objects. Replaces all uses of the objects' fields with SSA data flow. >+// This is able to eliminate cyclic object graphs. >+ >+bool performObjectAllocationElimination(Graph&); >+ >+} } // namespace JSC::DFG >+ >+#endif // ENABLE(DFG_JIT) >+ >+#endif // DFGObjectAllocationEliminationPhase_h >+ >diff --git a/Source/JavaScriptCore/dfg/DFGPlan.cpp b/Source/JavaScriptCore/dfg/DFGPlan.cpp >index a2e27d58f3433d61d8fffc8d49d869391a9e5cdc..0d21c5f2107ff56ae8a5ff2830d6e2dc466784dd 100644 >--- a/Source/JavaScriptCore/dfg/DFGPlan.cpp >+++ b/Source/JavaScriptCore/dfg/DFGPlan.cpp >@@ -52,6 +52,7 @@ > #include "DFGMovHintRemovalPhase.h" > #include "DFGOSRAvailabilityAnalysisPhase.h" > #include "DFGOSREntrypointCreationPhase.h" >+#include "DFGObjectAllocationEliminationPhase.h" > #include "DFGObjectAllocationSinkingPhase.h" > #include "DFGPhantomInsertionPhase.h" > #include "DFGPredictionInjectionPhase.h" >@@ -365,6 +366,7 @@ Plan::CompilationPath Plan::compileInThreadImpl(LongLivedState& longLivedState) > if (Options::enableObjectAllocationSinking()) { > changed |= performCriticalEdgeBreaking(dfg); > changed |= performObjectAllocationSinking(dfg); >+ changed |= performObjectAllocationElimination(dfg); > } > if (changed) { > // State-at-tail and state-at-head will be invalid if we did strength reduction since >diff --git a/Source/JavaScriptCore/dfg/DFGPromotedHeapLocation.h b/Source/JavaScriptCore/dfg/DFGPromotedHeapLocation.h >index bc151584807437db072941419e8b4b9ba3e562cb..7e5069d662d734afeb954033f4b1895d4f6b0244 100644 >--- a/Source/JavaScriptCore/dfg/DFGPromotedHeapLocation.h >+++ b/Source/JavaScriptCore/dfg/DFGPromotedHeapLocation.h >@@ -57,6 +57,12 @@ public: > , m_info(info) > { > } >+ >+ PromotedLocationDescriptor(WTF::HashTableDeletedValueType) >+ : m_kind(InvalidPromotedLocationKind) >+ , m_info(1) >+ { >+ } > > bool operator!() const { return m_kind == InvalidPromotedLocationKind; } > >@@ -94,6 +100,12 @@ private: > unsigned m_info; > }; > >+struct PromotedLocationDescriptorHash { >+ static unsigned hash(const PromotedLocationDescriptor& key) { return key.hash(); } >+ static bool equal(const PromotedLocationDescriptor& a, const PromotedLocationDescriptor& b) { return a == b; } >+ static const bool safeToCompareToEmptyOrDeleted = true; >+}; >+ > class PromotedHeapLocation { > public: > PromotedHeapLocation( >@@ -141,6 +153,11 @@ public: > return m_base == other.m_base > && m_meta == other.m_meta; > } >+ >+ bool operator!=(const PromotedHeapLocation& other) const >+ { >+ return !(*this == other); >+ } > > bool isHashTableDeletedValue() const > { >@@ -176,6 +193,16 @@ template<> struct HashTraits<JSC::DFG::PromotedHeapLocation> : SimpleClassHashTr > static const bool emptyValueIsZero = false; > }; > >+template<typename T> struct DefaultHash; >+template<> struct DefaultHash<JSC::DFG::PromotedLocationDescriptor> { >+ typedef JSC::DFG::PromotedLocationDescriptorHash Hash; >+}; >+ >+template<typename T> struct HashTraits; >+template<> struct HashTraits<JSC::DFG::PromotedLocationDescriptor> : SimpleClassHashTraits<JSC::DFG::PromotedLocationDescriptor> { >+ static const bool emptyValueIsZero = false; >+}; >+ > } // namespace WTF > > #endif // ENABLE(DFG_JIT) >diff --git a/Source/JavaScriptCore/dfg/DFGValidate.cpp b/Source/JavaScriptCore/dfg/DFGValidate.cpp >index 2ba333849399e43b6c41f5dffbe909335151f552..a969c13c9616b8d46f8a5a1266c3eba98880061e 100644 >--- a/Source/JavaScriptCore/dfg/DFGValidate.cpp >+++ b/Source/JavaScriptCore/dfg/DFGValidate.cpp >@@ -524,12 +524,42 @@ private: > case Phantom: > VALIDATE((node), !"bad node type for SSA"); > break; >- >+ > default: > // FIXME: Add more things here. > // https://bugs.webkit.org/show_bug.cgi?id=123471 > break; > } >+ switch (node->op()) { >+ case PhantomNewObject: >+ case PhantomNewFunction: >+ case PhantomCreateActivation: >+ case PhantomDirectArguments: >+ case PhantomClonedArguments: >+ case MovHint: >+ case Upsilon: >+ case ForwardVarargs: >+ case CallForwardVarargs: >+ case ConstructForwardVarargs: >+ case GetMyArgumentByVal: >+ break; >+ >+ case Check: >+ // FIXME: This is probably not correct. >+ break; >+ >+ case PutHint: >+ VALIDATE((node), node->child1()->isPhantomAllocation()); >+ break; >+ >+ default: >+ m_graph.doToChildren( >+ node, >+ [&] (const Edge& edge) { >+ VALIDATE((node), !edge->isPhantomAllocation()); >+ }); >+ break; >+ } > } > } > } >diff --git a/Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp b/Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp >index 27788e7f6a5a9fb9e53069ec54c0900decd9ec7e..16f195f477a9676b16457e7a9b8fae65f8a65d30 100644 >--- a/Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp >+++ b/Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp >@@ -47,6 +47,18 @@ namespace JSC { namespace FTL { > > using namespace DFG; > >+static bool neededForMaterialization(PromotedLocationDescriptor location) >+{ >+ switch (location.kind()) { >+ case NamedPropertyPLoc: >+ case ClosureVarPLoc: >+ return false; >+ >+ default: >+ return true; >+ } >+} >+ > static void compileRecovery( > CCallHelpers& jit, const ExitValue& value, StackMaps::Record* record, StackMaps& stackmaps, > char* registerScratch, >@@ -220,24 +232,24 @@ static void compileStub( > jit.or32(GPRInfo::regT2, MacroAssembler::AbsoluteAddress(arrayProfile->addressOfArrayModes())); > } > } >- >+ > if (!!exit.m_valueProfile) > jit.store64(GPRInfo::regT0, exit.m_valueProfile.getSpecFailBucket(0)); > } >- >- // Materialize all objects. Don't materialize an object until all of the objects it needs >- // have been materialized. Curiously, this is the only place that we have an algorithm that prevents >- // OSR exit from handling cyclic object materializations. Of course, object allocation sinking >- // currently wouldn't recognize a cycle as being sinkable - but if it did then the only thing that >- // would ahve to change is this fixpoint. Instead we would allocate the objects first and populate >- // them with data later. >+ >+ // Materialize all objects. Don't materialize an object until all >+ // of the objects it needs have been materialized. We break cycles >+ // by populating objects late - we only consider an object as >+ // needing another object if the later is needed for the >+ // allocation of the former. >+ > HashSet<ExitTimeObjectMaterialization*> toMaterialize; > for (ExitTimeObjectMaterialization* materialization : exit.m_materializations) > toMaterialize.add(materialization); >- >+ > while (!toMaterialize.isEmpty()) { > unsigned previousToMaterializeSize = toMaterialize.size(); >- >+ > Vector<ExitTimeObjectMaterialization*> worklist; > worklist.appendRange(toMaterialize.begin(), toMaterialize.end()); > for (ExitTimeObjectMaterialization* materialization : worklist) { >@@ -246,20 +258,28 @@ static void compileStub( > for (ExitPropertyValue value : materialization->properties()) { > if (!value.value().isObjectMaterialization()) > continue; >+ if (!neededForMaterialization(value.location())) >+ continue; > if (toMaterialize.contains(value.value().objectMaterialization())) { >- // Gotta skip this one, since one of its fields points to a materialization >- // that hasn't been materialized. >+ // Gotta skip this one, since it needs a >+ // materialization that hasn't been materialized. > allGood = false; > break; > } > } > if (!allGood) > continue; >- >- // All systems go for materializing the object. First we recover the values of all of >- // its fields and then we call a function to actually allocate the beast. >+ >+ // All systems go for materializing the object. First we >+ // recover the values of all of its fields and then we >+ // call a function to actually allocate the beast. >+ // We only recover the fields that are needed for the allocation. > for (unsigned propertyIndex = materialization->properties().size(); propertyIndex--;) { >- const ExitValue& value = materialization->properties()[propertyIndex].value(); >+ const ExitPropertyValue& property = materialization->properties()[propertyIndex]; >+ const ExitValue& value = property.value(); >+ if (!neededForMaterialization(property.location())) >+ continue; >+ > compileRecovery( > jit, value, record, jitCode->stackmaps, registerScratch, > materializationToPointer); >@@ -273,7 +293,7 @@ static void compileStub( > jit.move(CCallHelpers::TrustedImmPtr(bitwise_cast<void*>(operationMaterializeObjectInOSR)), GPRInfo::nonArgGPR0); > jit.call(GPRInfo::nonArgGPR0); > jit.storePtr(GPRInfo::returnValueGPR, materializationToPointer.get(materialization)); >- >+ > // Let everyone know that we're done. > toMaterialize.remove(materialization); > } >@@ -284,6 +304,27 @@ static void compileStub( > RELEASE_ASSERT(toMaterialize.size() < previousToMaterializeSize); > } > >+ // Now that all the objects have been allocated, we populate them >+ // with the correct values. This time we can recover all the >+ // fields, including those that are only needed for the allocation. >+ for (ExitTimeObjectMaterialization* materialization : exit.m_materializations) { >+ for (unsigned propertyIndex = materialization->properties().size(); propertyIndex--;) { >+ const ExitValue& value = materialization->properties()[propertyIndex].value(); >+ compileRecovery( >+ jit, value, record, jitCode->stackmaps, registerScratch, >+ materializationToPointer); >+ jit.storePtr(GPRInfo::regT0, materializationArguments + propertyIndex); >+ } >+ >+ // This call assumes that we don't pass arguments on the stack >+ jit.setupArgumentsWithExecState( >+ CCallHelpers::TrustedImmPtr(materialization), >+ CCallHelpers::TrustedImmPtr(materializationToPointer.get(materialization)), >+ CCallHelpers::TrustedImmPtr(materializationArguments)); >+ jit.move(CCallHelpers::TrustedImmPtr(bitwise_cast<void*>(operationPopulateObjectInOSR)), GPRInfo::nonArgGPR0); >+ jit.call(GPRInfo::nonArgGPR0); >+ } >+ > // Save all state from wherever the exit data tells us it was, into the appropriate place in > // the scratch buffer. This also does the reboxing. > >diff --git a/Source/JavaScriptCore/ftl/FTLOperations.cpp b/Source/JavaScriptCore/ftl/FTLOperations.cpp >index 4e5c8b3fb793a83b2367fb00a7e7c1b74a4849e0..4c38373886a35eff8d2f924cf42b71d311f5379c 100644 >--- a/Source/JavaScriptCore/ftl/FTLOperations.cpp >+++ b/Source/JavaScriptCore/ftl/FTLOperations.cpp >@@ -48,49 +48,110 @@ extern "C" JSCell* JIT_OPERATION operationNewObjectWithButterfly(ExecState* exec > return JSFinalObject::create(exec, structure, butterfly); > } > >+extern "C" void JIT_OPERATION operationPopulateObjectInOSR( >+ ExecState* exec, ExitTimeObjectMaterialization* materialization, >+ EncodedJSValue* encodedValue, EncodedJSValue* values) >+{ >+ VM& vm = exec->vm(); >+ CodeBlock* codeBlock = exec->codeBlock(); >+ >+ // We cannot GC. We've got pointers in evil places. >+ // FIXME: We are not doing anything that can GC here, and this is >+ // probably unnecessary. >+ DeferGCForAWhile deferGC(vm.heap); >+ >+ switch (materialization->type()) { >+ case PhantomNewObject: { >+ JSFinalObject* object = jsCast<JSFinalObject*>(JSValue::decode(*encodedValue)); >+ Structure* structure = object->structure(); >+ >+ // Figure out what the heck to populate the object with. Use >+ // getPropertiesConcurrently() because that happens to be >+ // lower-level and more convenient. It doesn't change the >+ // materialization of the property table. We want to have >+ // minimal visible effects on the system. Also, don't mind >+ // that this is O(n^2). It doesn't matter. We only get here >+ // from OSR exit. >+ for (PropertyMapEntry entry : structure->getPropertiesConcurrently()) { >+ for (unsigned i = materialization->properties().size(); i--;) { >+ const ExitPropertyValue& property = materialization->properties()[i]; >+ if (property.location().kind() != NamedPropertyPLoc) >+ continue; >+ if (codeBlock->identifier(property.location().info()).impl() != entry.key) >+ continue; >+ >+ object->putDirect(vm, entry.offset, JSValue::decode(values[i])); >+ } >+ } >+ break; >+ } >+ >+ case PhantomNewFunction: >+ case PhantomDirectArguments: >+ case PhantomClonedArguments: >+ // Those are completely handled by operationMaterializeObjectInOSR >+ break; >+ >+ case PhantomCreateActivation: { >+ JSLexicalEnvironment* activation = jsCast<JSLexicalEnvironment*>(JSValue::decode(*encodedValue)); >+ >+ // Figure out what to populate the activation with >+ for (unsigned i = materialization->properties().size(); i--;) { >+ const ExitPropertyValue& property = materialization->properties()[i]; >+ if (property.location().kind() != ClosureVarPLoc) >+ continue; >+ >+ activation->variableAt(ScopeOffset(property.location().info())).set(exec->vm(), activation, JSValue::decode(values[i])); >+ } >+ >+ break; >+ } >+ >+ >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ break; >+ >+ } >+} >+ > extern "C" JSCell* JIT_OPERATION operationMaterializeObjectInOSR( > ExecState* exec, ExitTimeObjectMaterialization* materialization, EncodedJSValue* values) > { > VM& vm = exec->vm(); >- CodeBlock* codeBlock = exec->codeBlock(); > > // We cannot GC. We've got pointers in evil places. > DeferGCForAWhile deferGC(vm.heap); > > switch (materialization->type()) { > case PhantomNewObject: { >- // First figure out what the structure is. >+ // Figure out what the structure is > Structure* structure = nullptr; > for (unsigned i = materialization->properties().size(); i--;) { > const ExitPropertyValue& property = materialization->properties()[i]; > if (property.location() != PromotedLocationDescriptor(StructurePLoc)) > continue; >- >+ > structure = jsCast<Structure*>(JSValue::decode(values[i])); > break; > } > RELEASE_ASSERT(structure); >- >- // Let's create that object! >+ > JSFinalObject* result = JSFinalObject::create(vm, structure); >- >- // Now figure out what the heck to populate the object with. Use getPropertiesConcurrently() >- // because that happens to be lower-level and more convenient. It doesn't change the >- // materialization of the property table. We want to have minimal visible effects on the >- // system. Also, don't mind that this is O(n^2). It doesn't matter. We only get here from OSR >- // exit. >- for (PropertyMapEntry entry : structure->getPropertiesConcurrently()) { >- for (unsigned i = materialization->properties().size(); i--;) { >- const ExitPropertyValue& property = materialization->properties()[i]; >- if (property.location().kind() != NamedPropertyPLoc) >- continue; >- if (codeBlock->identifier(property.location().info()).impl() != entry.key) >- continue; >- >- result->putDirect(vm, entry.offset, JSValue::decode(values[i])); >- } >- } >- >+ >+ // The real values will be put subsequently by >+ // operationPopulateNewObjectInOSR. We can't fill them in >+ // now, because they may not be available yet (typically >+ // because we have a cyclic dependency graph). >+ >+ // We put a dummy value here in order to avoid super-subtle >+ // GC-and-OSR-exit crashes in case we have a bug and some >+ // field is, for any reason, not filled later. >+ // We use a random-ish number instead of a sensible value like >+ // undefined to make possible bugs easier to track. >+ for (PropertyMapEntry entry : structure->getPropertiesConcurrently()) >+ result->putDirect(vm, entry.offset, jsNumber(19723)); >+ > return result; > } > >@@ -113,7 +174,7 @@ extern "C" JSCell* JIT_OPERATION operationMaterializeObjectInOSR( > } > > case PhantomCreateActivation: { >- // Figure out where the scope is >+ // Figure out what the scope and symbol table are > JSScope* scope = nullptr; > SymbolTable* table = nullptr; > for (unsigned i = materialization->properties().size(); i--;) { >@@ -133,13 +194,17 @@ extern "C" JSCell* JIT_OPERATION operationMaterializeObjectInOSR( > JSLexicalEnvironment* result = JSLexicalEnvironment::create(vm, structure, scope, table); > > RELEASE_ASSERT(materialization->properties().size() - 2 == table->scopeSize()); >- // Figure out what to populate the activation with >+ >+ // The real values will be put subsequently by >+ // operationPopulateNewObjectInOSR. See the PhantomNewObject >+ // case for details. > for (unsigned i = materialization->properties().size(); i--;) { > const ExitPropertyValue& property = materialization->properties()[i]; > if (property.location().kind() != ClosureVarPLoc) > continue; > >- result->variableAt(ScopeOffset(property.location().info())).set(exec->vm(), result, JSValue::decode(values[i])); >+ result->variableAt(ScopeOffset(property.location().info())).set( >+ exec->vm(), result, jsNumber(29834)); > } > > if (validationEnabled()) { >diff --git a/Source/JavaScriptCore/ftl/FTLOperations.h b/Source/JavaScriptCore/ftl/FTLOperations.h >index d15f18eb9add3379fb5cf142de34613c48577505..a5568f91b1172b5129f8839783e73bc6d8c83f7c 100644 >--- a/Source/JavaScriptCore/ftl/FTLOperations.h >+++ b/Source/JavaScriptCore/ftl/FTLOperations.h >@@ -40,6 +40,9 @@ JSCell* JIT_OPERATION operationNewObjectWithButterfly(ExecState*, Structure*) WT > JSCell* JIT_OPERATION operationMaterializeObjectInOSR( > ExecState*, ExitTimeObjectMaterialization*, EncodedJSValue*) WTF_INTERNAL; > >+void JIT_OPERATION operationPopulateObjectInOSR( >+ ExecState*, ExitTimeObjectMaterialization*, EncodedJSValue*, EncodedJSValue*) WTF_INTERNAL; >+ > } // extern "C" > > } } // namespace JSC::DFG >diff --git a/Source/WTF/wtf/HashSet.h b/Source/WTF/wtf/HashSet.h >index 6b0c9271a5753021d4c4ff47a631e70d6983c116..8c2f5eb9798efb4d4ecf10a19868aa7586093acd 100644 >--- a/Source/WTF/wtf/HashSet.h >+++ b/Source/WTF/wtf/HashSet.h >@@ -117,6 +117,8 @@ namespace WTF { > > bool operator==(const HashSet&) const; > >+ bool operator!=(const HashSet&) const; >+ > private: > HashTableType m_impl; > }; >@@ -348,6 +350,12 @@ namespace WTF { > return true; > } > >+ template<typename T, typename U, typename V> >+ inline bool HashSet<T, U, V>::operator!=(const HashSet& other) const >+ { >+ return !(*this == other); >+ } >+ > } // namespace WTF > > using WTF::HashSet; >diff --git a/Source/WTF/wtf/TinyPtrSet.h b/Source/WTF/wtf/TinyPtrSet.h >index 93b198cf8986d3fe668702777eb15038849a5e6b..776af823288e8ec86c8e7024736dfb76a55ab91f 100644 >--- a/Source/WTF/wtf/TinyPtrSet.h >+++ b/Source/WTF/wtf/TinyPtrSet.h >@@ -366,6 +366,11 @@ public: > return false; > return isSubsetOf(other); > } >+ >+ bool operator!=(const TinyPtrSet& other) const >+ { >+ return !(*this == other); >+ } > > private: > friend class JSC::DFG::StructureAbstractValue;
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 143073
:
252332
|
252425
|
252500
|
254886
|
255679
|
255681
|
255756
|
255867
|
255926
|
256018
|
256041
|
256721