The liveness pruning done by ObjectAllocationSinkingPhase ignores the possibility...
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 13 May 2015 22:14:25 +0000 (22:14 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 13 May 2015 22:14:25 +0000 (22:14 +0000)
https://bugs.webkit.org/show_bug.cgi?id=144945

Reviewed by Michael Saboff.

We were making the mistake of using DFG liveness for object allocation sinking decisions.
This is wrong. In fact we almost never want to use DFG liveness directly. The only place
where that makes sense is pruning in DFG AI.

So, I created a CombinedLiveness class that combines the DFG liveness with bytecode
liveness.

In the process of doing this, I realized that the DFGForAllKills definition of combined
liveness at block tail was not strictly right; it was using the bytecode liveness at the
block terminal instead of the union of the bytecode live-at-heads of successor blocks. So,
I changed DFGForAllKills to work in terms of CombinedLiveness.

This allows me to unskip the test I added in r184260. I also added a new test that tries to
trigger this bug more directly.

* CMakeLists.txt:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* dfg/DFGArgumentsEliminationPhase.cpp:
* dfg/DFGCombinedLiveness.cpp: Added.
(JSC::DFG::liveNodesAtHead):
(JSC::DFG::CombinedLiveness::CombinedLiveness):
* dfg/DFGCombinedLiveness.h: Added.
(JSC::DFG::CombinedLiveness::CombinedLiveness):
* dfg/DFGForAllKills.h:
(JSC::DFG::forAllKillsInBlock):
(JSC::DFG::forAllLiveNodesAtTail): Deleted.
* dfg/DFGObjectAllocationSinkingPhase.cpp:
(JSC::DFG::ObjectAllocationSinkingPhase::performSinking):
(JSC::DFG::ObjectAllocationSinkingPhase::determineMaterializationPoints):
(JSC::DFG::ObjectAllocationSinkingPhase::placeMaterializationPoints):
(JSC::DFG::ObjectAllocationSinkingPhase::promoteSunkenFields):
* tests/stress/escape-object-in-diamond-then-exit.js: Added.
* tests/stress/sink-object-past-invalid-check-sneaky.js:

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

Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/dfg/DFGArgumentsEliminationPhase.cpp
Source/JavaScriptCore/dfg/DFGCombinedLiveness.cpp [new file with mode: 0644]
Source/JavaScriptCore/dfg/DFGCombinedLiveness.h [new file with mode: 0644]
Source/JavaScriptCore/dfg/DFGForAllKills.h
Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp
Source/JavaScriptCore/tests/stress/escape-object-in-diamond-then-exit.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/sink-object-past-invalid-check-sneaky.js

index 0284ba6..e532154 100644 (file)
@@ -145,6 +145,7 @@ set(JavaScriptCore_SOURCES
     dfg/DFGCleanUpPhase.cpp
     dfg/DFGClobberSet.cpp
     dfg/DFGClobberize.cpp
+    dfg/DFGCombinedLiveness.cpp
     dfg/DFGCommon.cpp
     dfg/DFGCommonData.cpp
     dfg/DFGCompilationKey.cpp
index 906cc83..a9cc411 100644 (file)
@@ -1,3 +1,45 @@
+2015-05-13  Filip Pizlo  <fpizlo@apple.com>
+
+        The liveness pruning done by ObjectAllocationSinkingPhase ignores the possibility of an object's bytecode liveness being longer than its DFG liveness
+        https://bugs.webkit.org/show_bug.cgi?id=144945
+
+        Reviewed by Michael Saboff.
+        
+        We were making the mistake of using DFG liveness for object allocation sinking decisions.
+        This is wrong. In fact we almost never want to use DFG liveness directly. The only place
+        where that makes sense is pruning in DFG AI.
+        
+        So, I created a CombinedLiveness class that combines the DFG liveness with bytecode
+        liveness.
+        
+        In the process of doing this, I realized that the DFGForAllKills definition of combined
+        liveness at block tail was not strictly right; it was using the bytecode liveness at the
+        block terminal instead of the union of the bytecode live-at-heads of successor blocks. So,
+        I changed DFGForAllKills to work in terms of CombinedLiveness.
+        
+        This allows me to unskip the test I added in r184260. I also added a new test that tries to
+        trigger this bug more directly.
+
+        * CMakeLists.txt:
+        * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * dfg/DFGArgumentsEliminationPhase.cpp:
+        * dfg/DFGCombinedLiveness.cpp: Added.
+        (JSC::DFG::liveNodesAtHead):
+        (JSC::DFG::CombinedLiveness::CombinedLiveness):
+        * dfg/DFGCombinedLiveness.h: Added.
+        (JSC::DFG::CombinedLiveness::CombinedLiveness):
+        * dfg/DFGForAllKills.h:
+        (JSC::DFG::forAllKillsInBlock):
+        (JSC::DFG::forAllLiveNodesAtTail): Deleted.
+        * dfg/DFGObjectAllocationSinkingPhase.cpp:
+        (JSC::DFG::ObjectAllocationSinkingPhase::performSinking):
+        (JSC::DFG::ObjectAllocationSinkingPhase::determineMaterializationPoints):
+        (JSC::DFG::ObjectAllocationSinkingPhase::placeMaterializationPoints):
+        (JSC::DFG::ObjectAllocationSinkingPhase::promoteSunkenFields):
+        * tests/stress/escape-object-in-diamond-then-exit.js: Added.
+        * tests/stress/sink-object-past-invalid-check-sneaky.js:
+
 2015-05-13  Ryosuke Niwa  <rniwa@webkit.org>
 
         I skipped a wrong test in r184270. Fix that.
index da6bb19..76bd886 100644 (file)
     <ClCompile Include="..\dfg\DFGCleanUpPhase.cpp" />
     <ClCompile Include="..\dfg\DFGClobberize.cpp" />
     <ClCompile Include="..\dfg\DFGClobberSet.cpp" />
+    <ClCompile Include="..\dfg\DFGCombinedLiveness.cpp" />
     <ClCompile Include="..\dfg\DFGCommon.cpp" />
     <ClCompile Include="..\dfg\DFGCommonData.cpp" />
     <ClCompile Include="..\dfg\DFGCompilationKey.cpp" />
     <ClInclude Include="..\dfg\DFGCleanUpPhase.h" />
     <ClInclude Include="..\dfg\DFGClobberize.h" />
     <ClInclude Include="..\dfg\DFGClobberSet.h" />
+    <ClInclude Include="..\dfg\DFGCombinedLiveness.h" />
     <ClInclude Include="..\dfg\DFGCommon.h" />
     <ClInclude Include="..\dfg\DFGCommonData.h" />
     <ClInclude Include="..\dfg\DFGCompilationKey.h" />
index 8372164..3192e69 100644 (file)
@@ -82,6 +82,8 @@
                0F0332C418B01763005F979A /* GetByIdVariant.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F0332C218B01763005F979A /* GetByIdVariant.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F0332C618B53FA9005F979A /* FTLWeight.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F0332C518B53FA9005F979A /* FTLWeight.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F0332C818B546EC005F979A /* FTLWeightedTarget.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F0332C718B546EC005F979A /* FTLWeightedTarget.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               0F04396D1B03DC0B009598B7 /* DFGCombinedLiveness.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F04396B1B03DC0B009598B7 /* DFGCombinedLiveness.cpp */; };
+               0F04396E1B03DC0B009598B7 /* DFGCombinedLiveness.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F04396C1B03DC0B009598B7 /* DFGCombinedLiveness.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F05C3B41683CF9200BAF45B /* DFGArrayifySlowPathGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F05C3B21683CF8F00BAF45B /* DFGArrayifySlowPathGenerator.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F0776BF14FF002B00102332 /* JITCompilationEffort.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F0776BD14FF002800102332 /* JITCompilationEffort.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F0B839C14BCF46300885B4F /* LLIntThunks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F0B839714BCF45A00885B4F /* LLIntThunks.cpp */; };
                0F0332C218B01763005F979A /* GetByIdVariant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GetByIdVariant.h; sourceTree = "<group>"; };
                0F0332C518B53FA9005F979A /* FTLWeight.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLWeight.h; path = ftl/FTLWeight.h; sourceTree = "<group>"; };
                0F0332C718B546EC005F979A /* FTLWeightedTarget.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLWeightedTarget.h; path = ftl/FTLWeightedTarget.h; sourceTree = "<group>"; };
+               0F04396B1B03DC0B009598B7 /* DFGCombinedLiveness.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGCombinedLiveness.cpp; path = dfg/DFGCombinedLiveness.cpp; sourceTree = "<group>"; };
+               0F04396C1B03DC0B009598B7 /* DFGCombinedLiveness.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGCombinedLiveness.h; path = dfg/DFGCombinedLiveness.h; sourceTree = "<group>"; };
                0F05C3B21683CF8F00BAF45B /* DFGArrayifySlowPathGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGArrayifySlowPathGenerator.h; path = dfg/DFGArrayifySlowPathGenerator.h; sourceTree = "<group>"; };
                0F0776BD14FF002800102332 /* JITCompilationEffort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JITCompilationEffort.h; sourceTree = "<group>"; };
                0F0B839714BCF45A00885B4F /* LLIntThunks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LLIntThunks.cpp; path = llint/LLIntThunks.cpp; sourceTree = "<group>"; };
                                A77A423917A0BBFD00A8DB81 /* DFGClobberize.h */,
                                A77A423A17A0BBFD00A8DB81 /* DFGClobberSet.cpp */,
                                A77A423B17A0BBFD00A8DB81 /* DFGClobberSet.h */,
+                               0F04396B1B03DC0B009598B7 /* DFGCombinedLiveness.cpp */,
+                               0F04396C1B03DC0B009598B7 /* DFGCombinedLiveness.h */,
                                0FB4B51A16B62772003F696B /* DFGCommon.cpp */,
                                0FC0977E1469EBC400CF2442 /* DFGCommon.h */,
                                0FEA0A2D170D40BF00BB722C /* DFGCommonData.cpp */,
                                0FC3CD0019ADA410006AC72A /* DFGBlockWorklist.h in Headers */,
                                0FE050181AA9091100D33B33 /* DirectArguments.h in Headers */,
                                FE20CE9E15F04A9500DF3430 /* LLIntCLoop.h in Headers */,
+                               0F04396E1B03DC0B009598B7 /* DFGCombinedLiveness.h in Headers */,
                                0F4680CA14BBB16C00BFE272 /* LLIntCommon.h in Headers */,
                                0FBDB9AD1AB0FBC6000B57E5 /* DFGCallCreateDirectArgumentsSlowPathGenerator.h in Headers */,
                                0F4680D314BBD16700BFE272 /* LLIntData.h in Headers */,
                                70EC0EC61AA0D7DA00B6AAFA /* StringIteratorPrototype.cpp in Sources */,
                                A5FD0067189AFE9C00633231 /* ScriptArguments.cpp in Sources */,
                                A5FD006D189B00AA00633231 /* ScriptCallFrame.cpp in Sources */,
+                               0F04396D1B03DC0B009598B7 /* DFGCombinedLiveness.cpp in Sources */,
                                A5FD006F189B00AA00633231 /* ScriptCallStack.cpp in Sources */,
                                A5FD007D189B0B4C00633231 /* ScriptCallStackFactory.cpp in Sources */,
                                A503FA25188EFFFD00110F14 /* ScriptDebugServer.cpp in Sources */,
index 9400143..1b07c9d 100644 (file)
@@ -33,6 +33,7 @@
 #include "DFGBasicBlockInlines.h"
 #include "DFGBlockMapInlines.h"
 #include "DFGClobberize.h"
+#include "DFGCombinedLiveness.h"
 #include "DFGForAllKills.h"
 #include "DFGGraph.h"
 #include "DFGInsertionSet.h"
@@ -226,6 +227,7 @@ private:
         performLivenessAnalysis(m_graph);
         performOSRAvailabilityAnalysis(m_graph);
         m_graph.initializeNodeOwners();
+        CombinedLiveness combinedLiveness(m_graph);
         
         BlockMap<Operands<bool>> clobberedByBlock(m_graph);
         for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
@@ -264,7 +266,7 @@ private:
                 continue;
             
             forAllKillsInBlock(
-                m_graph, block,
+                m_graph, combinedLiveness, block,
                 [&] (unsigned nodeIndex, Node* candidate) {
                     if (!m_candidates.contains(candidate))
                         return;
diff --git a/Source/JavaScriptCore/dfg/DFGCombinedLiveness.cpp b/Source/JavaScriptCore/dfg/DFGCombinedLiveness.cpp
new file mode 100644 (file)
index 0000000..ccf9433
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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 "DFGCombinedLiveness.h"
+
+#if ENABLE(DFG_JIT)
+
+#include "DFGAvailabilityMap.h"
+#include "DFGBlockMapInlines.h"
+#include "FullBytecodeLiveness.h"
+#include "JSCInlines.h"
+
+namespace JSC { namespace DFG {
+
+HashSet<Node*> liveNodesAtHead(Graph& graph, BasicBlock* block)
+{
+    HashSet<Node*> seen;
+    for (Node* node : block->ssa->liveAtHead)
+        seen.add(node);
+    
+    AvailabilityMap& availabilityMap = block->ssa->availabilityAtHead;
+    graph.forAllLocalsLiveInBytecode(
+        block->firstOrigin().forExit,
+        [&] (VirtualRegister reg) {
+            availabilityMap.closeStartingWithLocal(
+                reg,
+                [&] (Node* node) -> bool {
+                    return seen.contains(node);
+                },
+                [&] (Node* node) -> bool {
+                    return seen.add(node).isNewEntry;
+                });
+        });
+    
+    return seen;
+}
+
+CombinedLiveness::CombinedLiveness(Graph& graph)
+    : liveAtHead(graph)
+    , liveAtTail(graph)
+{
+    // First compute the liveAtHead for each block.
+    for (BasicBlock* block : graph.blocksInNaturalOrder())
+        liveAtHead[block] = liveNodesAtHead(graph, block);
+    
+    // Now compute the liveAtTail by unifying the liveAtHead of the successors.
+    for (BasicBlock* block : graph.blocksInNaturalOrder()) {
+        for (BasicBlock* successor : block->successors()) {
+            for (Node* node : liveAtHead[successor])
+                liveAtTail[block].add(node);
+        }
+    }
+}
+
+} } // namespace JSC::DFG
+
+#endif // ENABLE(DFG_JIT)
+
diff --git a/Source/JavaScriptCore/dfg/DFGCombinedLiveness.h b/Source/JavaScriptCore/dfg/DFGCombinedLiveness.h
new file mode 100644 (file)
index 0000000..ff761cf
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * 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 DFGCombinedLiveness_h
+#define DFGCombinedLiveness_h
+
+#if ENABLE(DFG_JIT)
+
+#include "DFGBlockMap.h"
+#include "DFGGraph.h"
+
+namespace JSC { namespace DFG {
+
+// Returns the set of nodes live at tail, both due to due DFG and due to bytecode (i.e. OSR exit).
+HashSet<Node*> liveNodesAtHead(Graph&, BasicBlock*);
+
+struct CombinedLiveness {
+    CombinedLiveness() { }
+    
+    CombinedLiveness(Graph&);
+    
+    BlockMap<HashSet<Node*>> liveAtHead;
+    BlockMap<HashSet<Node*>> liveAtTail;
+};
+
+} } // namespace JSC::DFG
+
+#endif // ENABLE(DFG_JIT)
+
+#endif // DFGCombinedLiveness_h
+
index 55d6611..bb630cd 100644 (file)
@@ -26,7 +26,7 @@
 #ifndef DFGForAllKills_h
 #define DFGForAllKills_h
 
-#include "BytecodeKills.h"
+#include "DFGCombinedLiveness.h"
 #include "DFGGraph.h"
 #include "DFGOSRAvailabilityAnalysisPhase.h"
 #include "FullBytecodeLiveness.h"
@@ -39,35 +39,6 @@ namespace JSC { namespace DFG {
 // node is killed. A prerequisite to using these utilities is having liveness and OSR availability
 // computed.
 
-template<typename Functor>
-void forAllLiveNodesAtTail(Graph& graph, BasicBlock* block, const Functor& functor)
-{
-    HashSet<Node*> seen;
-    for (Node* node : block->ssa->liveAtTail) {
-        if (seen.add(node).isNewEntry)
-            functor(node);
-    }
-    
-    DFG_ASSERT(graph, block->terminal(), block->terminal()->origin.forExit.isSet());
-    
-    AvailabilityMap& availabilityMap = block->ssa->availabilityAtTail;
-    graph.forAllLocalsLiveInBytecode(
-        block->terminal()->origin.forExit,
-        [&] (VirtualRegister reg) {
-            availabilityMap.closeStartingWithLocal(
-                reg,
-                [&] (Node* node) -> bool {
-                    return seen.contains(node);
-                },
-                [&] (Node* node) -> bool {
-                    if (!seen.add(node).isNewEntry)
-                        return false;
-                    functor(node);
-                    return true;
-                });
-        });
-}
-
 // This tells you those things that die on the boundary between nodeBefore and nodeAfter. It is
 // conservative in the sense that it might resort to telling you some things that are still live at
 // nodeAfter.
@@ -192,13 +163,12 @@ void forAllKilledNodesAtNodeIndex(
 // the value is either no longer live. This pretends that nodes are dead at the end of the block, so that
 // you can use this to do per-basic-block analyses.
 template<typename Functor>
-void forAllKillsInBlock(Graph& graph, BasicBlock* block, const Functor& functor)
+void forAllKillsInBlock(
+    Graph& graph, const CombinedLiveness& combinedLiveness, BasicBlock* block,
+    const Functor& functor)
 {
-    forAllLiveNodesAtTail(
-        graph, block,
-        [&] (Node* node) {
-            functor(block->size(), node);
-        });
+    for (Node* node : combinedLiveness.liveAtTail[block])
+        functor(block->size(), node);
     
     LocalOSRAvailabilityCalculator localAvailability;
     localAvailability.beginBlock(block);
index f7d2383..eaf33f2 100644 (file)
@@ -31,6 +31,7 @@
 #include "DFGAbstractHeap.h"
 #include "DFGBlockMapInlines.h"
 #include "DFGClobberize.h"
+#include "DFGCombinedLiveness.h"
 #include "DFGGraph.h"
 #include "DFGInsertOSRHintsForUpdate.h"
 #include "DFGInsertionSet.h"
@@ -107,6 +108,7 @@ private:
         m_graph.computeRefCounts();
         performLivenessAnalysis(m_graph);
         performOSRAvailabilityAnalysis(m_graph);
+        m_combinedLiveness = CombinedLiveness(m_graph);
         
         CString graphBeforeSinking;
         if (Options::verboseValidationFailure() && Options::validateGraphAtEachPhase()) {
@@ -189,7 +191,7 @@ private:
                 // So, we prune materialized-at-tail to only include things that are live.
                 Vector<Node*> toRemove;
                 for (auto pair : materialized) {
-                    if (!block->ssa->liveAtTail.contains(pair.key))
+                    if (!m_combinedLiveness.liveAtTail[block].contains(pair.key))
                         toRemove.append(pair.key);
                 }
                 for (Node* key : toRemove)
@@ -227,7 +229,7 @@ private:
                 if (pair.value)
                     continue; // It was materialized.
                 
-                if (block->ssa->liveAtTail.contains(pair.key))
+                if (m_combinedLiveness.liveAtTail[block].contains(pair.key))
                     continue; // It might still get materialized in all of the successors.
                 
                 // We know that it died in this block and it wasn't materialized. That means that
@@ -391,7 +393,7 @@ private:
         m_ssaCalculator.computePhis(
             [&] (SSACalculator::Variable* variable, BasicBlock* block) -> Node* {
                 Node* allocation = indexToNode[variable->index()];
-                if (!block->ssa->liveAtHead.contains(allocation))
+                if (!m_combinedLiveness.liveAtHead[block].contains(allocation))
                     return nullptr;
                 
                 Node* phiNode = m_graph.addNode(allocation->prediction(), Phi, NodeOrigin());
@@ -405,7 +407,7 @@ private:
         for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
             HashMap<Node*, Node*> mapping;
             
-            for (Node* candidate : block->ssa->liveAtHead) {
+            for (Node* candidate : m_combinedLiveness.liveAtHead[block]) {
                 SSACalculator::Variable* variable = nodeToVariable.get(candidate);
                 if (!variable)
                     continue;
@@ -714,7 +716,7 @@ private:
         m_ssaCalculator.computePhis(
             [&] (SSACalculator::Variable* variable, BasicBlock* block) -> Node* {
                 PromotedHeapLocation location = m_indexToLocation[variable->index()];
-                if (!block->ssa->liveAtHead.contains(location.base()))
+                if (!m_combinedLiveness.liveAtHead[block].contains(location.base()))
                     return nullptr;
                 
                 Node* phiNode = m_graph.addNode(SpecHeapTop, Phi, NodeOrigin());
@@ -1085,6 +1087,7 @@ private:
         }
     }
     
+    CombinedLiveness m_combinedLiveness;
     SSACalculator m_ssaCalculator;
     HashSet<Node*> m_sinkCandidates;
     HashMap<Node*, Node*> m_materializationToEscapee;
diff --git a/Source/JavaScriptCore/tests/stress/escape-object-in-diamond-then-exit.js b/Source/JavaScriptCore/tests/stress/escape-object-in-diamond-then-exit.js
new file mode 100644 (file)
index 0000000..fbb903d
--- /dev/null
@@ -0,0 +1,40 @@
+var global = null;
+
+function foo(p, q) {
+    var o = {f:42};
+    if (p)
+        global = o;
+    var tmp = q + 1;
+    return o.f + tmp;
+}
+
+noInline(foo);
+
+var lastObject = null;
+
+function validateEscape(when) {
+    if (global === lastObject)
+        throw "Error: bad value in global " + when + ", identical to lastObject.";
+    if (global === null || !(typeof global == "object"))
+        throw "Error: bad value in global " + when + ": it's not an object.";
+    if (global.f != 42)
+        throw "Error: bad value in global " + when + ": f isn't 42, it's: " + global.f;
+    lastObject = global;
+    global = null;
+}
+
+for (var i = 0; i < 10000; ++i) {
+    var escape = !!(i & 1);
+    var result = foo(escape, 42);
+    if (result != 42 + 42 + 1)
+        throw "Error: bad result: " + result;
+    if (escape)
+        validateEscape("in loop");
+    else if (global !== null)
+        throw "Error: bad value in global: " + global;
+}
+
+var result = foo(true, 2147483647);
+if (result != 42 + 2147483647 + 1)
+    throw "Error: bad result at end: " + result;
+validateEscape("at end");
index b565a8f..7b38cef 100644 (file)
@@ -1,6 +1,3 @@
-// https://bugs.webkit.org/show_bug.cgi?id=144945
-//@ skip
-
 function foo(p) {
     var result = 0;
     var o = {valueOf: function() { result = 1; }};