| /* |
| * 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<FlushedAt>& 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<FlushedAt> m_live; |
| }; |
| |
| bool performFlushLivenessAnalysis(Graph& graph) |
| { |
| SamplingRegion samplingRegion("DFG Flush-Liveness Analysis Phase"); |
| return runPhase<FlushLivenessAnalysisPhase>(graph); |
| } |
| |
| } } // namespace JSC::DFG |
| |
| #endif // ENABLE(DFG_JIT) |
| |