summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/dfg/DFGValidate.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/JavaScriptCore/dfg/DFGValidate.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGValidate.cpp')
-rw-r--r--Source/JavaScriptCore/dfg/DFGValidate.cpp459
1 files changed, 395 insertions, 64 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGValidate.cpp b/Source/JavaScriptCore/dfg/DFGValidate.cpp
index 09bea406f..1bfd3a8ef 100644
--- a/Source/JavaScriptCore/dfg/DFGValidate.cpp
+++ b/Source/JavaScriptCore/dfg/DFGValidate.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -29,24 +29,31 @@
#if ENABLE(DFG_JIT)
#include "CodeBlockWithJITType.h"
+#include "DFGClobberize.h"
+#include "DFGClobbersExitState.h"
+#include "DFGMayExit.h"
+#include "JSCInlines.h"
#include <wtf/Assertions.h>
-#include <wtf/BitVector.h>
namespace JSC { namespace DFG {
+namespace {
+
class Validate {
public:
- Validate(Graph& graph, GraphDumpMode graphDumpMode)
+ Validate(Graph& graph, GraphDumpMode graphDumpMode, CString graphDumpBeforePhase)
: m_graph(graph)
, m_graphDumpMode(graphDumpMode)
+ , m_graphDumpBeforePhase(graphDumpBeforePhase)
{
}
#define VALIDATE(context, assertion) do { \
if (!(assertion)) { \
+ startCrashing(); \
dataLogF("\n\n\nAt "); \
reportValidationContext context; \
- dataLogF(": validation %s (%s:%d) failed.\n", #assertion, __FILE__, __LINE__); \
+ dataLogF(": validation failed: %s (%s:%d).\n", #assertion, __FILE__, __LINE__); \
dumpGraphIfAppropriate(); \
WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion); \
CRASH(); \
@@ -55,13 +62,14 @@ public:
#define V_EQUAL(context, left, right) do { \
if (left != right) { \
+ startCrashing(); \
dataLogF("\n\n\nAt "); \
reportValidationContext context; \
- dataLogF(": validation (%s = ", #left); \
+ dataLogF(": validation failed: (%s = ", #left); \
dataLog(left); \
dataLogF(") == (%s = ", #right); \
dataLog(right); \
- dataLogF(") (%s:%d) failed.\n", __FILE__, __LINE__); \
+ dataLogF(") (%s:%d).\n", __FILE__, __LINE__); \
dumpGraphIfAppropriate(); \
WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #left " == " #right); \
CRASH(); \
@@ -69,7 +77,7 @@ public:
} 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
@@ -113,6 +121,9 @@ public:
continue;
m_myRefCounts.find(edge.node())->value++;
+
+ validateEdgeWithDoubleResultIfNecessary(node, edge);
+ VALIDATE((node, edge), edge->hasInt52Result() == (edge.useKind() == Int52RepUse));
if (m_graph.m_form == SSA) {
// In SSA, all edges must hasResult().
@@ -138,31 +149,6 @@ public:
break;
VALIDATE((node, edge), edge->variableAccessData() == node->variableAccessData());
break;
- case Phantom:
- switch (m_graph.m_form) {
- case LoadStore:
- if (j) {
- VALIDATE((node, edge), edge->hasResult());
- break;
- }
- switch (edge->op()) {
- case Phi:
- case SetArgument:
- case SetLocal:
- break;
- default:
- VALIDATE((node, edge), edge->hasResult());
- break;
- }
- break;
- case ThreadedCPS:
- VALIDATE((node, edge), edge->hasResult());
- break;
- case SSA:
- RELEASE_ASSERT_NOT_REACHED();
- break;
- }
- break;
default:
VALIDATE((node, edge), edge->hasResult());
break;
@@ -179,8 +165,157 @@ public:
Node* node = block->node(i);
if (m_graph.m_refCountState == ExactRefCount)
V_EQUAL((node), m_myRefCounts.get(node), node->adjustedRefCount());
- else
- V_EQUAL((node), node->refCount(), 1);
+ }
+
+ bool foundTerminal = false;
+ for (size_t i = 0 ; i < block->size(); ++i) {
+ Node* node = block->at(i);
+ if (node->isTerminal()) {
+ foundTerminal = true;
+ for (size_t j = i + 1; j < block->size(); ++j) {
+ node = block->at(j);
+ VALIDATE((node), node->op() == Phantom || node->op() == PhantomLocal || node->op() == Flush || node->op() == Check);
+ m_graph.doToChildren(
+ node,
+ [&] (Edge edge) {
+ VALIDATE((node, edge), shouldNotHaveTypeCheck(edge.useKind()));
+ });
+ }
+ break;
+ }
+ }
+ VALIDATE((block), foundTerminal);
+
+ for (size_t i = 0; i < block->size(); ++i) {
+ Node* node = block->at(i);
+
+ VALIDATE((node), node->origin.isSet());
+ VALIDATE((node), node->origin.semantic.isSet() == node->origin.forExit.isSet());
+ VALIDATE((node), !(!node->origin.forExit.isSet() && node->origin.exitOK));
+ VALIDATE((node), !(mayExit(m_graph, node) == Exits && !node->origin.exitOK));
+
+ if (i) {
+ Node* previousNode = block->at(i - 1);
+ VALIDATE(
+ (node),
+ !clobbersExitState(m_graph, previousNode)
+ || !node->origin.exitOK
+ || node->op() == ExitOK
+ || node->origin.forExit != previousNode->origin.forExit);
+ VALIDATE(
+ (node),
+ !(!previousNode->origin.exitOK && node->origin.exitOK)
+ || node->op() == ExitOK
+ || node->origin.forExit != previousNode->origin.forExit);
+ }
+
+ VALIDATE((node), !node->hasStructure() || !!node->structure().get());
+ VALIDATE((node), !node->hasCellOperand() || node->cellOperand()->value().isCell());
+ VALIDATE((node), !node->hasCellOperand() || !!node->cellOperand()->value());
+
+ if (!(node->flags() & NodeHasVarArgs)) {
+ if (!node->child2())
+ VALIDATE((node), !node->child3());
+ if (!node->child1())
+ VALIDATE((node), !node->child2());
+ }
+
+ switch (node->op()) {
+ case Identity:
+ VALIDATE((node), canonicalResultRepresentation(node->result()) == canonicalResultRepresentation(node->child1()->result()));
+ break;
+ case SetLocal:
+ case PutStack:
+ case Upsilon:
+ VALIDATE((node), !!node->child1());
+ switch (node->child1().useKind()) {
+ case UntypedUse:
+ case CellUse:
+ case KnownCellUse:
+ case Int32Use:
+ case KnownInt32Use:
+ case Int52RepUse:
+ case DoubleRepUse:
+ case BooleanUse:
+ case KnownBooleanUse:
+ break;
+ default:
+ VALIDATE((node), !"Bad use kind");
+ break;
+ }
+ break;
+ case MakeRope:
+ case ValueAdd:
+ case ArithAdd:
+ case ArithSub:
+ case ArithMul:
+ case ArithIMul:
+ case ArithDiv:
+ case ArithMod:
+ case ArithMin:
+ case ArithMax:
+ case ArithPow:
+ case CompareLess:
+ case CompareLessEq:
+ case CompareGreater:
+ case CompareGreaterEq:
+ case CompareEq:
+ case CompareStrictEq:
+ case StrCat:
+ VALIDATE((node), !!node->child1());
+ VALIDATE((node), !!node->child2());
+ break;
+ case CompareEqPtr:
+ VALIDATE((node), !!node->child1());
+ VALIDATE((node), !!node->cellOperand()->value() && node->cellOperand()->value().isCell());
+ break;
+ case CheckStructure:
+ case StringFromCharCode:
+ VALIDATE((node), !!node->child1());
+ break;
+ case PutStructure:
+ VALIDATE((node), !node->transition()->previous->dfgShouldWatch());
+ break;
+ case MultiPutByOffset:
+ for (unsigned i = node->multiPutByOffsetData().variants.size(); i--;) {
+ const PutByIdVariant& variant = node->multiPutByOffsetData().variants[i];
+ if (variant.kind() != PutByIdVariant::Transition)
+ continue;
+ VALIDATE((node), !variant.oldStructureForTransition()->dfgShouldWatch());
+ }
+ break;
+ case MaterializeNewObject:
+ for (RegisteredStructure structure : node->structureSet()) {
+ // This only supports structures that are JSFinalObject or JSArray.
+ VALIDATE(
+ (node),
+ structure->classInfo() == JSFinalObject::info()
+ || structure->classInfo() == JSArray::info());
+
+ // We only support certain indexing shapes.
+ VALIDATE((node), !hasAnyArrayStorage(structure->indexingType()));
+ }
+ break;
+ case DoubleConstant:
+ case Int52Constant:
+ VALIDATE((node), node->isNumberConstant());
+ break;
+ case GetByOffset:
+ case PutByOffset:
+ // FIXME: We should be able to validate that GetByOffset and PutByOffset are
+ // using the same object for storage and base. I think this means finally
+ // splitting these nodes into two node types, one for inline and one for
+ // out-of-line. The out-of-line one will require that the first node is storage,
+ // while the inline one will not take a storage child at all.
+ // https://bugs.webkit.org/show_bug.cgi?id=159602
+ break;
+ case HasOwnProperty: {
+ VALIDATE((node), !!m_graph.m_vm.hasOwnPropertyCache());
+ break;
+ }
+ default:
+ break;
+ }
}
}
@@ -194,11 +329,53 @@ public:
validateSSA();
break;
}
+
+ // Validate clobbered states.
+ struct DefLambdaAdaptor {
+ std::function<void(PureValue)> pureValue;
+ std::function<void(HeapLocation, LazyNode)> locationAndNode;
+
+ void operator()(PureValue value) const
+ {
+ pureValue(value);
+ }
+
+ void operator()(HeapLocation location, LazyNode node) const
+ {
+ locationAndNode(location, node);
+ }
+ };
+ for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
+ for (Node* node : *block) {
+ clobberize(m_graph, node,
+ [&] (AbstractHeap) { },
+ [&] (AbstractHeap heap)
+ {
+ // CSE assumes that HEAP TOP is never written.
+ // If this assumption is weakened, you need to update clobbering
+ // in CSE accordingly.
+ if (heap.kind() == Stack)
+ VALIDATE((node), !heap.payload().isTop());
+ },
+ DefLambdaAdaptor {
+ [&] (PureValue) { },
+ [&] (HeapLocation location, LazyNode)
+ {
+ VALIDATE((node), location.heap().kind() != SideState);
+
+ // More specific kinds should be used instead.
+ VALIDATE((node), location.heap().kind() != World);
+ VALIDATE((node), location.heap().kind() != Heap);
+ }
+ });
+ }
+ }
}
private:
Graph& m_graph;
GraphDumpMode m_graphDumpMode;
+ CString m_graphDumpBeforePhase;
HashMap<Node*, unsigned> m_myRefCounts;
HashSet<Node*> m_acceptableNodes;
@@ -308,17 +485,17 @@ private:
block->variablesAtHead.numberOfLocals());
for (size_t i = 0; i < block->variablesAtHead.numberOfArguments(); ++i) {
- VALIDATE((virtualRegisterForArgument(i), block), !block->variablesAtHead.argument(i) || block->variablesAtHead.argument(i)->hasVariableAccessData(m_graph));
+ VALIDATE((virtualRegisterForArgument(i), block), !block->variablesAtHead.argument(i) || block->variablesAtHead.argument(i)->accessesStack(m_graph));
if (m_graph.m_form == ThreadedCPS)
- VALIDATE((virtualRegisterForArgument(i), block), !block->variablesAtTail.argument(i) || block->variablesAtTail.argument(i)->hasVariableAccessData(m_graph));
+ VALIDATE((virtualRegisterForArgument(i), block), !block->variablesAtTail.argument(i) || block->variablesAtTail.argument(i)->accessesStack(m_graph));
getLocalPositions.argument(i) = notSet;
setLocalPositions.argument(i) = notSet;
}
for (size_t i = 0; i < block->variablesAtHead.numberOfLocals(); ++i) {
- VALIDATE((virtualRegisterForLocal(i), block), !block->variablesAtHead.local(i) || block->variablesAtHead.local(i)->hasVariableAccessData(m_graph));
+ VALIDATE((virtualRegisterForLocal(i), block), !block->variablesAtHead.local(i) || block->variablesAtHead.local(i)->accessesStack(m_graph));
if (m_graph.m_form == ThreadedCPS)
- VALIDATE((virtualRegisterForLocal(i), block), !block->variablesAtTail.local(i) || block->variablesAtTail.local(i)->hasVariableAccessData(m_graph));
+ VALIDATE((virtualRegisterForLocal(i), block), !block->variablesAtTail.local(i) || block->variablesAtTail.local(i)->accessesStack(m_graph));
getLocalPositions.local(i) = notSet;
setLocalPositions.local(i) = notSet;
@@ -328,6 +505,7 @@ private:
Node* node = block->at(i);
ASSERT(nodesInThisBlock.contains(node));
VALIDATE((node), node->op() != Phi);
+ VALIDATE((node), node->origin.forExit.isSet());
for (unsigned j = 0; j < m_graph.numChildren(node); ++j) {
Edge edge = m_graph.child(node, j);
if (!edge)
@@ -338,39 +516,95 @@ private:
case GetLocal:
case Flush:
break;
- case Phantom:
- if (m_graph.m_form == LoadStore && !j)
- break;
- FALLTHROUGH;
default:
VALIDATE((node, edge), !phisInThisBlock.contains(edge.node()));
break;
}
}
+ switch (node->op()) {
+ case Phi:
+ case Upsilon:
+ case CheckInBounds:
+ case PhantomNewObject:
+ case PhantomNewFunction:
+ case PhantomNewGeneratorFunction:
+ case PhantomNewAsyncFunction:
+ case PhantomCreateActivation:
+ case GetMyArgumentByVal:
+ case GetMyArgumentByValOutOfBounds:
+ case PutHint:
+ case CheckStructureImmediate:
+ case MaterializeCreateActivation:
+ case PutStack:
+ case KillStack:
+ case GetStack:
+ VALIDATE((node), !"unexpected node type in CPS");
+ break;
+ case MaterializeNewObject: {
+ // CPS only allows array lengths to be constant. This constraint only exists
+ // because we don't have DFG support for anything more and we don't need any
+ // other kind of support for now.
+ ObjectMaterializationData& data = node->objectMaterializationData();
+ for (unsigned i = data.m_properties.size(); i--;) {
+ PromotedLocationDescriptor descriptor = data.m_properties[i];
+ Edge edge = m_graph.varArgChild(node, 1 + i);
+ switch (descriptor.kind()) {
+ case PublicLengthPLoc:
+ case VectorLengthPLoc:
+ VALIDATE((node, edge), edge->isInt32Constant());
+ break;
+ default:
+ break;
+ }
+ }
+
+ // CPS only allows one structure.
+ VALIDATE((node), node->structureSet().size() == 1);
+
+ // CPS disallows int32 and double arrays. Those require weird type checks and
+ // conversions. They are not needed in the DFG right now. We should add support
+ // for these if the DFG ever needs it.
+ for (RegisteredStructure structure : node->structureSet()) {
+ VALIDATE((node), !hasInt32(structure->indexingType()));
+ VALIDATE((node), !hasDouble(structure->indexingType()));
+ }
+ break;
+ }
+ case Phantom:
+ VALIDATE((node), m_graph.m_fixpointState != FixpointNotConverged);
+ break;
+ default:
+ break;
+ }
+
if (!node->shouldGenerate())
continue;
switch (node->op()) {
case GetLocal:
- if (node->variableAccessData()->isCaptured())
- break;
// Ignore GetLocal's that we know to be dead, but that the graph
// doesn't yet know to be dead.
if (!m_myRefCounts.get(node))
break;
- if (m_graph.m_form == ThreadedCPS)
+ if (m_graph.m_form == ThreadedCPS) {
VALIDATE((node, block), getLocalPositions.operand(node->local()) == notSet);
+ VALIDATE((node, block), !!node->child1());
+ }
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;
+ case SetArgument:
+ // This acts like a reset. It's ok to have a second GetLocal for a local in the same
+ // block if we had a SetArgument for that local.
+ getLocalPositions.operand(node->local()) = notSet;
+ setLocalPositions.operand(node->local()) = notSet;
+ break;
default:
break;
}
@@ -400,30 +634,120 @@ private:
if (!block)
continue;
- unsigned nodeIndex = 0;
- for (; nodeIndex < block->size() && !block->at(nodeIndex)->codeOrigin.isSet(); nodeIndex++) { }
-
- VALIDATE((block), nodeIndex < block->size());
-
- for (; nodeIndex < block->size(); nodeIndex++)
- VALIDATE((block->at(nodeIndex)), block->at(nodeIndex)->codeOrigin.isSet());
+ VALIDATE((block), block->phis.isEmpty());
+
+ bool didSeeExitOK = false;
- for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
- Node* node = block->at(nodeIndex);
+ for (auto* node : *block) {
+ didSeeExitOK |= node->origin.exitOK;
switch (node->op()) {
case Phi:
- VALIDATE((node), !node->codeOrigin.isSet());
+ // Phi cannot exit, and it would be wrong to hoist anything to the Phi that could
+ // exit.
+ VALIDATE((node), !node->origin.exitOK);
+
+ // It never makes sense to have exitOK anywhere in the block before a Phi. It's only
+ // OK to exit after all Phis are done.
+ VALIDATE((node), !didSeeExitOK);
break;
+ case GetLocal:
+ case SetLocal:
+ case GetLocalUnlinked:
+ case SetArgument:
+ case Phantom:
+ VALIDATE((node), !"bad node type for SSA");
+ break;
+
default:
// FIXME: Add more things here.
// https://bugs.webkit.org/show_bug.cgi?id=123471
break;
}
+ switch (node->op()) {
+ case PhantomNewObject:
+ case PhantomNewFunction:
+ case PhantomNewGeneratorFunction:
+ case PhantomNewAsyncFunction:
+ case PhantomCreateActivation:
+ case PhantomDirectArguments:
+ case PhantomCreateRest:
+ case PhantomClonedArguments:
+ case MovHint:
+ case Upsilon:
+ case ForwardVarargs:
+ case CallForwardVarargs:
+ case TailCallForwardVarargs:
+ case TailCallForwardVarargsInlinedCaller:
+ case ConstructForwardVarargs:
+ case GetMyArgumentByVal:
+ case GetMyArgumentByValOutOfBounds:
+ break;
+
+ case Check:
+ // FIXME: This is probably not correct.
+ break;
+
+ case PutHint:
+ VALIDATE((node), node->child1()->isPhantomAllocation());
+ break;
+
+ case PhantomSpread:
+ VALIDATE((node), m_graph.m_form == SSA);
+ // We currently only support PhantomSpread over PhantomCreateRest.
+ VALIDATE((node), node->child1()->op() == PhantomCreateRest);
+ break;
+
+ case PhantomNewArrayWithSpread: {
+ VALIDATE((node), m_graph.m_form == SSA);
+ BitVector* bitVector = node->bitVector();
+ for (unsigned i = 0; i < node->numChildren(); i++) {
+ Node* child = m_graph.varArgChild(node, i).node();
+ if (bitVector->get(i)) {
+ // We currently only support PhantomSpread over PhantomCreateRest.
+ VALIDATE((node), child->op() == PhantomSpread);
+ } else
+ VALIDATE((node), !child->isPhantomAllocation());
+ }
+ break;
+ }
+
+ case NewArrayWithSpread: {
+ BitVector* bitVector = node->bitVector();
+ for (unsigned i = 0; i < node->numChildren(); i++) {
+ Node* child = m_graph.varArgChild(node, i).node();
+ if (child->isPhantomAllocation()) {
+ VALIDATE((node), bitVector->get(i));
+ VALIDATE((node), m_graph.m_form == SSA);
+ VALIDATE((node), child->op() == PhantomSpread);
+ }
+ }
+ break;
+ }
+
+ default:
+ m_graph.doToChildren(
+ node,
+ [&] (const Edge& edge) {
+ VALIDATE((node), !edge->isPhantomAllocation());
+ });
+ break;
+ }
}
}
}
-
+
+ void validateEdgeWithDoubleResultIfNecessary(Node* node, Edge edge)
+ {
+ if (!edge->hasDoubleResult())
+ return;
+
+ if (m_graph.m_planStage < PlanStage::AfterFixup)
+ return;
+
+ VALIDATE((node, edge), edge.useKind() == DoubleRepUse || edge.useKind() == DoubleRepRealUse || edge.useKind() == DoubleRepAnyIntUse);
+ }
+
void checkOperand(
BasicBlock* block, Operands<size_t>& getLocalPositions,
Operands<size_t>& setLocalPositions, VirtualRegister operand)
@@ -458,23 +782,23 @@ private:
void reportValidationContext(VirtualRegister local, BasicBlock* block)
{
if (!block) {
- dataLog("r", local, " in null Block ");
+ dataLog(local, " in null Block ");
return;
}
- dataLog("r", local, " in Block ", *block);
+ dataLog(local, " in Block ", *block);
}
void reportValidationContext(
VirtualRegister local, BasicBlock* sourceBlock, BasicBlock* destinationBlock)
{
- dataLog("r", local, " in Block ", *sourceBlock, " -> ", *destinationBlock);
+ dataLog(local, " in Block ", *sourceBlock, " -> ", *destinationBlock);
}
void reportValidationContext(
VirtualRegister local, BasicBlock* sourceBlock, Node* prevNode)
{
- dataLog(prevNode, " for r", local, " in Block ", *sourceBlock);
+ dataLog(prevNode, " for ", local, " in Block ", *sourceBlock);
}
void reportValidationContext(Node* node, BasicBlock* block)
@@ -497,14 +821,21 @@ private:
{
if (m_graphDumpMode == DontDumpGraph)
return;
+ dataLog("\n");
+ if (!m_graphDumpBeforePhase.isNull()) {
+ dataLog("Before phase:\n");
+ dataLog(m_graphDumpBeforePhase);
+ }
dataLog("At time of failure:\n");
m_graph.dump();
}
};
-void validate(Graph& graph, GraphDumpMode graphDumpMode)
+} // End anonymous namespace.
+
+void validate(Graph& graph, GraphDumpMode graphDumpMode, CString graphDumpBeforePhase)
{
- Validate validationObject(graph, graphDumpMode);
+ Validate validationObject(graph, graphDumpMode, graphDumpBeforePhase);
validationObject.validate();
}