| /* |
| * Copyright (C) 2012 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 "DFGValidate.h" |
| |
| #if ENABLE(DFG_JIT) |
| |
| #include <wtf/Assertions.h> |
| #include <wtf/BitVector.h> |
| |
| namespace JSC { namespace DFG { |
| |
| #if DFG_ENABLE(VALIDATION) |
| |
| class Validate { |
| public: |
| Validate(Graph& graph, GraphDumpMode graphDumpMode) |
| : m_graph(graph) |
| , m_graphDumpMode(graphDumpMode) |
| { |
| } |
| |
| #define VALIDATE(context, assertion) do { \ |
| if (!(assertion)) { \ |
| dataLog("\n\n\nAt "); \ |
| reportValidationContext context; \ |
| dataLog(": validation %s (%s:%d) failed.\n", #assertion, __FILE__, __LINE__); \ |
| dumpGraphIfAppropriate(); \ |
| WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion); \ |
| CRASH(); \ |
| } \ |
| } while (0) |
| |
| #define V_EQUAL(context, left, right) do { \ |
| if (left != right) { \ |
| dataLog("\n\n\nAt "); \ |
| reportValidationContext context; \ |
| dataLog(": validation (%s = ", #left); \ |
| dumpData(left); \ |
| dataLog(") == (%s = ", #right); \ |
| dumpData(right); \ |
| dataLog(") (%s:%d) failed.\n", __FILE__, __LINE__); \ |
| dumpGraphIfAppropriate(); \ |
| WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #left " == " #right); \ |
| CRASH(); \ |
| } \ |
| } while (0) |
| |
| #define notSet (static_cast<size_t>(-1)) |
| |
| void validate() |
| { |
| // NB. This code is not written for performance, since it is not intended to run |
| // in release builds. |
| |
| // Validate ref counts and uses. |
| Vector<unsigned> myRefCounts; |
| myRefCounts.fill(0, m_graph.size()); |
| BitVector acceptableNodeIndices; |
| for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { |
| BasicBlock* block = m_graph.m_blocks[blockIndex].get(); |
| if (!block) |
| continue; |
| if (!block->isReachable) |
| continue; |
| for (size_t i = 0; i < block->numNodes(); ++i) { |
| NodeIndex nodeIndex = block->nodeIndex(i); |
| acceptableNodeIndices.set(nodeIndex); |
| Node& node = m_graph[nodeIndex]; |
| if (!node.shouldGenerate()) |
| continue; |
| for (unsigned j = 0; j < m_graph.numChildren(node); ++j) { |
| Edge edge = m_graph.child(node, j); |
| if (!edge) |
| continue; |
| |
| myRefCounts[edge.index()]++; |
| |
| // Unless I'm a Flush, Phantom, GetLocal, or Phi, my children should hasResult(). |
| switch (node.op()) { |
| case Flush: |
| case Phantom: |
| case GetLocal: |
| case Phi: |
| break; |
| default: |
| VALIDATE((nodeIndex, edge), m_graph[edge].hasResult()); |
| break; |
| } |
| } |
| } |
| } |
| for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { |
| BasicBlock* block = m_graph.m_blocks[blockIndex].get(); |
| if (!block) |
| continue; |
| if (!block->isReachable) |
| continue; |
| |
| BitVector phisInThisBlock; |
| BitVector nodesInThisBlock; |
| |
| for (size_t i = 0; i < block->numNodes(); ++i) { |
| NodeIndex nodeIndex = block->nodeIndex(i); |
| Node& node = m_graph[nodeIndex]; |
| nodesInThisBlock.set(nodeIndex); |
| if (block->isPhiIndex(i)) |
| phisInThisBlock.set(nodeIndex); |
| V_EQUAL((nodeIndex), myRefCounts[nodeIndex], node.adjustedRefCount()); |
| for (unsigned j = 0; j < m_graph.numChildren(node); ++j) { |
| Edge edge = m_graph.child(node, j); |
| if (!edge) |
| continue; |
| VALIDATE((nodeIndex, edge), acceptableNodeIndices.get(edge.index())); |
| } |
| } |
| |
| for (size_t i = 0; i < block->phis.size(); ++i) { |
| NodeIndex nodeIndex = block->phis[i]; |
| Node& node = m_graph[nodeIndex]; |
| ASSERT(phisInThisBlock.get(nodeIndex)); |
| VALIDATE((nodeIndex), node.op() == Phi); |
| VirtualRegister local = node.local(); |
| for (unsigned j = 0; j < m_graph.numChildren(node); ++j) { |
| Edge edge = m_graph.child(node, j); |
| if (!edge) |
| continue; |
| |
| VALIDATE((nodeIndex, edge), |
| m_graph[edge].op() == SetLocal |
| || m_graph[edge].op() == SetArgument |
| || m_graph[edge].op() == Flush |
| || m_graph[edge].op() == Phi); |
| |
| if (phisInThisBlock.get(edge.index())) |
| continue; |
| |
| if (nodesInThisBlock.get(edge.index())) { |
| VALIDATE((nodeIndex, edge), |
| m_graph[edge].op() == SetLocal |
| || m_graph[edge].op() == SetArgument |
| || m_graph[edge].op() == Flush); |
| |
| continue; |
| } |
| |
| // There must exist a predecessor block that has this node index in |
| // its tail variables. |
| bool found = false; |
| for (unsigned k = 0; k < block->m_predecessors.size(); ++k) { |
| BasicBlock* prevBlock = m_graph.m_blocks[block->m_predecessors[k]].get(); |
| VALIDATE((Block, block->m_predecessors[k]), prevBlock); |
| VALIDATE((Block, block->m_predecessors[k]), prevBlock->isReachable); |
| NodeIndex prevNodeIndex = prevBlock->variablesAtTail.operand(local); |
| // If we have a Phi that is not referring to *this* block then all predecessors |
| // must have that local available. |
| VALIDATE((local, blockIndex, Block, block->m_predecessors[k]), prevNodeIndex != NoNode); |
| Node* prevNode = &m_graph[prevNodeIndex]; |
| if (prevNode->op() == GetLocal) { |
| prevNodeIndex = prevNode->child1().index(); |
| prevNode = &m_graph[prevNodeIndex]; |
| } |
| if (node.shouldGenerate()) { |
| VALIDATE((local, block->m_predecessors[k], prevNodeIndex), |
| prevNode->shouldGenerate()); |
| } |
| VALIDATE((local, block->m_predecessors[k], prevNodeIndex), |
| prevNode->op() == SetLocal |
| || prevNode->op() == SetArgument |
| || prevNode->op() == Flush |
| || prevNode->op() == Phi); |
| if (prevNodeIndex == edge.index()) { |
| found = true; |
| break; |
| } |
| // At this point it cannot refer into this block. |
| VALIDATE((local, block->m_predecessors[k], prevNodeIndex), !prevBlock->isInBlock(edge.index())); |
| } |
| |
| VALIDATE((nodeIndex, edge), found); |
| } |
| } |
| |
| Operands<size_t> getLocalPositions( |
| block->variablesAtHead.numberOfArguments(), |
| block->variablesAtHead.numberOfLocals()); |
| Operands<size_t> setLocalPositions( |
| block->variablesAtHead.numberOfArguments(), |
| block->variablesAtHead.numberOfLocals()); |
| |
| for (size_t i = 0; i < block->variablesAtHead.numberOfArguments(); ++i) { |
| getLocalPositions.argument(i) = notSet; |
| setLocalPositions.argument(i) = notSet; |
| } |
| for (size_t i = 0; i < block->variablesAtHead.numberOfLocals(); ++i) { |
| getLocalPositions.local(i) = notSet; |
| setLocalPositions.local(i) = notSet; |
| } |
| |
| for (size_t i = 0; i < block->size(); ++i) { |
| NodeIndex nodeIndex = block->at(i); |
| Node& node = m_graph[nodeIndex]; |
| ASSERT(nodesInThisBlock.get(nodeIndex)); |
| VALIDATE((nodeIndex), node.op() != Phi); |
| for (unsigned j = 0; j < m_graph.numChildren(node); ++j) { |
| Edge edge = m_graph.child(node, j); |
| if (!edge) |
| continue; |
| VALIDATE((nodeIndex, edge), nodesInThisBlock.get(nodeIndex)); |
| } |
| |
| if (!node.shouldGenerate()) |
| continue; |
| switch (node.op()) { |
| case GetLocal: |
| if (node.variableAccessData()->isCaptured()) |
| break; |
| VALIDATE((nodeIndex, blockIndex), getLocalPositions.operand(node.local()) == notSet); |
| getLocalPositions.operand(node.local()) = i; |
| break; |
| case SetLocal: |
| if (node.variableAccessData()->isCaptured()) |
| break; |
| // Only record the first SetLocal. There may be multiple SetLocals |
| // because of flushing. |
| if (setLocalPositions.operand(node.local()) != notSet) |
| break; |
| setLocalPositions.operand(node.local()) = i; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| for (size_t i = 0; i < block->variablesAtHead.numberOfArguments(); ++i) { |
| checkOperand( |
| blockIndex, getLocalPositions, setLocalPositions, argumentToOperand(i)); |
| } |
| for (size_t i = 0; i < block->variablesAtHead.numberOfLocals(); ++i) { |
| checkOperand( |
| blockIndex, getLocalPositions, setLocalPositions, i); |
| } |
| } |
| } |
| |
| private: |
| Graph& m_graph; |
| GraphDumpMode m_graphDumpMode; |
| |
| void checkOperand( |
| BlockIndex blockIndex, Operands<size_t>& getLocalPositions, |
| Operands<size_t>& setLocalPositions, int operand) |
| { |
| if (getLocalPositions.operand(operand) == notSet) |
| return; |
| if (setLocalPositions.operand(operand) == notSet) |
| return; |
| |
| BasicBlock* block = m_graph.m_blocks[blockIndex].get(); |
| |
| VALIDATE( |
| (block->at(getLocalPositions.operand(operand)), |
| block->at(setLocalPositions.operand(operand)), |
| blockIndex), |
| getLocalPositions.operand(operand) < setLocalPositions.operand(operand)); |
| } |
| |
| void reportValidationContext(NodeIndex nodeIndex) |
| { |
| dataLog("@%u", nodeIndex); |
| } |
| |
| enum BlockTag { Block }; |
| void reportValidationContext(BlockTag, BlockIndex blockIndex) |
| { |
| dataLog("Block #%u", blockIndex); |
| } |
| |
| void reportValidationContext(NodeIndex nodeIndex, Edge edge) |
| { |
| dataLog("@%u -> %s@%u", nodeIndex, useKindToString(edge.useKind()), edge.index()); |
| } |
| |
| void reportValidationContext( |
| VirtualRegister local, BlockIndex sourceBlockIndex, BlockTag, BlockIndex destinationBlockIndex) |
| { |
| dataLog("r%d in Block #%u -> #%u", local, sourceBlockIndex, destinationBlockIndex); |
| } |
| |
| void reportValidationContext( |
| VirtualRegister local, BlockIndex sourceBlockIndex, NodeIndex prevNodeIndex) |
| { |
| dataLog("@%u for r%d in Block #%u", prevNodeIndex, local, sourceBlockIndex); |
| } |
| |
| void reportValidationContext( |
| NodeIndex nodeIndex, BlockIndex blockIndex) |
| { |
| dataLog("@%u in Block #%u", nodeIndex, blockIndex); |
| } |
| |
| void reportValidationContext( |
| NodeIndex nodeIndex, NodeIndex nodeIndex2, BlockIndex blockIndex) |
| { |
| dataLog("@%u and @%u in Block #%u", nodeIndex, nodeIndex2, blockIndex); |
| } |
| |
| void reportValidationContext( |
| NodeIndex nodeIndex, BlockIndex blockIndex, NodeIndex expectedNodeIndex, Edge incomingEdge) |
| { |
| dataLog("@%u in Block #%u, searching for @%u from @%u", nodeIndex, blockIndex, expectedNodeIndex, incomingEdge.index()); |
| } |
| |
| void dumpData(unsigned value) |
| { |
| dataLog("%u", value); |
| } |
| |
| void dumpGraphIfAppropriate() |
| { |
| if (m_graphDumpMode == DontDumpGraph) |
| return; |
| dataLog("Graph at time of failure:\n"); |
| m_graph.dump(); |
| } |
| }; |
| |
| void validate(Graph& graph, GraphDumpMode graphDumpMode) |
| { |
| Validate validationObject(graph, graphDumpMode); |
| validationObject.validate(); |
| } |
| |
| #endif // DFG_ENABLE(VALIDATION) |
| |
| } } // namespace JSC::DFG |
| |
| #endif // ENABLE(DFG_JIT) |
| |