Commit a48ad24a authored by titzer's avatar titzer Committed by Commit bot

[turbofan] Reuse forward fixpoint algorithm in Typer by making it a Reducer.

R=jarin@chromium.org
BUG=

Review URL: https://codereview.chromium.org/769303002

Cr-Commit-Position: refs/heads/master@{#25633}
parent e564b3fe
...@@ -201,6 +201,11 @@ inline Bounds NodeProperties::GetBounds(Node* node) { ...@@ -201,6 +201,11 @@ inline Bounds NodeProperties::GetBounds(Node* node) {
return node->bounds(); return node->bounds();
} }
inline void NodeProperties::RemoveBounds(Node* node) {
Bounds empty;
node->set_bounds(empty);
}
inline void NodeProperties::SetBounds(Node* node, Bounds b) { inline void NodeProperties::SetBounds(Node* node, Bounds b) {
DCHECK(b.lower != NULL && b.upper != NULL); DCHECK(b.lower != NULL && b.upper != NULL);
node->set_bounds(b); node->set_bounds(b);
......
...@@ -43,6 +43,7 @@ class NodeProperties { ...@@ -43,6 +43,7 @@ class NodeProperties {
static inline bool IsTyped(Node* node); static inline bool IsTyped(Node* node);
static inline Bounds GetBounds(Node* node); static inline Bounds GetBounds(Node* node);
static inline void SetBounds(Node* node, Bounds bounds); static inline void SetBounds(Node* node, Bounds bounds);
static inline void RemoveBounds(Node* node);
static inline bool AllValueInputsAreTyped(Node* node); static inline bool AllValueInputsAreTyped(Node* node);
static inline int FirstValueIndex(Node* node); static inline int FirstValueIndex(Node* node);
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "src/bootstrapper.h" #include "src/bootstrapper.h"
#include "src/compiler/graph-inl.h" #include "src/compiler/graph-inl.h"
#include "src/compiler/graph-reducer.h"
#include "src/compiler/js-operator.h" #include "src/compiler/js-operator.h"
#include "src/compiler/node.h" #include "src/compiler/node.h"
#include "src/compiler/node-properties-inl.h" #include "src/compiler/node-properties-inl.h"
...@@ -217,10 +218,42 @@ Typer::~Typer() { ...@@ -217,10 +218,42 @@ Typer::~Typer() {
} }
class Typer::Visitor : public NullNodeVisitor { class Typer::Visitor : public Reducer {
public: public:
explicit Visitor(Typer* typer) : typer_(typer) {} explicit Visitor(Typer* typer) : typer_(typer) {}
virtual Reduction Reduce(Node* node) OVERRIDE {
if (node->op()->ValueOutputCount() == 0) return NoChange();
switch (node->opcode()) {
#define DECLARE_CASE(x) \
case IrOpcode::k##x: \
return UpdateBounds(node, TypeBinaryOp(node, x##Typer));
JS_SIMPLE_BINOP_LIST(DECLARE_CASE)
#undef DECLARE_CASE
#define DECLARE_CASE(x) \
case IrOpcode::k##x: \
return UpdateBounds(node, Type##x(node));
DECLARE_CASE(Start)
// VALUE_OP_LIST without JS_SIMPLE_BINOP_LIST:
COMMON_OP_LIST(DECLARE_CASE)
SIMPLIFIED_OP_LIST(DECLARE_CASE)
MACHINE_OP_LIST(DECLARE_CASE)
JS_SIMPLE_UNOP_LIST(DECLARE_CASE)
JS_OBJECT_OP_LIST(DECLARE_CASE)
JS_CONTEXT_OP_LIST(DECLARE_CASE)
JS_OTHER_OP_LIST(DECLARE_CASE)
#undef DECLARE_CASE
#define DECLARE_CASE(x) case IrOpcode::k##x:
DECLARE_CASE(End)
INNER_CONTROL_OP_LIST(DECLARE_CASE)
#undef DECLARE_CASE
break;
}
return NoChange();
}
Bounds TypeNode(Node* node) { Bounds TypeNode(Node* node) {
switch (node->opcode()) { switch (node->opcode()) {
#define DECLARE_CASE(x) \ #define DECLARE_CASE(x) \
...@@ -252,7 +285,10 @@ class Typer::Visitor : public NullNodeVisitor { ...@@ -252,7 +285,10 @@ class Typer::Visitor : public NullNodeVisitor {
Type* TypeConstant(Handle<Object> value); Type* TypeConstant(Handle<Object> value);
protected: private:
Typer* typer_;
MaybeHandle<Context> context_;
#define DECLARE_METHOD(x) inline Bounds Type##x(Node* node); #define DECLARE_METHOD(x) inline Bounds Type##x(Node* node);
DECLARE_METHOD(Start) DECLARE_METHOD(Start)
VALUE_OP_LIST(DECLARE_METHOD) VALUE_OP_LIST(DECLARE_METHOD)
...@@ -283,10 +319,6 @@ class Typer::Visitor : public NullNodeVisitor { ...@@ -283,10 +319,6 @@ class Typer::Visitor : public NullNodeVisitor {
Graph* graph() { return typer_->graph(); } Graph* graph() { return typer_->graph(); }
MaybeHandle<Context> context() { return typer_->context(); } MaybeHandle<Context> context() { return typer_->context(); }
private:
Typer* typer_;
MaybeHandle<Context> context_;
typedef Type* (*UnaryTyperFun)(Type*, Typer* t); typedef Type* (*UnaryTyperFun)(Type*, Typer* t);
typedef Type* (*BinaryTyperFun)(Type*, Type*, Typer* t); typedef Type* (*BinaryTyperFun)(Type*, Type*, Typer* t);
...@@ -319,54 +351,11 @@ class Typer::Visitor : public NullNodeVisitor { ...@@ -319,54 +351,11 @@ class Typer::Visitor : public NullNodeVisitor {
static Type* JSUnaryNotTyper(Type*, Typer*); static Type* JSUnaryNotTyper(Type*, Typer*);
static Type* JSLoadPropertyTyper(Type*, Type*, Typer*); static Type* JSLoadPropertyTyper(Type*, Type*, Typer*);
static Type* JSCallFunctionTyper(Type*, Typer*); static Type* JSCallFunctionTyper(Type*, Typer*);
};
class Typer::RunVisitor : public Typer::Visitor {
public:
explicit RunVisitor(Typer* typer)
: Visitor(typer),
redo(NodeSet::key_compare(), NodeSet::allocator_type(typer->zone())) {}
void Post(Node* node) {
if (node->op()->ValueOutputCount() > 0) {
Bounds bounds = TypeNode(node);
NodeProperties::SetBounds(node, bounds);
// Remember incompletely typed nodes for least fixpoint iteration.
if (!NodeProperties::AllValueInputsAreTyped(node)) redo.insert(node);
}
}
NodeSet redo;
};
class Typer::WidenVisitor : public Typer::Visitor {
public:
explicit WidenVisitor(Typer* typer)
: Visitor(typer),
local_zone_(zone()->isolate()),
enabled_(graph()->NodeCount(), true, &local_zone_),
queue_(&local_zone_) {}
void Run(NodeSet* nodes) {
// Queue all the roots.
for (Node* node : *nodes) {
Queue(node);
}
while (!queue_.empty()) {
Node* node = queue_.front();
queue_.pop();
if (node->op()->ValueOutputCount() > 0) {
// Enable future queuing (and thus re-typing) of this node.
enabled_[node->id()] = true;
// Compute the new type.
Bounds previous = BoundsOrNone(node);
Bounds current = TypeNode(node);
Reduction UpdateBounds(Node* node, Bounds current) {
if (NodeProperties::IsTyped(node)) {
// Widen the bounds of a previously typed node.
Bounds previous = NodeProperties::GetBounds(node);
// Speed up termination in the presence of range types: // Speed up termination in the presence of range types:
current.upper = Weaken(current.upper, previous.upper); current.upper = Weaken(current.upper, previous.upper);
current.lower = Weaken(current.lower, previous.lower); current.lower = Weaken(current.lower, previous.lower);
...@@ -376,40 +365,46 @@ class Typer::WidenVisitor : public Typer::Visitor { ...@@ -376,40 +365,46 @@ class Typer::WidenVisitor : public Typer::Visitor {
DCHECK(previous.upper->Is(current.upper)); DCHECK(previous.upper->Is(current.upper));
NodeProperties::SetBounds(node, current); NodeProperties::SetBounds(node, current);
// If something changed, push all uses into the queue.
if (!(previous.Narrows(current) && current.Narrows(previous))) { if (!(previous.Narrows(current) && current.Narrows(previous))) {
for (Node* use : node->uses()) { // If something changed, revisit all uses.
Queue(use); return Changed(node);
} }
return NoChange();
} else {
// No previous type, simply update the bounds.
NodeProperties::SetBounds(node, current);
return Changed(node);
} }
} }
// If there is no value output, we deliberately leave the node disabled };
// for queuing - there is no need to type it.
void Typer::Run() {
{
// TODO(titzer): this is a hack. Reset types for interior nodes first.
NodeDeque deque(zone());
NodeMarker<bool> marked(graph(), 2);
deque.push_front(graph()->end());
marked.Set(graph()->end(), true);
while (!deque.empty()) {
Node* node = deque.front();
deque.pop_front();
// TODO(titzer): there shouldn't be a need to retype constants.
if (node->op()->ValueOutputCount() > 0)
NodeProperties::RemoveBounds(node);
for (Node* input : node->inputs()) {
if (!marked.Get(input)) {
marked.Set(input, true);
deque.push_back(input);
} }
} }
void Queue(Node* node) {
// If the node is enabled for queuing, push it to the queue and disable it
// (to avoid queuing it multiple times).
if (enabled_[node->id()]) {
queue_.push(node);
enabled_[node->id()] = false;
} }
} }
private: Visitor visitor(this);
Zone local_zone_; GraphReducer graph_reducer(graph(), zone());
BoolVector enabled_; graph_reducer.AddReducer(&visitor);
ZoneQueue<Node*> queue_; graph_reducer.ReduceGraph();
};
void Typer::Run() {
RunVisitor typing(this);
graph_->VisitNodeInputsFromEnd(&typing);
// Find least fixpoint.
WidenVisitor widen(this);
widen.Run(&typing.redo);
} }
...@@ -1302,12 +1297,17 @@ Bounds Typer::Visitor::TypeJSInstanceOf(Node* node) { ...@@ -1302,12 +1297,17 @@ Bounds Typer::Visitor::TypeJSInstanceOf(Node* node) {
Bounds Typer::Visitor::TypeJSLoadContext(Node* node) { Bounds Typer::Visitor::TypeJSLoadContext(Node* node) {
Bounds outer = Operand(node, 0); Bounds outer = Operand(node, 0);
DCHECK(outer.upper->Maybe(Type::Internal())); Type* context_type = outer.upper;
if (context_type->Is(Type::None())) {
// Upper bound of context is not yet known.
return Bounds(Type::None(), Type::Any());
}
DCHECK(context_type->Maybe(Type::Internal()));
// TODO(rossberg): More precisely, instead of the above assertion, we should // TODO(rossberg): More precisely, instead of the above assertion, we should
// back-propagate the constraint that it has to be a subtype of Internal. // back-propagate the constraint that it has to be a subtype of Internal.
ContextAccess access = OpParameter<ContextAccess>(node); ContextAccess access = OpParameter<ContextAccess>(node);
Type* context_type = outer.upper;
MaybeHandle<Context> context; MaybeHandle<Context> context;
if (context_type->IsConstant()) { if (context_type->IsConstant()) {
context = Handle<Context>::cast(context_type->AsConstant()->Value()); context = Handle<Context>::cast(context_type->AsConstant()->Value());
......
...@@ -31,8 +31,6 @@ class Typer { ...@@ -31,8 +31,6 @@ class Typer {
private: private:
class Visitor; class Visitor;
class RunVisitor;
class WidenVisitor;
class Decorator; class Decorator;
Graph* graph_; Graph* graph_;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment