blob: 97bb762aff517450f8e111e5f393a001b6ff03dc [file] [log] [blame]
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/compiler/verifier.h"
#include "src/compiler/generic-algorithm.h"
#include "src/compiler/generic-node-inl.h"
#include "src/compiler/generic-node.h"
#include "src/compiler/graph-inl.h"
#include "src/compiler/graph.h"
#include "src/compiler/node.h"
#include "src/compiler/node-properties-inl.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/opcodes.h"
#include "src/compiler/operator.h"
namespace v8 {
namespace internal {
namespace compiler {
static bool IsDefUseChainLinkPresent(Node* def, Node* use) {
Node::Uses uses = def->uses();
for (Node::Uses::iterator it = uses.begin(); it != uses.end(); ++it) {
if (*it == use) return true;
}
return false;
}
static bool IsUseDefChainLinkPresent(Node* def, Node* use) {
Node::Inputs inputs = use->inputs();
for (Node::Inputs::iterator it = inputs.begin(); it != inputs.end(); ++it) {
if (*it == def) return true;
}
return false;
}
class Verifier::Visitor : public NullNodeVisitor {
public:
explicit Visitor(Zone* zone)
: reached_from_start(NodeSet::key_compare(),
NodeSet::allocator_type(zone)),
reached_from_end(NodeSet::key_compare(),
NodeSet::allocator_type(zone)) {}
// Fulfills the PreNodeCallback interface.
GenericGraphVisit::Control Pre(Node* node);
bool from_start;
NodeSet reached_from_start;
NodeSet reached_from_end;
};
GenericGraphVisit::Control Verifier::Visitor::Pre(Node* node) {
int value_count = OperatorProperties::GetValueInputCount(node->op());
int context_count = OperatorProperties::GetContextInputCount(node->op());
int effect_count = OperatorProperties::GetEffectInputCount(node->op());
int control_count = OperatorProperties::GetControlInputCount(node->op());
// Verify number of inputs matches up.
int input_count = value_count + context_count + effect_count + control_count;
CHECK_EQ(input_count, node->InputCount());
// Verify all value inputs actually produce a value.
for (int i = 0; i < value_count; ++i) {
Node* value = NodeProperties::GetValueInput(node, i);
CHECK(OperatorProperties::HasValueOutput(value->op()));
CHECK(IsDefUseChainLinkPresent(value, node));
CHECK(IsUseDefChainLinkPresent(value, node));
}
// Verify all context inputs are value nodes.
for (int i = 0; i < context_count; ++i) {
Node* context = NodeProperties::GetContextInput(node);
CHECK(OperatorProperties::HasValueOutput(context->op()));
CHECK(IsDefUseChainLinkPresent(context, node));
CHECK(IsUseDefChainLinkPresent(context, node));
}
// Verify all effect inputs actually have an effect.
for (int i = 0; i < effect_count; ++i) {
Node* effect = NodeProperties::GetEffectInput(node);
CHECK(OperatorProperties::HasEffectOutput(effect->op()));
CHECK(IsDefUseChainLinkPresent(effect, node));
CHECK(IsUseDefChainLinkPresent(effect, node));
}
// Verify all control inputs are control nodes.
for (int i = 0; i < control_count; ++i) {
Node* control = NodeProperties::GetControlInput(node, i);
CHECK(OperatorProperties::HasControlOutput(control->op()));
CHECK(IsDefUseChainLinkPresent(control, node));
CHECK(IsUseDefChainLinkPresent(control, node));
}
// Verify all successors are projections if multiple value outputs exist.
if (OperatorProperties::GetValueOutputCount(node->op()) > 1) {
Node::Uses uses = node->uses();
for (Node::Uses::iterator it = uses.begin(); it != uses.end(); ++it) {
CHECK(!NodeProperties::IsValueEdge(it.edge()) ||
(*it)->opcode() == IrOpcode::kProjection ||
(*it)->opcode() == IrOpcode::kParameter);
}
}
switch (node->opcode()) {
case IrOpcode::kStart:
// Start has no inputs.
CHECK_EQ(0, input_count);
break;
case IrOpcode::kEnd:
// End has no outputs.
CHECK(!OperatorProperties::HasValueOutput(node->op()));
CHECK(!OperatorProperties::HasEffectOutput(node->op()));
CHECK(!OperatorProperties::HasControlOutput(node->op()));
break;
case IrOpcode::kDead:
// Dead is never connected to the graph.
UNREACHABLE();
case IrOpcode::kBranch: {
// Branch uses are IfTrue and IfFalse.
Node::Uses uses = node->uses();
bool got_true = false, got_false = false;
for (Node::Uses::iterator it = uses.begin(); it != uses.end(); ++it) {
CHECK(((*it)->opcode() == IrOpcode::kIfTrue && !got_true) ||
((*it)->opcode() == IrOpcode::kIfFalse && !got_false));
if ((*it)->opcode() == IrOpcode::kIfTrue) got_true = true;
if ((*it)->opcode() == IrOpcode::kIfFalse) got_false = true;
}
// TODO(rossberg): Currently fails for various tests.
// CHECK(got_true && got_false);
break;
}
case IrOpcode::kIfTrue:
case IrOpcode::kIfFalse:
CHECK_EQ(IrOpcode::kBranch,
NodeProperties::GetControlInput(node, 0)->opcode());
break;
case IrOpcode::kLoop:
case IrOpcode::kMerge:
break;
case IrOpcode::kReturn:
// TODO(rossberg): check successor is End
break;
case IrOpcode::kThrow:
// TODO(rossberg): what are the constraints on these?
break;
case IrOpcode::kParameter: {
// Parameters have the start node as inputs.
CHECK_EQ(1, input_count);
CHECK_EQ(IrOpcode::kStart,
NodeProperties::GetValueInput(node, 0)->opcode());
// Parameter has an input that produces enough values.
int index = static_cast<Operator1<int>*>(node->op())->parameter();
Node* input = NodeProperties::GetValueInput(node, 0);
// Currently, parameter indices start at -1 instead of 0.
CHECK_GT(OperatorProperties::GetValueOutputCount(input->op()), index + 1);
break;
}
case IrOpcode::kInt32Constant:
case IrOpcode::kInt64Constant:
case IrOpcode::kFloat64Constant:
case IrOpcode::kExternalConstant:
case IrOpcode::kNumberConstant:
case IrOpcode::kHeapConstant:
// Constants have no inputs.
CHECK_EQ(0, input_count);
break;
case IrOpcode::kPhi: {
// Phi input count matches parent control node.
CHECK_EQ(1, control_count);
Node* control = NodeProperties::GetControlInput(node, 0);
CHECK_EQ(value_count,
OperatorProperties::GetControlInputCount(control->op()));
break;
}
case IrOpcode::kEffectPhi: {
// EffectPhi input count matches parent control node.
CHECK_EQ(1, control_count);
Node* control = NodeProperties::GetControlInput(node, 0);
CHECK_EQ(effect_count,
OperatorProperties::GetControlInputCount(control->op()));
break;
}
case IrOpcode::kLazyDeoptimization:
// TODO(jarin): what are the constraints on these?
break;
case IrOpcode::kDeoptimize:
// TODO(jarin): what are the constraints on these?
break;
case IrOpcode::kFrameState:
// TODO(jarin): what are the constraints on these?
break;
case IrOpcode::kCall:
// TODO(rossberg): what are the constraints on these?
break;
case IrOpcode::kContinuation:
// TODO(jarin): what are the constraints on these?
break;
case IrOpcode::kProjection: {
// Projection has an input that produces enough values.
int index = static_cast<Operator1<int>*>(node->op())->parameter();
Node* input = NodeProperties::GetValueInput(node, 0);
CHECK_GT(OperatorProperties::GetValueOutputCount(input->op()), index);
break;
}
default:
// TODO(rossberg): Check other node kinds.
break;
}
if (from_start) {
reached_from_start.insert(node);
} else {
reached_from_end.insert(node);
}
return GenericGraphVisit::CONTINUE;
}
void Verifier::Run(Graph* graph) {
Visitor visitor(graph->zone());
CHECK_NE(NULL, graph->start());
visitor.from_start = true;
graph->VisitNodeUsesFromStart(&visitor);
CHECK_NE(NULL, graph->end());
visitor.from_start = false;
graph->VisitNodeInputsFromEnd(&visitor);
// All control nodes reachable from end are reachable from start.
for (NodeSet::iterator it = visitor.reached_from_end.begin();
it != visitor.reached_from_end.end(); ++it) {
CHECK(!NodeProperties::IsControl(*it) ||
visitor.reached_from_start.count(*it));
}
}
}
}
} // namespace v8::internal::compiler