Title: [221607] trunk
Revision
221607
Author
sbar...@apple.com
Date
2017-09-04 21:10:53 -0700 (Mon, 04 Sep 2017)

Log Message

typeCheckHoistingPhase may emit a CheckStructure on the empty value which leads to a dereference of zero on 64 bit platforms
https://bugs.webkit.org/show_bug.cgi?id=176317

Reviewed by Keith Miller.

JSTests:

* stress/dont-crash-when-hoist-check-structure-on-tdz.js: Added.
(Foo):

Source/_javascript_Core:

It turns out that TypeCheckHoistingPhase may hoist a CheckStructure up to
the SetLocal of a particular value where the value is the empty JSValue.
On 64-bit platforms, the empty value is zero. This means that the empty value
passes a cell check. This will lead to a crash when we dereference null to load
the value's structure. This patch teaches TypeCheckHoistingPhase to be conservative
in the structure checks it hoists. On 64-bit platforms, instead of emitting a
CheckStructure node, we now emit a CheckStructureOrEmpty node. This node allows
the empty value to flow through. If the value isn't empty, it'll perform the normal
structure check that CheckStructure performs. For now, we only emit CheckStructureOrEmpty
on 64-bit platforms since a cell check on 32-bit platforms does not allow the empty
value to flow through.

* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGArgumentsEliminationPhase.cpp:
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNode.h:
(JSC::DFG::Node::convertCheckStructureOrEmptyToCheckStructure):
(JSC::DFG::Node::hasStructureSet):
* dfg/DFGNodeType.h:
* dfg/DFGObjectAllocationSinkingPhase.cpp:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::SafeToExecuteEdge::SafeToExecuteEdge):
(JSC::DFG::SafeToExecuteEdge::operator()):
(JSC::DFG::SafeToExecuteEdge::maySeeEmptyChild):
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::emitStructureCheck):
(JSC::DFG::SpeculativeJIT::compileCheckStructure):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGTypeCheckHoistingPhase.cpp:
(JSC::DFG::TypeCheckHoistingPhase::run):
* dfg/DFGValidate.cpp:
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileCheckStructureOrEmpty):

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (221606 => 221607)


--- trunk/JSTests/ChangeLog	2017-09-05 03:44:05 UTC (rev 221606)
+++ trunk/JSTests/ChangeLog	2017-09-05 04:10:53 UTC (rev 221607)
@@ -1,3 +1,13 @@
+2017-09-04  Saam Barati  <sbar...@apple.com>
+
+        typeCheckHoistingPhase may emit a CheckStructure on the empty value which leads to a dereference of zero on 64 bit platforms
+        https://bugs.webkit.org/show_bug.cgi?id=176317
+
+        Reviewed by Keith Miller.
+
+        * stress/dont-crash-when-hoist-check-structure-on-tdz.js: Added.
+        (Foo):
+
 2017-09-03  Yusuke Suzuki  <utatane....@gmail.com>
 
         [DFG][FTL] Efficiently execute number#toString()

Added: trunk/JSTests/stress/dont-crash-when-hoist-check-structure-on-tdz.js (0 => 221607)


--- trunk/JSTests/stress/dont-crash-when-hoist-check-structure-on-tdz.js	                        (rev 0)
+++ trunk/JSTests/stress/dont-crash-when-hoist-check-structure-on-tdz.js	2017-09-05 04:10:53 UTC (rev 221607)
@@ -0,0 +1,28 @@
+class Foo extends Object {
+    constructor(c1, c2) {
+        if (c1)
+            super();
+        let arrow = () => {
+            if (c2)
+                this.foo = 20;
+            else
+                this.foo = 40;
+        };
+        noInline(arrow);
+        arrow();
+    }
+}
+noInline(Foo);
+
+for (let i = 0; i < 1000; ++i)
+    new Foo(true, !!(i%2));
+
+let threw = false;
+try {
+    new Foo(false, true);
+} catch {
+    threw = true;
+} finally {
+    if (!threw)
+        throw new Error("Bad")
+}

Modified: trunk/Source/_javascript_Core/ChangeLog (221606 => 221607)


--- trunk/Source/_javascript_Core/ChangeLog	2017-09-05 03:44:05 UTC (rev 221606)
+++ trunk/Source/_javascript_Core/ChangeLog	2017-09-05 04:10:53 UTC (rev 221607)
@@ -1,5 +1,63 @@
 2017-09-04  Saam Barati  <sbar...@apple.com>
 
+        typeCheckHoistingPhase may emit a CheckStructure on the empty value which leads to a dereference of zero on 64 bit platforms
+        https://bugs.webkit.org/show_bug.cgi?id=176317
+
+        Reviewed by Keith Miller.
+
+        It turns out that TypeCheckHoistingPhase may hoist a CheckStructure up to 
+        the SetLocal of a particular value where the value is the empty JSValue.
+        On 64-bit platforms, the empty value is zero. This means that the empty value
+        passes a cell check. This will lead to a crash when we dereference null to load
+        the value's structure. This patch teaches TypeCheckHoistingPhase to be conservative
+        in the structure checks it hoists. On 64-bit platforms, instead of emitting a
+        CheckStructure node, we now emit a CheckStructureOrEmpty node. This node allows
+        the empty value to flow through. If the value isn't empty, it'll perform the normal
+        structure check that CheckStructure performs. For now, we only emit CheckStructureOrEmpty
+        on 64-bit platforms since a cell check on 32-bit platforms does not allow the empty
+        value to flow through.
+
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGArgumentsEliminationPhase.cpp:
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGConstantFoldingPhase.cpp:
+        (JSC::DFG::ConstantFoldingPhase::foldConstants):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::convertCheckStructureOrEmptyToCheckStructure):
+        (JSC::DFG::Node::hasStructureSet):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGObjectAllocationSinkingPhase.cpp:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::SafeToExecuteEdge::SafeToExecuteEdge):
+        (JSC::DFG::SafeToExecuteEdge::operator()):
+        (JSC::DFG::SafeToExecuteEdge::maySeeEmptyChild):
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::emitStructureCheck):
+        (JSC::DFG::SpeculativeJIT::compileCheckStructure):
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGTypeCheckHoistingPhase.cpp:
+        (JSC::DFG::TypeCheckHoistingPhase::run):
+        * dfg/DFGValidate.cpp:
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileCheckStructureOrEmpty):
+
+2017-09-04  Saam Barati  <sbar...@apple.com>
+
         Support compiling catch in the FTL
         https://bugs.webkit.org/show_bug.cgi?id=175396
 

Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (221606 => 221607)


--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2017-09-05 03:44:05 UTC (rev 221606)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2017-09-05 04:10:53 UTC (rev 221607)
@@ -2406,6 +2406,18 @@
         filter(value, set, admittedTypes);
         break;
     }
+
+    case CheckStructureOrEmpty: {
+        AbstractValue& value = forNode(node->child1());
+
+        bool mayBeEmpty = value.m_type & SpecEmpty;
+        if (!mayBeEmpty)
+            m_state.setFoundConstants(true);
+
+        SpeculatedType admittedTypes = mayBeEmpty ? SpecEmpty : SpecNone;
+        filter(value, node->structureSet(), admittedTypes);
+        break;
+    }
         
     case CheckStructureImmediate: {
         // FIXME: This currently can only reason about one structure at a time.

Modified: trunk/Source/_javascript_Core/dfg/DFGArgumentsEliminationPhase.cpp (221606 => 221607)


--- trunk/Source/_javascript_Core/dfg/DFGArgumentsEliminationPhase.cpp	2017-09-05 03:44:05 UTC (rev 221606)
+++ trunk/Source/_javascript_Core/dfg/DFGArgumentsEliminationPhase.cpp	2017-09-05 04:10:53 UTC (rev 221607)
@@ -370,6 +370,7 @@
                     escapeBasedOnArrayMode(node->arrayMode(), node->child1(), node);
                     break;
 
+                case CheckStructureOrEmpty:
                 case CheckStructure: {
                     if (!m_candidates.contains(node->child1().node()))
                         break;
@@ -1096,6 +1097,7 @@
                     break;
                 }
 
+                case CheckStructureOrEmpty:
                 case CheckStructure:
                     if (!isEliminatedAllocation(node->child1().node()))
                         break;

Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (221606 => 221607)


--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2017-09-05 03:44:05 UTC (rev 221606)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2017-09-05 04:10:53 UTC (rev 221607)
@@ -958,6 +958,7 @@
         return;
     }
         
+    case CheckStructureOrEmpty:
     case CheckStructure:
         read(JSCell_structureID);
         return;

Modified: trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp (221606 => 221607)


--- trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp	2017-09-05 03:44:05 UTC (rev 221606)
+++ trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp	2017-09-05 04:10:53 UTC (rev 221607)
@@ -161,7 +161,14 @@
                 break;
             }
 
-                
+            case CheckStructureOrEmpty: {
+                const AbstractValue& value = m_state.forNode(node->child1());
+                if (value.m_type & SpecEmpty)
+                    break;
+                node->convertCheckStructureOrEmptyToCheckStructure();
+                changed = true;
+                FALLTHROUGH;
+            }
             case CheckStructure:
             case ArrayifyToStructure: {
                 AbstractValue& value = m_state.forNode(node->child1());

Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (221606 => 221607)


--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2017-09-05 03:44:05 UTC (rev 221606)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2017-09-05 04:10:53 UTC (rev 221607)
@@ -115,6 +115,8 @@
     case DeleteById:
     case DeleteByVal:
     case CheckStructure:
+    case CheckStructureOrEmpty:
+    case CheckStructureImmediate:
     case GetExecutable:
     case GetButterfly:
     case GetButterflyWithoutCaging:
@@ -263,7 +265,6 @@
     case GetMyArgumentByValOutOfBounds:
     case ForwardVarargs:
     case PutHint:
-    case CheckStructureImmediate:
     case PutStack:
     case KillStack:
     case GetStack:

Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (221606 => 221607)


--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2017-09-05 03:44:05 UTC (rev 221606)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2017-09-05 04:10:53 UTC (rev 221607)
@@ -1583,6 +1583,7 @@
         case GetVectorLength:
         case PutHint:
         case CheckStructureImmediate:
+        case CheckStructureOrEmpty:
         case MaterializeNewObject:
         case MaterializeCreateActivation:
         case PutStack:

Modified: trunk/Source/_javascript_Core/dfg/DFGNode.h (221606 => 221607)


--- trunk/Source/_javascript_Core/dfg/DFGNode.h	2017-09-05 03:44:05 UTC (rev 221606)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.h	2017-09-05 04:10:53 UTC (rev 221607)
@@ -432,6 +432,12 @@
         m_opInfo = set;
     }
 
+    void convertCheckStructureOrEmptyToCheckStructure()
+    {
+        ASSERT(op() == CheckStructureOrEmpty);
+        setOpAndDefaultFlags(CheckStructure);
+    }
+
     void convertToCheckStructureImmediate(Node* structure)
     {
         ASSERT(op() == CheckStructure);
@@ -1716,6 +1722,7 @@
     {
         switch (op()) {
         case CheckStructure:
+        case CheckStructureOrEmpty:
         case CheckStructureImmediate:
         case MaterializeNewObject:
             return true;

Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (221606 => 221607)


--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2017-09-05 03:44:05 UTC (rev 221606)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2017-09-05 04:10:53 UTC (rev 221607)
@@ -203,6 +203,7 @@
     macro(DeleteById, NodeResultBoolean | NodeMustGenerate) \
     macro(DeleteByVal, NodeResultBoolean | NodeMustGenerate) \
     macro(CheckStructure, NodeMustGenerate) \
+    macro(CheckStructureOrEmpty, NodeMustGenerate) \
     macro(GetExecutable, NodeResultJS) \
     macro(PutStructure, NodeMustGenerate) \
     macro(AllocatePropertyStorage, NodeMustGenerate | NodeResultStorage) \

Modified: trunk/Source/_javascript_Core/dfg/DFGObjectAllocationSinkingPhase.cpp (221606 => 221607)


--- trunk/Source/_javascript_Core/dfg/DFGObjectAllocationSinkingPhase.cpp	2017-09-05 03:44:05 UTC (rev 221606)
+++ trunk/Source/_javascript_Core/dfg/DFGObjectAllocationSinkingPhase.cpp	2017-09-05 04:10:53 UTC (rev 221607)
@@ -892,6 +892,7 @@
                 m_heap.escape(node->child1().node());
             break;
 
+        case CheckStructureOrEmpty:
         case CheckStructure: {
             Allocation* allocation = m_heap.onlyLocalAllocation(node->child1().node());
             if (allocation && allocation->isObjectAllocation()) {

Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (221606 => 221607)


--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2017-09-05 03:44:05 UTC (rev 221606)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2017-09-05 04:10:53 UTC (rev 221607)
@@ -1069,6 +1069,7 @@
         case GetMyArgumentByValOutOfBounds:
         case PutHint:
         case CheckStructureImmediate:
+        case CheckStructureOrEmpty:
         case MaterializeNewObject:
         case MaterializeCreateActivation:
         case PutStack:
@@ -1081,8 +1082,7 @@
         case RecordRegExpCachedResult:
         case LazyJSConstant:
         case CallDOM: {
-            // This node should never be visible at this stage of compilation. It is
-            // inserted by fixup(), which follows this phase.
+            // This node should never be visible at this stage of compilation.
             DFG_CRASH(m_graph, m_currentNode, "Unexpected node during prediction propagation");
             break;
         }

Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (221606 => 221607)


--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2017-09-05 03:44:05 UTC (rev 221606)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2017-09-05 04:10:53 UTC (rev 221607)
@@ -36,12 +36,13 @@
 public:
     SafeToExecuteEdge(AbstractStateType& state)
         : m_state(state)
-        , m_result(true)
     {
     }
     
     void operator()(Node*, Edge edge)
     {
+        m_maySeeEmptyChild |= !!(m_state.forNode(edge).m_type & SpecEmpty);
+
         switch (edge.useKind()) {
         case UntypedUse:
         case Int32Use:
@@ -110,9 +111,11 @@
     }
     
     bool result() const { return m_result; }
+    bool maySeeEmptyChild() const { return m_maySeeEmptyChild; }
 private:
     AbstractStateType& m_state;
-    bool m_result;
+    bool m_result { true };
+    bool m_maySeeEmptyChild { false };
 };
 
 // Determines if it's safe to execute a node within the given abstract state. This may
@@ -134,6 +137,24 @@
     if (!safeToExecuteEdge.result())
         return false;
 
+    if (safeToExecuteEdge.maySeeEmptyChild()) {
+        // We conservatively assume if the empty value flows into a node,
+        // it might not be able to handle it (e.g, crash). In general, the bytecode generator
+        // emits code in such a way that most node types don't need to worry about the empty value
+        // because they will never see it. However, code motion has to consider the empty
+        // value so it does not insert/move nodes to a place where they will crash. E.g, the
+        // type check hoisting phase needs to insert CheckStructureOrEmpty instead of CheckStructure
+        // for hoisted structure checks because it can not guarantee that a particular local is not
+        // the empty value.
+        switch (node->op()) {
+        case CheckNotEmpty:
+        case CheckStructureOrEmpty:
+            break;
+        default:
+            return false;
+        }
+    }
+
     // NOTE: This tends to lie when it comes to effectful nodes, because it knows that they aren't going to
     // get hoisted anyway.
 
@@ -214,6 +235,7 @@
     case DefineDataProperty:
     case DefineAccessorProperty:
     case CheckStructure:
+    case CheckStructureOrEmpty:
     case GetExecutable:
     case GetButterfly:
     case GetButterflyWithoutCaging:

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (221606 => 221607)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2017-09-05 03:44:05 UTC (rev 221606)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2017-09-05 04:10:53 UTC (rev 221607)
@@ -7891,7 +7891,7 @@
     cellResult(resultGPR, node);
 }
 
-void SpeculativeJIT::compileCheckStructure(Node* node, GPRReg cellGPR, GPRReg tempGPR)
+void SpeculativeJIT::emitStructureCheck(Node* node, GPRReg cellGPR, GPRReg tempGPR)
 {
     ASSERT(node->structureSet().size());
     
@@ -7936,7 +7936,7 @@
     case CellUse:
     case KnownCellUse: {
         SpeculateCellOperand cell(this, node->child1());
-        compileCheckStructure(node, cell.gpr(), InvalidGPRReg);
+        emitStructureCheck(node, cell.gpr(), InvalidGPRReg);
         noResult(node);
         return;
     }
@@ -7954,7 +7954,7 @@
             m_jit.branchIfNotOther(valueRegs, tempGPR));
         JITCompiler::Jump done = m_jit.jump();
         cell.link(&m_jit);
-        compileCheckStructure(node, valueRegs.payloadGPR(), tempGPR);
+        emitStructureCheck(node, valueRegs.payloadGPR(), tempGPR);
         done.link(&m_jit);
         noResult(node);
         return;

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (221606 => 221607)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2017-09-05 03:44:05 UTC (rev 221606)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2017-09-05 04:10:53 UTC (rev 221607)
@@ -2906,8 +2906,8 @@
     void compileIsObjectOrNull(Node*);
     void compileIsFunction(Node*);
     void compileTypeOf(Node*);
-    void compileCheckStructure(Node*, GPRReg cellGPR, GPRReg tempGPR);
     void compileCheckStructure(Node*);
+    void emitStructureCheck(Node*, GPRReg cellGPR, GPRReg tempGPR);
     void compilePutAccessorById(Node*);
     void compilePutGetterSetterById(Node*);
     void compilePutAccessorByVal(Node*);

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (221606 => 221607)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2017-09-05 03:44:05 UTC (rev 221606)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2017-09-05 04:10:53 UTC (rev 221607)
@@ -5658,6 +5658,10 @@
         break;
     }
 
+    case CheckStructureOrEmpty:
+        DFG_CRASH(m_jit.graph(), node, "CheckStructureOrEmpty only used in 64-bit DFG");
+        break;
+
     case LastNodeType:
     case Phi:
     case Upsilon:

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (221606 => 221607)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2017-09-05 03:44:05 UTC (rev 221606)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2017-09-05 04:10:53 UTC (rev 221607)
@@ -4634,6 +4634,22 @@
         break;
     }
         
+    case CheckStructureOrEmpty: {
+        SpeculateCellOperand cell(this, node->child1());
+        GPRReg cellGPR = cell.gpr();
+        MacroAssembler::Jump isEmpty;
+        if (m_interpreter.forNode(node->child1()).m_type & SpecEmpty)
+            isEmpty = m_jit.branchTest64(MacroAssembler::Zero, cellGPR);
+
+        emitStructureCheck(node, cellGPR, InvalidGPRReg);
+
+        if (isEmpty.isSet())
+            isEmpty.link(&m_jit);
+
+        noResult(node);
+        break;
+    }
+
     case CheckStructure: {
         compileCheckStructure(node);
         break;

Modified: trunk/Source/_javascript_Core/dfg/DFGTypeCheckHoistingPhase.cpp (221606 => 221607)


--- trunk/Source/_javascript_Core/dfg/DFGTypeCheckHoistingPhase.cpp	2017-09-05 03:44:05 UTC (rev 221606)
+++ trunk/Source/_javascript_Core/dfg/DFGTypeCheckHoistingPhase.cpp	2017-09-05 04:10:53 UTC (rev 221607)
@@ -178,8 +178,14 @@
                     Edge child1 = node->child1();
                     
                     if (iter->value.m_structure) {
+                        // Note: On 64-bit platforms, cell checks allow the empty value to flow through.
+                        // This means that this structure check may see the empty value as input. We need
+                        // to emit a node that explicitly handles the empty value. Most of the time, CheckStructureOrEmpty
+                        // will be folded to CheckStructure because AI proves that the incoming value is
+                        // definitely not empty.
+                        static_assert(is64Bit() || !(SpecCellCheck & SpecEmpty), "");
                         insertionSet.insertNode(
-                            indexForChecks, SpecNone, CheckStructure,
+                            indexForChecks, SpecNone, is64Bit() ? CheckStructureOrEmpty : CheckStructure,
                             originForChecks.withSemantic(origin.semantic),
                             OpInfo(m_graph.addStructureSet(iter->value.m_structure)),
                             Edge(child1.node(), CellUse));

Modified: trunk/Source/_javascript_Core/dfg/DFGValidate.cpp (221606 => 221607)


--- trunk/Source/_javascript_Core/dfg/DFGValidate.cpp	2017-09-05 03:44:05 UTC (rev 221606)
+++ trunk/Source/_javascript_Core/dfg/DFGValidate.cpp	2017-09-05 04:10:53 UTC (rev 221607)
@@ -277,6 +277,11 @@
                     VALIDATE((node), !!node->child1());
                     VALIDATE((node), !!node->cellOperand()->value() && node->cellOperand()->value().isCell());
                     break;
+                case CheckStructureOrEmpty:
+                    VALIDATE((node), is64Bit());
+                    VALIDATE((node), !!node->child1());
+                    VALIDATE((node), node->child1().useKind() == CellUse);
+                    break;
                 case CheckStructure:
                 case StringFromCharCode:
                     VALIDATE((node), !!node->child1());

Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (221606 => 221607)


--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2017-09-05 03:44:05 UTC (rev 221606)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2017-09-05 04:10:53 UTC (rev 221607)
@@ -65,6 +65,7 @@
     case BitLShift:
     case BitURShift:
     case CheckStructure:
+    case CheckStructureOrEmpty:
     case DoubleAsInt32:
     case ArrayifyToStructure:
     case PutStructure:

Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (221606 => 221607)


--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2017-09-05 03:44:05 UTC (rev 221606)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2017-09-05 04:10:53 UTC (rev 221607)
@@ -644,6 +644,9 @@
         case CheckStructure:
             compileCheckStructure();
             break;
+        case CheckStructureOrEmpty:
+            compileCheckStructureOrEmpty();
+            break;
         case CheckCell:
             compileCheckCell();
             break;
@@ -2763,6 +2766,39 @@
             return;
         }
     }
+
+    void compileCheckStructureOrEmpty()
+    {
+        ExitKind exitKind;
+        if (m_node->child1()->hasConstant())
+            exitKind = BadConstantCache;
+        else
+            exitKind = BadCache;
+
+        LValue cell = lowCell(m_node->child1());
+        bool maySeeEmptyValue = m_interpreter.forNode(m_node->child1()).m_type & SpecEmpty;
+        LBasicBlock notEmpty;
+        LBasicBlock continuation;
+        LBasicBlock lastNext;
+        if (maySeeEmptyValue) {
+            notEmpty = m_out.newBlock();
+            continuation = m_out.newBlock();
+            m_out.branch(m_out.isZero64(cell), unsure(continuation), unsure(notEmpty));
+            lastNext = m_out.appendTo(notEmpty, continuation);
+        }
+
+        checkStructure(
+            m_out.load32(cell, m_heaps.JSCell_structureID), jsValueValue(cell),
+            exitKind, m_node->structureSet(),
+            [&] (RegisteredStructure structure) {
+                return weakStructureID(structure);
+            });
+
+        if (maySeeEmptyValue) {
+            m_out.jump(continuation);
+            m_out.appendTo(continuation, lastNext);
+        }
+    }
     
     void compileCheckCell()
     {
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to