123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500 |
- /*
- * Copyright (C) 2013 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 "DFGCPSRethreadingPhase.h"
- #if ENABLE(DFG_JIT)
- #include "DFGBasicBlockInlines.h"
- #include "DFGGraph.h"
- #include "DFGPhase.h"
- #include "Operations.h"
- namespace JSC { namespace DFG {
- class CPSRethreadingPhase : public Phase {
- public:
- CPSRethreadingPhase(Graph& graph)
- : Phase(graph, "CPS rethreading")
- {
- }
-
- bool run()
- {
- if (m_graph.m_form == ThreadedCPS)
- return false;
-
- clearIsLoadedFrom();
- freeUnnecessaryNodes();
- canonicalizeLocalsInBlocks();
- propagatePhis<LocalOperand>();
- propagatePhis<ArgumentOperand>();
-
- m_graph.m_form = ThreadedCPS;
- return true;
- }
- private:
-
- void clearIsLoadedFrom()
- {
- for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i)
- m_graph.m_variableAccessData[i].setIsLoadedFrom(false);
- }
-
- void freeUnnecessaryNodes()
- {
- SamplingRegion samplingRegion("DFG CPS Rethreading: freeUnnecessaryNodes");
-
- for (BlockIndex blockIndex = m_graph.m_blocks.size(); blockIndex--;) {
- BasicBlock* block = m_graph.m_blocks[blockIndex].get();
- if (!block)
- continue;
- ASSERT(block->isReachable);
-
- unsigned fromIndex = 0;
- unsigned toIndex = 0;
- while (fromIndex < block->size()) {
- Node* node = block->at(fromIndex++);
- switch (node->op()) {
- case GetLocal:
- case Flush:
- case PhantomLocal:
- node->children.setChild1(Edge());
- break;
- case Phantom:
- if (!node->child1())
- continue;
- switch (node->child1()->op()) {
- case Phi:
- case SetArgument:
- case SetLocal:
- node->convertToPhantomLocal();
- break;
- default:
- ASSERT(node->child1()->hasResult());
- break;
- }
- break;
- case Nop:
- continue;
- default:
- break;
- }
- node->replacement = 0; // Reset the replacement since the next phase will use it.
- block->at(toIndex++) = node;
- }
- block->resize(toIndex);
-
- for (unsigned phiIndex = block->phis.size(); phiIndex--;)
- m_graph.m_allocator.free(block->phis[phiIndex]);
- block->phis.resize(0);
- }
- }
-
- template<OperandKind operandKind>
- void clearVariablesAtHeadAndTail()
- {
- ASSERT(
- m_block->variablesAtHead.sizeFor<operandKind>()
- == m_block->variablesAtTail.sizeFor<operandKind>());
-
- for (unsigned i = m_block->variablesAtHead.sizeFor<operandKind>(); i--;) {
- m_block->variablesAtHead.atFor<operandKind>(i) = 0;
- m_block->variablesAtTail.atFor<operandKind>(i) = 0;
- }
- }
-
- ALWAYS_INLINE Node* addPhiSilently(BasicBlock* block, const CodeOrigin& codeOrigin, VariableAccessData* variable)
- {
- Node* result = m_graph.addNode(SpecNone, Phi, codeOrigin, OpInfo(variable));
- block->phis.append(result);
- return result;
- }
-
- template<OperandKind operandKind>
- ALWAYS_INLINE Node* addPhi(BasicBlock* block, const CodeOrigin& codeOrigin, VariableAccessData* variable, size_t index)
- {
- Node* result = addPhiSilently(block, codeOrigin, variable);
- phiStackFor<operandKind>().append(PhiStackEntry(block, index, result));
- return result;
- }
-
- template<OperandKind operandKind>
- ALWAYS_INLINE Node* addPhi(const CodeOrigin& codeOrigin, VariableAccessData* variable, size_t index)
- {
- return addPhi<operandKind>(m_block, codeOrigin, variable, index);
- }
-
- template<OperandKind operandKind>
- void canonicalizeGetLocalFor(Node* node, VariableAccessData* variable, size_t idx)
- {
- ASSERT(!node->child1());
-
- if (Node* otherNode = m_block->variablesAtTail.atFor<operandKind>(idx)) {
- ASSERT(otherNode->variableAccessData() == variable);
-
- switch (otherNode->op()) {
- case Flush:
- case PhantomLocal:
- otherNode = otherNode->child1().node();
- if (otherNode->op() == Phi) {
- // We need to have a GetLocal, so this might as well be the one.
- node->children.setChild1(Edge(otherNode));
- m_block->variablesAtTail.atFor<operandKind>(idx) = node;
- return;
- }
- ASSERT(otherNode->op() == SetLocal || otherNode->op() == SetArgument);
- break;
- default:
- break;
- }
-
- ASSERT(otherNode->op() == SetLocal || otherNode->op() == SetArgument || otherNode->op() == GetLocal);
- ASSERT(otherNode->variableAccessData() == variable);
-
- if (otherNode->op() == SetArgument) {
- variable->setIsLoadedFrom(true);
- node->children.setChild1(Edge(otherNode));
- m_block->variablesAtTail.atFor<operandKind>(idx) = node;
- return;
- }
-
- if (variable->isCaptured()) {
- variable->setIsLoadedFrom(true);
- if (otherNode->op() == GetLocal)
- otherNode = otherNode->child1().node();
- else
- ASSERT(otherNode->op() == SetLocal || otherNode->op() == SetArgument);
-
- ASSERT(otherNode->op() == Phi || otherNode->op() == SetLocal || otherNode->op() == SetArgument);
-
- // Keep this GetLocal but link it to the prior ones.
- node->children.setChild1(Edge(otherNode));
- m_block->variablesAtTail.atFor<operandKind>(idx) = node;
- return;
- }
-
- if (otherNode->op() == GetLocal) {
- // Replace all references to this GetLocal with otherNode.
- node->replacement = otherNode;
- return;
- }
-
- ASSERT(otherNode->op() == SetLocal);
- node->replacement = otherNode->child1().node();
- return;
- }
-
- variable->setIsLoadedFrom(true);
- Node* phi = addPhi<operandKind>(node->codeOrigin, variable, idx);
- node->children.setChild1(Edge(phi));
- m_block->variablesAtHead.atFor<operandKind>(idx) = phi;
- m_block->variablesAtTail.atFor<operandKind>(idx) = node;
- }
-
- void canonicalizeGetLocal(Node* node)
- {
- VariableAccessData* variable = node->variableAccessData();
- if (operandIsArgument(variable->local()))
- canonicalizeGetLocalFor<ArgumentOperand>(node, variable, operandToArgument(variable->local()));
- else
- canonicalizeGetLocalFor<LocalOperand>(node, variable, variable->local());
- }
-
- void canonicalizeSetLocal(Node* node)
- {
- m_block->variablesAtTail.setOperand(node->local(), node);
- }
-
- template<NodeType nodeType, OperandKind operandKind>
- void canonicalizeFlushOrPhantomLocalFor(Node* node, VariableAccessData* variable, size_t idx)
- {
- ASSERT(!node->child1());
-
- if (Node* otherNode = m_block->variablesAtTail.atFor<operandKind>(idx)) {
- ASSERT(otherNode->variableAccessData() == variable);
-
- switch (otherNode->op()) {
- case Flush:
- case PhantomLocal:
- case GetLocal:
- otherNode = otherNode->child1().node();
- break;
- default:
- break;
- }
-
- ASSERT(otherNode->op() == Phi || otherNode->op() == SetLocal || otherNode->op() == SetArgument);
-
- if (nodeType == PhantomLocal && otherNode->op() == SetLocal) {
- // PhantomLocal(SetLocal) doesn't make sense. PhantomLocal means: at this
- // point I know I would have been interested in the value of this variable
- // for the purpose of OSR. PhantomLocal(SetLocal) means: at this point I
- // know that I would have read the value written by that SetLocal. This is
- // redundant and inefficient, since really it just means that we want to
- // be keeping the operand to the SetLocal alive. The SetLocal may die, and
- // we'll be fine because OSR tracks dead SetLocals.
-
- // So we turn this into a Phantom on the child of the SetLocal.
-
- node->convertToPhantom();
- node->children.setChild1(otherNode->child1());
- return;
- }
-
- variable->setIsLoadedFrom(true);
- // There is nothing wrong with having redundant Flush's. It just needs to
- // be linked appropriately. Note that if there had already been a previous
- // use at tail then we don't override it. It's fine for variablesAtTail to
- // omit Flushes and PhantomLocals. On the other hand, having it refer to a
- // Flush or a PhantomLocal if just before it the last use was a GetLocal would
- // seriously confuse the CFA.
- node->children.setChild1(Edge(otherNode));
- return;
- }
-
- variable->setIsLoadedFrom(true);
- node->children.setChild1(Edge(addPhi<operandKind>(node->codeOrigin, variable, idx)));
- m_block->variablesAtHead.atFor<operandKind>(idx) = node;
- m_block->variablesAtTail.atFor<operandKind>(idx) = node;
- }
- template<NodeType nodeType>
- void canonicalizeFlushOrPhantomLocal(Node* node)
- {
- VariableAccessData* variable = node->variableAccessData();
- if (operandIsArgument(variable->local()))
- canonicalizeFlushOrPhantomLocalFor<nodeType, ArgumentOperand>(node, variable, operandToArgument(variable->local()));
- else
- canonicalizeFlushOrPhantomLocalFor<nodeType, LocalOperand>(node, variable, variable->local());
- }
-
- void canonicalizeSetArgument(Node* node)
- {
- int local = node->local();
- ASSERT(operandIsArgument(local));
- int argument = operandToArgument(local);
- m_block->variablesAtHead.setArgumentFirstTime(argument, node);
- m_block->variablesAtTail.setArgumentFirstTime(argument, node);
- }
-
- void canonicalizeLocalsInBlock()
- {
- if (!m_block)
- return;
- ASSERT(m_block->isReachable);
-
- clearVariablesAtHeadAndTail<ArgumentOperand>();
- clearVariablesAtHeadAndTail<LocalOperand>();
-
- // Assumes that all phi references have been removed. Assumes that things that
- // should be live have a non-zero ref count, but doesn't assume that the ref
- // counts are correct beyond that (more formally !!logicalRefCount == !!actualRefCount
- // but not logicalRefCount == actualRefCount). Assumes that it can break ref
- // counts.
-
- for (unsigned nodeIndex = 0; nodeIndex < m_block->size(); ++nodeIndex) {
- Node* node = m_block->at(nodeIndex);
-
- m_graph.performSubstitution(node);
-
- // The rules for threaded CPS form:
- //
- // Head variable: describes what is live at the head of the basic block.
- // Head variable links may refer to Flush, PhantomLocal, Phi, or SetArgument.
- // SetArgument may only appear in the root block.
- //
- // Tail variable: the last thing that happened to the variable in the block.
- // It may be a Flush, PhantomLocal, GetLocal, SetLocal, or SetArgument.
- // SetArgument may only appear in the root block. Note that if there ever
- // was a GetLocal to the variable, and it was followed by PhantomLocals and
- // Flushes but not SetLocals, then the tail variable will be the GetLocal.
- // This reflects the fact that you only care that the tail variable is a
- // Flush or PhantomLocal if nothing else interesting happened.
- //
- // Child of GetLocal: the operation that the GetLocal keeps alive. For
- // uncaptured locals, it may be a Phi from the current block. For arguments,
- // it may be a SetArgument. For captured locals and arguments it may also be
- // a SetLocal.
- //
- // Child of SetLocal: must be a value producing node.
- //
- // Child of Flush: it may be a Phi from the current block or a SetLocal. For
- // arguments it may also be a SetArgument.
- //
- // Child of PhantomLocal: it may be a Phi from the current block. For
- // arguments it may also be a SetArgument.
- //
- // Children of Phi: other Phis in the same basic block, or any of the
- // following from predecessor blocks: SetLocal, Phi, or SetArgument. These
- // are computed by looking at the tail variables of the predecessor blocks
- // and either using it directly (if it's a SetLocal, Phi, or SetArgument) or
- // loading that nodes child (if it's a GetLocal, PhanomLocal, or Flush - all
- // of these will have children that are SetLocal, Phi, or SetArgument).
-
- switch (node->op()) {
- case GetLocal:
- canonicalizeGetLocal(node);
- break;
-
- case SetLocal:
- canonicalizeSetLocal(node);
- break;
-
- case Flush:
- canonicalizeFlushOrPhantomLocal<Flush>(node);
- break;
-
- case PhantomLocal:
- canonicalizeFlushOrPhantomLocal<PhantomLocal>(node);
- break;
-
- case SetArgument:
- canonicalizeSetArgument(node);
- break;
-
- default:
- break;
- }
- }
- }
-
- void canonicalizeLocalsInBlocks()
- {
- SamplingRegion samplingRegion("DFG CPS Rethreading: canonicalizeLocalsInBlocks");
-
- for (m_blockIndex = m_graph.m_blocks.size(); m_blockIndex--;) {
- m_block = m_graph.m_blocks[m_blockIndex].get();
- canonicalizeLocalsInBlock();
- }
- }
-
- template<OperandKind operandKind>
- void propagatePhis()
- {
- Vector<PhiStackEntry, 128>& phiStack = operandKind == ArgumentOperand ? m_argumentPhiStack : m_localPhiStack;
-
- SamplingRegion samplingRegion("DFG CPS Rethreading: propagatePhis");
-
- // Ensure that attempts to use this fail instantly.
- m_block = 0;
- m_blockIndex = NoBlock;
-
- while (!phiStack.isEmpty()) {
- PhiStackEntry entry = phiStack.last();
- phiStack.removeLast();
-
- BasicBlock* block = entry.m_block;
- PredecessorList& predecessors = block->m_predecessors;
- Node* currentPhi = entry.m_phi;
- VariableAccessData* variable = currentPhi->variableAccessData();
- size_t index = entry.m_index;
-
- for (size_t i = predecessors.size(); i--;) {
- BasicBlock* predecessorBlock = m_graph.m_blocks[predecessors[i]].get();
-
- Node* variableInPrevious = predecessorBlock->variablesAtTail.atFor<operandKind>(index);
- if (!variableInPrevious) {
- variableInPrevious = addPhi<operandKind>(predecessorBlock, currentPhi->codeOrigin, variable, index);
- predecessorBlock->variablesAtTail.atFor<operandKind>(index) = variableInPrevious;
- predecessorBlock->variablesAtHead.atFor<operandKind>(index) = variableInPrevious;
- } else {
- switch (variableInPrevious->op()) {
- case GetLocal:
- case PhantomLocal:
- case Flush:
- ASSERT(variableInPrevious->variableAccessData() == variableInPrevious->child1()->variableAccessData());
- variableInPrevious = variableInPrevious->child1().node();
- break;
- default:
- break;
- }
- }
-
- ASSERT(
- variableInPrevious->op() == SetLocal
- || variableInPrevious->op() == Phi
- || variableInPrevious->op() == SetArgument);
-
- if (!currentPhi->child1()) {
- currentPhi->children.setChild1(Edge(variableInPrevious));
- continue;
- }
- if (!currentPhi->child2()) {
- currentPhi->children.setChild2(Edge(variableInPrevious));
- continue;
- }
- if (!currentPhi->child3()) {
- currentPhi->children.setChild3(Edge(variableInPrevious));
- continue;
- }
-
- Node* newPhi = addPhiSilently(block, currentPhi->codeOrigin, variable);
- newPhi->children = currentPhi->children;
- currentPhi->children.initialize(newPhi, variableInPrevious, 0);
- }
- }
- }
-
- struct PhiStackEntry {
- PhiStackEntry(BasicBlock* block, size_t index, Node* phi)
- : m_block(block)
- , m_index(index)
- , m_phi(phi)
- {
- }
-
- BasicBlock* m_block;
- size_t m_index;
- Node* m_phi;
- };
-
- template<OperandKind operandKind>
- Vector<PhiStackEntry, 128>& phiStackFor()
- {
- if (operandKind == ArgumentOperand)
- return m_argumentPhiStack;
- return m_localPhiStack;
- }
-
- BlockIndex m_blockIndex;
- BasicBlock* m_block;
- Vector<PhiStackEntry, 128> m_argumentPhiStack;
- Vector<PhiStackEntry, 128> m_localPhiStack;
- };
- bool performCPSRethreading(Graph& graph)
- {
- SamplingRegion samplingRegion("DFG CPS Rethreading Phase");
- return runPhase<CPSRethreadingPhase>(graph);
- }
- } } // namespace JSC::DFG
- #endif // ENABLE(DFG_JIT)
|