/* * 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 "DFGFlushLivenessAnalysisPhase.h" #if ENABLE(DFG_JIT) #include "DFGBasicBlockInlines.h" #include "DFGGraph.h" #include "DFGInsertionSet.h" #include "DFGPhase.h" #include "OperandsInlines.h" #include "Operations.h" namespace JSC { namespace DFG { class FlushLivenessAnalysisPhase : public Phase { public: FlushLivenessAnalysisPhase(Graph& graph) : Phase(graph, "flush-liveness analysis") { } bool run() { ASSERT(m_graph.m_form == SSA); // Liveness is a backwards analysis; the roots are the blocks that // end in a terminal (Return/Unreachable). For now, we // use a fixpoint formulation since liveness is a rapid analysis with // convergence guaranteed after O(connectivity). // Start by assuming that everything is dead. for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) { BasicBlock* block = m_graph.block(blockIndex); if (!block) continue; block->ssa->flushAtHead.fill(FlushedAt()); block->ssa->flushAtTail.fill(FlushedAt()); } do { m_changed = false; for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) process(blockIndex); } while (m_changed); Operands& root = m_graph.block(0)->ssa->flushAtHead; for (unsigned i = root.size(); i--;) { if (root.isArgument(i)) { if (!root[i] || root[i] == FlushedAt(FlushedJSValue, VirtualRegister(root.operandForIndex(i)))) continue; } else { if (!root[i]) continue; } dataLog( "Bad flush liveness analysis result: bad flush liveness at root: ", root, "\n"); dataLog("IR at time of error:\n"); m_graph.dump(); CRASH(); } return true; } private: void process(BlockIndex blockIndex) { BasicBlock* block = m_graph.block(blockIndex); if (!block) return; m_live = block->ssa->flushAtTail; for (unsigned nodeIndex = block->size(); nodeIndex--;) { Node* node = block->at(nodeIndex); switch (node->op()) { case SetLocal: { VariableAccessData* variable = node->variableAccessData(); FlushedAt& current = m_live.operand(variable->local()); if (!!current && current != variable->flushedAt()) reportError(node); current = FlushedAt(); break; } case GetArgument: { VariableAccessData* variable = node->variableAccessData(); ASSERT(variable->local() == variable->machineLocal()); ASSERT(variable->local().isArgument()); FlushedAt& current = m_live.operand(variable->local()); if (!!current && current != variable->flushedAt()) reportError(node); current = FlushedAt(FlushedJSValue, node->local()); break; } case Flush: case GetLocal: { VariableAccessData* variable = node->variableAccessData(); FlushedAt& current = m_live.operand(variable->local()); if (!!current && current != variable->flushedAt()) reportError(node); current = variable->flushedAt(); break; } default: break; } } if (m_live == block->ssa->flushAtHead) return; m_changed = true; block->ssa->flushAtHead = m_live; for (unsigned i = block->predecessors.size(); i--;) { BasicBlock* predecessor = block->predecessors[i]; for (unsigned j = m_live.size(); j--;) { FlushedAt& predecessorFlush = predecessor->ssa->flushAtTail[j]; FlushedAt myFlush = m_live[j]; // Three possibilities: // 1) Predecessor format is Dead, in which case it acquires our format. // 2) Predecessor format is not Dead but our format is dead, in which // case we acquire the predecessor format. // 3) Predecessor format is identical to our format, in which case we // do nothing. // 4) Predecessor format is different from our format and it's not Dead, // in which case we have an erroneous set of Flushes and SetLocals. if (!predecessorFlush) { predecessorFlush = myFlush; continue; } if (!myFlush) { m_live[j] = predecessorFlush; continue; } if (predecessorFlush == myFlush) continue; dataLog( "Bad Flush merge at edge ", *predecessor, " -> ", *block, ", local variable r", m_live.operandForIndex(j), ": ", *predecessor, " has ", predecessorFlush, " and ", *block, " has ", myFlush, ".\n"); dataLog("IR at time of error:\n"); m_graph.dump(); CRASH(); } } } NO_RETURN_DUE_TO_CRASH void reportError(Node* node) { dataLog( "Bad flush merge at node ", node, ", r", node->local(), ": node claims ", node->variableAccessData()->flushedAt(), " but backwards flow claims ", m_live.operand(node->local()), ".\n"); dataLog("IR at time of error:\n"); m_graph.dump(); CRASH(); } bool m_changed; Operands m_live; }; bool performFlushLivenessAnalysis(Graph& graph) { SamplingRegion samplingRegion("DFG Flush-Liveness Analysis Phase"); return runPhase(graph); } } } // namespace JSC::DFG #endif // ENABLE(DFG_JIT)