| /* | 
 |  * Copyright (C) 2014-2018 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.  | 
 |  */ | 
 |  | 
 | #pragma once | 
 |  | 
 | #if ENABLE(DFG_JIT) | 
 |  | 
 | #include "DFGClobberize.h" | 
 |  | 
 | namespace JSC { namespace DFG { | 
 |  | 
 | template<typename ReadFunctor, typename WriteFunctor, typename DefFunctor> | 
 | class PreciseLocalClobberizeAdaptor { | 
 | public: | 
 |     PreciseLocalClobberizeAdaptor( | 
 |         Graph& graph, Node* node, | 
 |         const ReadFunctor& read, const WriteFunctor& write, const DefFunctor& def) | 
 |         : m_graph(graph) | 
 |         , m_node(node) | 
 |         , m_read(read) | 
 |         , m_unconditionalWrite(write) | 
 |         , m_def(def) | 
 |     { | 
 |     } | 
 |      | 
 |     void read(AbstractHeap heap) | 
 |     { | 
 |         if (heap.kind() == Stack) { | 
 |             if (heap.payload().isTop()) { | 
 |                 readTop(); | 
 |                 return; | 
 |             } | 
 |              | 
 |             callIfAppropriate(m_read, heap.operand()); | 
 |             return; | 
 |         } | 
 |          | 
 |         if (heap.overlaps(Stack)) { | 
 |             readTop(); | 
 |             return; | 
 |         } | 
 |     } | 
 |      | 
 |     void write(AbstractHeap heap) | 
 |     { | 
 |         // We expect stack writes to already be precisely characterized by DFG::clobberize(). | 
 |         if (heap.kind() == Stack) { | 
 |             RELEASE_ASSERT(!heap.payload().isTop()); | 
 |             callIfAppropriate(m_unconditionalWrite, heap.operand()); | 
 |             return; | 
 |         } | 
 |          | 
 |         RELEASE_ASSERT(!heap.overlaps(Stack)); | 
 |     } | 
 |      | 
 |     void def(PureValue) | 
 |     { | 
 |         // PureValue defs never have anything to do with locals, so ignore this. | 
 |     } | 
 |      | 
 |     void def(HeapLocation location, LazyNode node) | 
 |     { | 
 |         if (location.kind() != StackLoc) | 
 |             return; | 
 |          | 
 |         RELEASE_ASSERT(location.heap().kind() == Stack); | 
 |          | 
 |         m_def(location.heap().operand(), node); | 
 |     } | 
 |      | 
 | private: | 
 |     template<typename Functor> | 
 |     void callIfAppropriate(const Functor& functor, Operand operand) | 
 |     { | 
 |         if (operand.isLocal() && static_cast<unsigned>(operand.toLocal()) >= m_graph.block(0)->variablesAtHead.numberOfLocals()) | 
 |             return; | 
 |          | 
 |         if (operand.isArgument() && !operand.isHeader() && static_cast<unsigned>(operand.toArgument()) >= m_graph.block(0)->variablesAtHead.numberOfArguments()) | 
 |             return; | 
 |          | 
 |         functor(operand); | 
 |     } | 
 |      | 
 |     void readTop() | 
 |     { | 
 |         auto readFrame = [&] (InlineCallFrame* inlineCallFrame, unsigned numberOfArgumentsToSkip) { | 
 |             if (!inlineCallFrame) { | 
 |                 // Read the outermost arguments and argument count. | 
 |                 for (unsigned i = numberOfArgumentsToSkip; i < static_cast<unsigned>(m_graph.m_codeBlock->numParameters()); i++) | 
 |                     m_read(virtualRegisterForArgumentIncludingThis(i)); | 
 |                 m_read(VirtualRegister(CallFrameSlot::argumentCountIncludingThis)); | 
 |                 return; | 
 |             } | 
 |              | 
 |             for (unsigned i = numberOfArgumentsToSkip; i < inlineCallFrame->argumentsWithFixup.size(); i++) | 
 |                 m_read(VirtualRegister(inlineCallFrame->stackOffset + virtualRegisterForArgumentIncludingThis(i).offset())); | 
 |             if (inlineCallFrame->isVarargs()) | 
 |                 m_read(VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::argumentCountIncludingThis)); | 
 |         }; | 
 |  | 
 |         auto readSpread = [&] (Node* spread) { | 
 |             ASSERT(spread->op() == Spread || spread->op() == PhantomSpread); | 
 |             if (!spread->child1()->isPhantomAllocation()) | 
 |                 return; | 
 |  | 
 |             ASSERT(spread->child1()->op() == PhantomCreateRest || spread->child1()->op() == PhantomNewArrayBuffer); | 
 |             if (spread->child1()->op() == PhantomNewArrayBuffer) { | 
 |                 // This reads from a constant buffer. | 
 |                 return; | 
 |             } | 
 |             InlineCallFrame* inlineCallFrame = spread->child1()->origin.semantic.inlineCallFrame(); | 
 |             unsigned numberOfArgumentsToSkip = spread->child1()->numberOfArgumentsToSkip(); | 
 |             readFrame(inlineCallFrame, numberOfArgumentsToSkip); | 
 |         }; | 
 |  | 
 |         auto readNewArrayWithSpreadNode = [&] (Node* arrayWithSpread) { | 
 |             ASSERT(arrayWithSpread->op() == NewArrayWithSpread || arrayWithSpread->op() == PhantomNewArrayWithSpread); | 
 |             BitVector* bitVector = arrayWithSpread->bitVector(); | 
 |             for (unsigned i = 0; i < arrayWithSpread->numChildren(); i++) { | 
 |                 if (bitVector->get(i)) { | 
 |                     Node* child = m_graph.varArgChild(arrayWithSpread, i).node(); | 
 |                     if (child->op() == PhantomSpread) | 
 |                         readSpread(child); | 
 |                 } | 
 |             } | 
 |         }; | 
 |  | 
 |         switch (m_node->op()) { | 
 |         case ForwardVarargs: | 
 |         case CallForwardVarargs: | 
 |         case ConstructForwardVarargs: | 
 |         case TailCallForwardVarargs: | 
 |         case TailCallForwardVarargsInlinedCaller: | 
 |         case GetMyArgumentByVal: | 
 |         case GetMyArgumentByValOutOfBounds: | 
 |         case CreateDirectArguments: | 
 |         case CreateScopedArguments: | 
 |         case CreateClonedArguments: | 
 |         case CreateArgumentsButterfly: | 
 |         case PhantomDirectArguments: | 
 |         case PhantomClonedArguments: | 
 |         case GetRestLength: | 
 |         case CreateRest: { | 
 |             bool isForwardingNode = false; | 
 |             bool isPhantomNode = false; | 
 |             switch (m_node->op()) { | 
 |             case ForwardVarargs: | 
 |             case CallForwardVarargs: | 
 |             case ConstructForwardVarargs: | 
 |             case TailCallForwardVarargs: | 
 |             case TailCallForwardVarargsInlinedCaller: | 
 |                 isForwardingNode = true; | 
 |                 break; | 
 |             case PhantomDirectArguments: | 
 |             case PhantomClonedArguments: | 
 |                 isPhantomNode = true; | 
 |                 break; | 
 |             default: | 
 |                 break; | 
 |             } | 
 |  | 
 |             if (isPhantomNode && m_graph.m_plan.isFTL()) | 
 |                 break; | 
 |              | 
 |             if (isForwardingNode && m_node->hasArgumentsChild() && m_node->argumentsChild() | 
 |                 && (m_node->argumentsChild()->op() == PhantomNewArrayWithSpread || m_node->argumentsChild()->op() == PhantomSpread)) { | 
 |                 if (m_node->argumentsChild()->op() == PhantomNewArrayWithSpread) | 
 |                     readNewArrayWithSpreadNode(m_node->argumentsChild().node()); | 
 |                 else | 
 |                     readSpread(m_node->argumentsChild().node()); | 
 |             } else { | 
 |                 InlineCallFrame* inlineCallFrame; | 
 |                 if (m_node->hasArgumentsChild() && m_node->argumentsChild()) | 
 |                     inlineCallFrame = m_node->argumentsChild()->origin.semantic.inlineCallFrame(); | 
 |                 else | 
 |                     inlineCallFrame = m_node->origin.semantic.inlineCallFrame(); | 
 |  | 
 |                 unsigned numberOfArgumentsToSkip = 0; | 
 |                 if (m_node->op() == GetMyArgumentByVal || m_node->op() == GetMyArgumentByValOutOfBounds) { | 
 |                     // The value of numberOfArgumentsToSkip guarantees that GetMyArgumentByVal* will never | 
 |                     // read any arguments below the number of arguments to skip. For example, if numberOfArgumentsToSkip is 2, | 
 |                     // we will never read argument 0 or argument 1. | 
 |                     numberOfArgumentsToSkip = m_node->numberOfArgumentsToSkip(); | 
 |                 } | 
 |  | 
 |                 readFrame(inlineCallFrame, numberOfArgumentsToSkip); | 
 |             } | 
 |  | 
 |             break; | 
 |         } | 
 |          | 
 |         case Spread: | 
 |             readSpread(m_node); | 
 |             break; | 
 |          | 
 |         case NewArrayWithSpread: { | 
 |             readNewArrayWithSpreadNode(m_node); | 
 |             break; | 
 |         } | 
 |  | 
 |         case GetArgument: { | 
 |             InlineCallFrame* inlineCallFrame = m_node->origin.semantic.inlineCallFrame(); | 
 |             unsigned indexIncludingThis = m_node->argumentIndex(); | 
 |             if (!inlineCallFrame) { | 
 |                 if (indexIncludingThis < static_cast<unsigned>(m_graph.m_codeBlock->numParameters())) | 
 |                     m_read(virtualRegisterForArgumentIncludingThis(indexIncludingThis)); | 
 |                 m_read(VirtualRegister(CallFrameSlot::argumentCountIncludingThis)); | 
 |                 break; | 
 |             } | 
 |  | 
 |             ASSERT_WITH_MESSAGE(inlineCallFrame->isVarargs(), "GetArgument is only used for InlineCallFrame if the call frame is varargs."); | 
 |             if (indexIncludingThis < inlineCallFrame->argumentsWithFixup.size()) | 
 |                 m_read(VirtualRegister(inlineCallFrame->stackOffset + virtualRegisterForArgumentIncludingThis(indexIncludingThis).offset())); | 
 |             m_read(VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::argumentCountIncludingThis)); | 
 |             break; | 
 |         } | 
 |              | 
 |         default: { | 
 |             // All of the outermost arguments, except this, are read in sloppy mode. | 
 |             if (!m_graph.m_codeBlock->isStrictMode()) { | 
 |                 for (unsigned i = m_graph.m_codeBlock->numParameters(); i--;) | 
 |                     m_read(virtualRegisterForArgumentIncludingThis(i)); | 
 |             } | 
 |          | 
 |             // The stack header is read. | 
 |             for (unsigned i = 0; i < CallFrameSlot::thisArgument; ++i) | 
 |                 m_read(VirtualRegister(i)); | 
 |          | 
 |             // Read all of the inline arguments and call frame headers that we didn't already capture. | 
 |             for (InlineCallFrame* inlineCallFrame = m_node->origin.semantic.inlineCallFrame(); inlineCallFrame; inlineCallFrame = inlineCallFrame->getCallerInlineFrameSkippingTailCalls()) { | 
 |                 if (!inlineCallFrame->isStrictMode()) { | 
 |                     for (unsigned i = inlineCallFrame->argumentsWithFixup.size(); i--;) | 
 |                         m_read(VirtualRegister(inlineCallFrame->stackOffset + virtualRegisterForArgumentIncludingThis(i).offset())); | 
 |                 } | 
 |                 if (inlineCallFrame->isClosureCall) | 
 |                     m_read(VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::callee)); | 
 |                 if (inlineCallFrame->isVarargs()) | 
 |                     m_read(VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::argumentCountIncludingThis)); | 
 |             } | 
 |             break; | 
 |         } } | 
 |     } | 
 |      | 
 |     Graph& m_graph; | 
 |     Node* m_node; | 
 |     const ReadFunctor& m_read; | 
 |     const WriteFunctor& m_unconditionalWrite; | 
 |     const DefFunctor& m_def; | 
 | }; | 
 |  | 
 | template<typename ReadFunctor, typename WriteFunctor, typename DefFunctor> | 
 | void preciseLocalClobberize( | 
 |     Graph& graph, Node* node, | 
 |     const ReadFunctor& read, const WriteFunctor& write, const DefFunctor& def) | 
 | { | 
 |     PreciseLocalClobberizeAdaptor<ReadFunctor, WriteFunctor, DefFunctor> | 
 |         adaptor(graph, node, read, write, def); | 
 |     clobberize(graph, node, adaptor); | 
 | } | 
 |  | 
 | } } // namespace JSC::DFG | 
 |  | 
 | #endif // ENABLE(DFG_JIT) |