| Index: src/compiler/typer.cc
|
| diff --git a/src/compiler/typer.cc b/src/compiler/typer.cc
|
| index 67b73ebad00f1896104c65864d415ac24694906b..e5b7b839708ec537d23ce6978b99712ea45dc148 100644
|
| --- a/src/compiler/typer.cc
|
| +++ b/src/compiler/typer.cc
|
| @@ -4,6 +4,7 @@
|
|
|
| #include "src/bootstrapper.h"
|
| #include "src/compiler/graph-inl.h"
|
| +#include "src/compiler/graph-reducer.h"
|
| #include "src/compiler/js-operator.h"
|
| #include "src/compiler/node.h"
|
| #include "src/compiler/node-properties-inl.h"
|
| @@ -217,10 +218,42 @@ Typer::~Typer() {
|
| }
|
|
|
|
|
| -class Typer::Visitor : public NullNodeVisitor {
|
| +class Typer::Visitor : public Reducer {
|
| public:
|
| 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) {
|
| switch (node->opcode()) {
|
| #define DECLARE_CASE(x) \
|
| @@ -252,7 +285,10 @@ class Typer::Visitor : public NullNodeVisitor {
|
|
|
| Type* TypeConstant(Handle<Object> value);
|
|
|
| - protected:
|
| + private:
|
| + Typer* typer_;
|
| + MaybeHandle<Context> context_;
|
| +
|
| #define DECLARE_METHOD(x) inline Bounds Type##x(Node* node);
|
| DECLARE_METHOD(Start)
|
| VALUE_OP_LIST(DECLARE_METHOD)
|
| @@ -283,10 +319,6 @@ class Typer::Visitor : public NullNodeVisitor {
|
| Graph* graph() { return typer_->graph(); }
|
| MaybeHandle<Context> context() { return typer_->context(); }
|
|
|
| - private:
|
| - Typer* typer_;
|
| - MaybeHandle<Context> context_;
|
| -
|
| typedef Type* (*UnaryTyperFun)(Type*, Typer* t);
|
| typedef Type* (*BinaryTyperFun)(Type*, Type*, Typer* t);
|
|
|
| @@ -319,97 +351,60 @@ class Typer::Visitor : public NullNodeVisitor {
|
| static Type* JSUnaryNotTyper(Type*, Typer*);
|
| static Type* JSLoadPropertyTyper(Type*, 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);
|
| + 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:
|
| + current.upper = Weaken(current.upper, previous.upper);
|
| + current.lower = Weaken(current.lower, previous.lower);
|
| +
|
| + // Types should not get less precise.
|
| + DCHECK(previous.lower->Is(current.lower));
|
| + DCHECK(previous.upper->Is(current.upper));
|
| +
|
| + NodeProperties::SetBounds(node, current);
|
| + if (!(previous.Narrows(current) && current.Narrows(previous))) {
|
| + // If something changed, revisit all uses.
|
| + return Changed(node);
|
| + }
|
| + return NoChange();
|
| + } else {
|
| + // No previous type, simply update the bounds.
|
| + NodeProperties::SetBounds(node, current);
|
| + return Changed(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);
|
| -
|
| - // Speed up termination in the presence of range types:
|
| - current.upper = Weaken(current.upper, previous.upper);
|
| - current.lower = Weaken(current.lower, previous.lower);
|
| -
|
| - // Types should not get less precise.
|
| - DCHECK(previous.lower->Is(current.lower));
|
| - DCHECK(previous.upper->Is(current.upper));
|
| -
|
| - NodeProperties::SetBounds(node, current);
|
| - // If something changed, push all uses into the queue.
|
| - if (!(previous.Narrows(current) && current.Narrows(previous))) {
|
| - for (Node* use : node->uses()) {
|
| - Queue(use);
|
| - }
|
| +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);
|
| }
|
| }
|
| - // If there is no value output, we deliberately leave the node disabled
|
| - // for queuing - there is no need to type it.
|
| - }
|
| - }
|
| -
|
| - 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:
|
| - Zone local_zone_;
|
| - BoolVector enabled_;
|
| - ZoneQueue<Node*> queue_;
|
| -};
|
| -
|
| -
|
| -void Typer::Run() {
|
| - RunVisitor typing(this);
|
| - graph_->VisitNodeInputsFromEnd(&typing);
|
| - // Find least fixpoint.
|
| - WidenVisitor widen(this);
|
| - widen.Run(&typing.redo);
|
| + Visitor visitor(this);
|
| + GraphReducer graph_reducer(graph(), zone());
|
| + graph_reducer.AddReducer(&visitor);
|
| + graph_reducer.ReduceGraph();
|
| }
|
|
|
|
|
| @@ -1302,12 +1297,17 @@ Bounds Typer::Visitor::TypeJSInstanceOf(Node* node) {
|
|
|
| Bounds Typer::Visitor::TypeJSLoadContext(Node* node) {
|
| 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
|
| // back-propagate the constraint that it has to be a subtype of Internal.
|
|
|
| ContextAccess access = OpParameter<ContextAccess>(node);
|
| - Type* context_type = outer.upper;
|
| MaybeHandle<Context> context;
|
| if (context_type->IsConstant()) {
|
| context = Handle<Context>::cast(context_type->AsConstant()->Value());
|
|
|