summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/dfg/DFGValidate.cpp
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@nokia.com>2012-05-25 15:09:11 +0200
committerSimon Hausmann <simon.hausmann@nokia.com>2012-05-25 15:09:11 +0200
commita89b2ebb8e192c5e8cea21079bda2ee2c0c7dddd (patch)
treeb7abd9f49ae1d4d2e426a5883bfccd42b8e2ee12 /Source/JavaScriptCore/dfg/DFGValidate.cpp
parent8d473cf9743f1d30a16a27114e93bd5af5648d23 (diff)
downloadqtwebkit-a89b2ebb8e192c5e8cea21079bda2ee2c0c7dddd.tar.gz
Imported WebKit commit eb5c1b8fe4d4b1b90b5137433fc58a91da0e6878 (http://svn.webkit.org/repository/webkit/trunk@118516)
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGValidate.cpp')
-rw-r--r--Source/JavaScriptCore/dfg/DFGValidate.cpp362
1 files changed, 362 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGValidate.cpp b/Source/JavaScriptCore/dfg/DFGValidate.cpp
new file mode 100644
index 000000000..2b26123d8
--- /dev/null
+++ b/Source/JavaScriptCore/dfg/DFGValidate.cpp
@@ -0,0 +1,362 @@
+/*
+ * 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)
+