protected:
explicit Expression(Isolate* isolate)
- : bounds_(Type::None(), Type::Any(), isolate),
+ : bounds_(Bounds::Unbounded(isolate)),
id_(GetNextId(isolate)),
test_id_(GetNextId(isolate)) {}
void set_to_boolean_types(byte types) { to_boolean_types_ = types; }
--- /dev/null
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 THE COPYRIGHT
+// OWNER 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.
+
+#ifndef V8_EFFECTS_H_
+#define V8_EFFECTS_H_
+
+#include "v8.h"
+
+#include "types.h"
+
+namespace v8 {
+namespace internal {
+
+
+// A simple struct to represent (write) effects. A write is represented as a
+// modification of type bounds (e.g. of a variable).
+//
+// An effect can either be definite, if the write is known to have taken place,
+// or 'possible', if it was optional. The difference is relevant when composing
+// effects.
+//
+// There are two ways to compose effects: sequentially (they happen one after
+// the other) or alternatively (either one or the other happens). A definite
+// effect cancels out any previous effect upon sequencing. A possible effect
+// merges into a previous effect, i.e., type bounds are merged. Alternative
+// composition always merges bounds. It yields a possible effect if at least
+// one was only possible.
+struct Effect {
+ enum Modality { POSSIBLE, DEFINITE };
+
+ Modality modality;
+ Bounds bounds;
+
+ Effect() {}
+ Effect(Bounds b, Modality m = DEFINITE) : modality(m), bounds(b) {}
+
+ // The unknown effect.
+ static Effect Unknown(Isolate* isolate) {
+ return Effect(Bounds::Unbounded(isolate), POSSIBLE);
+ }
+
+ static Effect Forget(Isolate* isolate) {
+ return Effect(Bounds::Unbounded(isolate), DEFINITE);
+ }
+
+ // Sequential composition, as in 'e1; e2'.
+ static Effect Seq(Effect e1, Effect e2, Isolate* isolate) {
+ if (e2.modality == DEFINITE) return e2;
+ return Effect(Bounds::Either(e1.bounds, e2.bounds, isolate), e1.modality);
+ }
+
+ // Alternative composition, as in 'cond ? e1 : e2'.
+ static Effect Alt(Effect e1, Effect e2, Isolate* isolate) {
+ return Effect(
+ Bounds::Either(e1.bounds, e2.bounds, isolate),
+ e1.modality == POSSIBLE ? POSSIBLE : e2.modality);
+ }
+};
+
+
+// Classes encapsulating sets of effects on variables.
+//
+// Effects maps variables to effects and supports sequential and alternative
+// composition.
+//
+// NestedEffects is an incremental representation that supports persistence
+// through functional extension. It represents the map as an adjoin of a list
+// of maps, whose tail can be shared.
+//
+// Both classes provide similar interfaces, implemented in parts through the
+// EffectsMixin below (using sandwich style, to work around the style guide's
+// MI restriction).
+//
+// We also (ab)use Effects/NestedEffects as a representation for abstract
+// store typings. In that case, only definite effects are of interest.
+
+template<class Var, class Base, class Effects>
+class EffectsMixin: public Base {
+ public:
+ explicit EffectsMixin(Zone* zone) : Base(zone) {}
+
+ Effect Lookup(Var var) {
+ Locator locator;
+ return Find(var, &locator)
+ ? locator.value() : Effect::Unknown(Base::isolate());
+ }
+
+ Bounds LookupBounds(Var var) {
+ Effect effect = Lookup(var);
+ return effect.modality == Effect::DEFINITE
+ ? effect.bounds : Bounds::Unbounded(Base::isolate());
+ }
+
+ // Sequential composition.
+ void Seq(Var var, Effect effect) {
+ Locator locator;
+ if (!Insert(var, &locator)) {
+ effect = Effect::Seq(locator.value(), effect, Base::isolate());
+ }
+ locator.set_value(effect);
+ }
+
+ void Seq(Effects that) {
+ SeqMerger<EffectsMixin> merge = { *this };
+ that.ForEach(&merge);
+ }
+
+ // Alternative composition.
+ void Alt(Var var, Effect effect) {
+ Locator locator;
+ if (!Insert(var, &locator)) {
+ effect = Effect::Alt(locator.value(), effect, Base::isolate());
+ }
+ locator.set_value(effect);
+ }
+
+ void Alt(Effects that) {
+ AltWeakener<EffectsMixin> weaken = { *this, that };
+ this->ForEach(&weaken);
+ AltMerger<EffectsMixin> merge = { *this };
+ that.ForEach(&merge);
+ }
+
+ // Invalidation.
+ void Forget() {
+ Overrider override = {
+ Effect::Forget(Base::isolate()), Effects(Base::zone()) };
+ this->ForEach(&override);
+ Seq(override.effects);
+ }
+
+ protected:
+ typedef typename Base::Locator Locator;
+
+ template<class Self>
+ struct SeqMerger {
+ void Call(Var var, Effect effect) { self.Seq(var, effect); }
+ Self self;
+ };
+
+ template<class Self>
+ struct AltMerger {
+ void Call(Var var, Effect effect) { self.Alt(var, effect); }
+ Self self;
+ };
+
+ template<class Self>
+ struct AltWeakener {
+ void Call(Var var, Effect effect) {
+ if (effect.modality == Effect::DEFINITE && !other.Contains(var)) {
+ effect.modality = Effect::POSSIBLE;
+ Locator locator;
+ self.Insert(var, &locator);
+ locator.set_value(effect);
+ }
+ }
+ Self self;
+ Effects other;
+ };
+
+ struct Overrider {
+ void Call(Var var, Effect effect) { effects.Seq(var, new_effect); }
+ Effect new_effect;
+ Effects effects;
+ };
+};
+
+
+template<class Var, Var kNoVar> class Effects;
+template<class Var, Var kNoVar> class NestedEffectsBase;
+
+template<class Var, Var kNoVar>
+class EffectsBase {
+ public:
+ explicit EffectsBase(Zone* zone) : map_(new(zone) Mapping(zone)) {}
+
+ bool IsEmpty() { return map_->is_empty(); }
+
+ protected:
+ friend class NestedEffectsBase<Var, kNoVar>;
+ friend class
+ EffectsMixin<Var, NestedEffectsBase<Var, kNoVar>, Effects<Var, kNoVar> >;
+
+ Zone* zone() { return map_->allocator().zone(); }
+ Isolate* isolate() { return zone()->isolate(); }
+
+ struct SplayTreeConfig {
+ typedef Var Key;
+ typedef Effect Value;
+ static const Var kNoKey = kNoVar;
+ static Effect NoValue() { return Effect(); }
+ static int Compare(int x, int y) { return y - x; }
+ };
+ typedef ZoneSplayTree<SplayTreeConfig> Mapping;
+ typedef typename Mapping::Locator Locator;
+
+ bool Contains(Var var) {
+ ASSERT(var != kNoVar);
+ return map_->Contains(var);
+ }
+ bool Find(Var var, Locator* locator) {
+ ASSERT(var != kNoVar);
+ return map_->Find(var, locator);
+ }
+ bool Insert(Var var, Locator* locator) {
+ ASSERT(var != kNoVar);
+ return map_->Insert(var, locator);
+ }
+
+ template<class Callback>
+ void ForEach(Callback* callback) {
+ return map_->ForEach(callback);
+ }
+
+ private:
+ Mapping* map_;
+};
+
+template<class Var, Var kNoVar>
+const Var EffectsBase<Var, kNoVar>::SplayTreeConfig::kNoKey;
+
+template<class Var, Var kNoVar>
+class Effects: public
+ EffectsMixin<Var, EffectsBase<Var, kNoVar>, Effects<Var, kNoVar> > {
+ public:
+ explicit Effects(Zone* zone)
+ : EffectsMixin<Var, EffectsBase<Var, kNoVar>, Effects<Var, kNoVar> >(zone)
+ {}
+};
+
+
+template<class Var, Var kNoVar>
+class NestedEffectsBase {
+ public:
+ explicit NestedEffectsBase(Zone* zone) : node_(new(zone) Node(zone)) {}
+
+ template<class Callback>
+ void ForEach(Callback* callback) {
+ if (node_->previous) NestedEffectsBase(node_->previous).ForEach(callback);
+ node_->effects.ForEach(callback);
+ }
+
+ Effects<Var, kNoVar> Top() { return node_->effects; }
+
+ bool IsEmpty() {
+ for (Node* node = node_; node != NULL; node = node->previous) {
+ if (!node->effects.IsEmpty()) return false;
+ }
+ return true;
+ }
+
+ protected:
+ typedef typename EffectsBase<Var, kNoVar>::Locator Locator;
+
+ Zone* zone() { return node_->zone; }
+ Isolate* isolate() { return zone()->isolate(); }
+
+ void push() { node_ = new(node_->zone) Node(node_->zone, node_); }
+ void pop() { node_ = node_->previous; }
+ bool is_empty() { return node_ == NULL; }
+
+ bool Contains(Var var) {
+ ASSERT(var != kNoVar);
+ for (Node* node = node_; node != NULL; node = node->previous) {
+ if (node->effects.Contains(var)) return true;
+ }
+ return false;
+ }
+
+ bool Find(Var var, Locator* locator) {
+ ASSERT(var != kNoVar);
+ for (Node* node = node_; node != NULL; node = node->previous) {
+ if (node->effects.Find(var, locator)) return true;
+ }
+ return false;
+ }
+
+ bool Insert(Var var, Locator* locator);
+
+ private:
+ struct Node: ZoneObject {
+ Zone* zone;
+ Effects<Var, kNoVar> effects;
+ Node* previous;
+ explicit Node(Zone* zone, Node* previous = NULL)
+ : zone(zone), effects(zone), previous(previous) {}
+ };
+
+ explicit NestedEffectsBase(Node* node) : node_(node) {}
+
+ Node* node_;
+};
+
+
+template<class Var, Var kNoVar>
+bool NestedEffectsBase<Var, kNoVar>::Insert(Var var, Locator* locator) {
+ ASSERT(var != kNoVar);
+ if (!node_->effects.Insert(var, locator)) return false;
+ Locator shadowed;
+ for (Node* node = node_->previous; node != NULL; node = node->previous) {
+ if (node->effects.Find(var, &shadowed)) {
+ // Initialize with shadowed entry.
+ locator->set_value(shadowed.value());
+ return false;
+ }
+ }
+ return true;
+}
+
+
+template<class Var, Var kNoVar>
+class NestedEffects: public
+ EffectsMixin<Var, NestedEffectsBase<Var, kNoVar>, Effects<Var, kNoVar> > {
+ public:
+ explicit NestedEffects(Zone* zone) :
+ EffectsMixin<Var, NestedEffectsBase<Var, kNoVar>, Effects<Var, kNoVar> >(
+ zone) {}
+
+ // Create an extension of the current effect set. The current set should not
+ // be modified while the extension is in use.
+ NestedEffects Push() {
+ NestedEffects result = *this;
+ result.push();
+ return result;
+ }
+
+ NestedEffects Pop() {
+ NestedEffects result = *this;
+ result.pop();
+ ASSERT(!this->is_empty());
+ return result;
+ }
+};
+
+} } // namespace v8::internal
+
+#endif // V8_EFFECTS_H_
}
+template<typename Config, class Allocator>
+bool SplayTree<Config, Allocator>::Contains(const Key& key) {
+ return FindInternal(key);
+}
+
+
template<typename Config, class Allocator>
bool SplayTree<Config, Allocator>::Find(const Key& key, Locator* locator) {
if (FindInternal(key)) {
template <typename Config, class Allocator> template <class Callback>
void SplayTree<Config, Allocator>::ForEachNode(Callback* callback) {
+ if (root_ == NULL) return;
// Pre-allocate some space for tiny trees.
List<Node*, Allocator> nodes_to_visit(10, allocator_);
- if (root_ != NULL) nodes_to_visit.Add(root_, allocator_);
+ nodes_to_visit.Add(root_, allocator_);
int pos = 0;
while (pos < nodes_to_visit.length()) {
Node* node = nodes_to_visit[pos++];
//
// typedef Key: the key type
// typedef Value: the value type
-// static const kNoKey: the dummy key used when no key is set
-// static const kNoValue: the dummy value used to initialize nodes
-// int (Compare)(Key& a, Key& b) -> {-1, 0, 1}: comparison function
+// static const Key kNoKey: the dummy key used when no key is set
+// static Value kNoValue(): the dummy value used to initialize nodes
+// static int (Compare)(Key& a, Key& b) -> {-1, 0, 1}: comparison function
//
// The tree is also parameterized by an allocation policy
// (Allocator). The policy is used for allocating lists in the C free
UNREACHABLE();
}
+ AllocationPolicy allocator() { return allocator_; }
+
+ // Checks if there is a mapping for the key.
+ bool Contains(const Key& key);
+
// Inserts the given key in this tree with the given value. Returns
// true if a node was inserted, otherwise false. If found the locator
// is enabled and provides access to the mapping for the key.
// Remove the node with the given key from the tree.
bool Remove(const Key& key);
+ // Remove all keys from the tree.
+ void Clear() { ResetRoot(); }
+
bool is_empty() { return root_ == NULL; }
// Perform the splay operation for the given key. Moves the node with
explicit Bounds(Handle<Type> t) : lower(t), upper(t) {}
Bounds(Type* t, Isolate* isl) : lower(t, isl), upper(t, isl) {}
+ // Unrestricted bounds.
+ static Bounds Unbounded(Isolate* isl) {
+ return Bounds(Type::None(), Type::Any(), isl);
+ }
+
// Meet: both b1 and b2 are known to hold.
static Bounds Both(Bounds b1, Bounds b2, Isolate* isl) {
return Bounds(
Handle<Code>(info->closure()->shared()->code()),
Handle<Context>(info->closure()->context()->native_context()),
info->isolate(),
- info->zone()) {
+ info->zone()),
+ store_(info->zone()) {
InitializeAstVisitor();
}
void AstTyper::VisitBlock(Block* stmt) {
RECURSE(VisitStatements(stmt->statements()));
+ if (stmt->labels() != NULL) {
+ store_.Forget(); // Control may transfer here via 'break l'.
+ }
}
void AstTyper::VisitIfStatement(IfStatement* stmt) {
- RECURSE(Visit(stmt->condition()));
- RECURSE(Visit(stmt->then_statement()));
- RECURSE(Visit(stmt->else_statement()));
-
+ // Collect type feedback.
if (!stmt->condition()->ToBooleanIsTrue() &&
!stmt->condition()->ToBooleanIsFalse()) {
stmt->condition()->RecordToBooleanTypeFeedback(oracle());
}
+
+ RECURSE(Visit(stmt->condition()));
+ Effects then_effects = EnterEffects();
+ RECURSE(Visit(stmt->then_statement()));
+ ExitEffects();
+ Effects else_effects = EnterEffects();
+ RECURSE(Visit(stmt->else_statement()));
+ ExitEffects();
+ then_effects.Alt(else_effects);
+ store_.Seq(then_effects);
}
void AstTyper::VisitContinueStatement(ContinueStatement* stmt) {
+ // TODO(rossberg): is it worth having a non-termination effect?
}
void AstTyper::VisitBreakStatement(BreakStatement* stmt) {
+ // TODO(rossberg): is it worth having a non-termination effect?
}
void AstTyper::VisitReturnStatement(ReturnStatement* stmt) {
- RECURSE(Visit(stmt->expression()));
-
+ // Collect type feedback.
// TODO(rossberg): we only need this for inlining into test contexts...
stmt->expression()->RecordToBooleanTypeFeedback(oracle());
+
+ RECURSE(Visit(stmt->expression()));
+ // TODO(rossberg): is it worth having a non-termination effect?
}
void AstTyper::VisitSwitchStatement(SwitchStatement* stmt) {
RECURSE(Visit(stmt->tag()));
+
ZoneList<CaseClause*>* clauses = stmt->cases();
SwitchStatement::SwitchType switch_type = stmt->switch_type();
for (int i = 0; i < clauses->length(); ++i) {
CaseClause* clause = clauses->at(i);
if (!clause->is_default()) {
Expression* label = clause->label();
- RECURSE(Visit(label));
-
SwitchStatement::SwitchType label_switch_type =
label->IsSmiLiteral() ? SwitchStatement::SMI_SWITCH :
label->IsStringLiteral() ? SwitchStatement::STRING_SWITCH :
switch_type = label_switch_type;
else if (switch_type != label_switch_type)
switch_type = SwitchStatement::GENERIC_SWITCH;
+
+ RECURSE(Visit(label));
}
RECURSE(VisitStatements(clause->statements()));
}
+
+ // TODO(rossberg): handle switch effects
+ store_.Forget();
+
if (switch_type == SwitchStatement::UNKNOWN_SWITCH)
switch_type = SwitchStatement::GENERIC_SWITCH;
stmt->set_switch_type(switch_type);
+ // Collect type feedback.
// TODO(rossberg): can we eliminate this special case and extra loop?
if (switch_type == SwitchStatement::SMI_SWITCH) {
for (int i = 0; i < clauses->length(); ++i) {
void AstTyper::VisitDoWhileStatement(DoWhileStatement* stmt) {
- RECURSE(Visit(stmt->body()));
- RECURSE(Visit(stmt->cond()));
-
+ // Collect type feedback.
if (!stmt->cond()->ToBooleanIsTrue()) {
stmt->cond()->RecordToBooleanTypeFeedback(oracle());
}
+
+ // TODO(rossberg): refine the unconditional Forget (here and elsewhere) by
+ // computing the set of variables assigned in only some of the origins of the
+ // control transfer (such as the loop body here).
+ store_.Forget(); // Control may transfer here via looping or 'continue'.
+ RECURSE(Visit(stmt->body()));
+ RECURSE(Visit(stmt->cond()));
+ store_.Forget(); // Control may transfer here via 'break'.
}
void AstTyper::VisitWhileStatement(WhileStatement* stmt) {
- RECURSE(Visit(stmt->cond()));
- RECURSE(Visit(stmt->body()));
-
+ // Collect type feedback.
if (!stmt->cond()->ToBooleanIsTrue()) {
stmt->cond()->RecordToBooleanTypeFeedback(oracle());
}
+
+ store_.Forget(); // Control may transfer here via looping or 'continue'.
+ RECURSE(Visit(stmt->cond()));
+ RECURSE(Visit(stmt->body()));
+ store_.Forget(); // Control may transfer here via termination or 'break'.
}
if (stmt->init() != NULL) {
RECURSE(Visit(stmt->init()));
}
+ store_.Forget(); // Control may transfer here via looping.
if (stmt->cond() != NULL) {
- RECURSE(Visit(stmt->cond()));
-
+ // Collect type feedback.
stmt->cond()->RecordToBooleanTypeFeedback(oracle());
+
+ RECURSE(Visit(stmt->cond()));
}
RECURSE(Visit(stmt->body()));
+ store_.Forget(); // Control may transfer here via 'continue'.
if (stmt->next() != NULL) {
RECURSE(Visit(stmt->next()));
}
+ store_.Forget(); // Control may transfer here via termination or 'break'.
}
void AstTyper::VisitForInStatement(ForInStatement* stmt) {
+ // Collect type feedback.
+ stmt->RecordTypeFeedback(oracle());
+
RECURSE(Visit(stmt->enumerable()));
+ store_.Forget(); // Control may transfer here via looping or 'continue'.
RECURSE(Visit(stmt->body()));
-
- stmt->RecordTypeFeedback(oracle());
+ store_.Forget(); // Control may transfer here via 'break'.
}
void AstTyper::VisitForOfStatement(ForOfStatement* stmt) {
RECURSE(Visit(stmt->iterable()));
+ store_.Forget(); // Control may transfer here via looping or 'continue'.
RECURSE(Visit(stmt->body()));
+ store_.Forget(); // Control may transfer here via 'break'.
}
void AstTyper::VisitTryCatchStatement(TryCatchStatement* stmt) {
+ Effects try_effects = EnterEffects();
RECURSE(Visit(stmt->try_block()));
+ ExitEffects();
+ Effects catch_effects = EnterEffects();
+ store_.Forget(); // Control may transfer here via 'throw'.
RECURSE(Visit(stmt->catch_block()));
+ ExitEffects();
+ try_effects.Alt(catch_effects);
+ store_.Seq(try_effects);
+ // At this point, only variables that were reassigned in the catch block are
+ // still remembered.
}
void AstTyper::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
RECURSE(Visit(stmt->try_block()));
+ store_.Forget(); // Control may transfer here via 'throw'.
RECURSE(Visit(stmt->finally_block()));
}
void AstTyper::VisitDebuggerStatement(DebuggerStatement* stmt) {
+ store_.Forget(); // May do whatever.
}
void AstTyper::VisitConditional(Conditional* expr) {
+ // Collect type feedback.
+ expr->condition()->RecordToBooleanTypeFeedback(oracle());
+
RECURSE(Visit(expr->condition()));
+ Effects then_effects = EnterEffects();
RECURSE(Visit(expr->then_expression()));
+ ExitEffects();
+ Effects else_effects = EnterEffects();
RECURSE(Visit(expr->else_expression()));
-
- expr->condition()->RecordToBooleanTypeFeedback(oracle());
+ ExitEffects();
+ then_effects.Alt(else_effects);
+ store_.Seq(then_effects);
NarrowType(expr, Bounds::Either(
expr->then_expression()->bounds(),
void AstTyper::VisitVariableProxy(VariableProxy* expr) {
- // TODO(rossberg): typing of variables
+ Variable* var = expr->var();
+ if (var->IsStackAllocated()) {
+ NarrowType(expr, store_.LookupBounds(variable_index(var)));
+ }
}
ZoneList<ObjectLiteral::Property*>* properties = expr->properties();
for (int i = 0; i < properties->length(); ++i) {
ObjectLiteral::Property* prop = properties->at(i);
- RECURSE(Visit(prop->value()));
+ // Collect type feedback.
if ((prop->kind() == ObjectLiteral::Property::MATERIALIZED_LITERAL &&
!CompileTimeValue::IsCompileTimeValue(prop->value())) ||
prop->kind() == ObjectLiteral::Property::COMPUTED) {
prop->RecordTypeFeedback(oracle());
}
}
+
+ RECURSE(Visit(prop->value()));
}
NarrowType(expr, Bounds(Type::Object(), isolate_));
void AstTyper::VisitAssignment(Assignment* expr) {
// TODO(rossberg): Can we clean this up?
if (expr->is_compound()) {
- RECURSE(Visit(expr->binary_operation()));
-
+ // Collect type feedback.
Expression* target = expr->target();
Property* prop = target->AsProperty();
if (prop != NULL) {
}
}
+ RECURSE(Visit(expr->binary_operation()));
+
NarrowType(expr, expr->binary_operation()->bounds());
} else {
- RECURSE(Visit(expr->target()));
- RECURSE(Visit(expr->value()));
-
+ // Collect type feedback.
if (expr->target()->AsProperty()) {
expr->RecordTypeFeedback(oracle(), zone());
}
+ RECURSE(Visit(expr->target()));
+ RECURSE(Visit(expr->value()));
+
NarrowType(expr, expr->value()->bounds());
}
- // TODO(rossberg): handle target variables
+
+ if (expr->target()->AsVariableProxy()) {
+ Variable* var = expr->target()->AsVariableProxy()->var();
+ if (var->IsStackAllocated()) {
+ store_.Seq(variable_index(var), Effect(expr->bounds()));
+ }
+ }
}
RECURSE(Visit(expr->generator_object()));
RECURSE(Visit(expr->expression()));
- // We don't know anything about the type.
+ // We don't know anything about the result type.
}
void AstTyper::VisitThrow(Throw* expr) {
RECURSE(Visit(expr->exception()));
+ // TODO(rossberg): is it worth having a non-termination effect?
NarrowType(expr, Bounds(Type::None(), isolate_));
}
void AstTyper::VisitProperty(Property* expr) {
+ // Collect type feedback.
+ expr->RecordTypeFeedback(oracle(), zone());
+
RECURSE(Visit(expr->obj()));
RECURSE(Visit(expr->key()));
- expr->RecordTypeFeedback(oracle(), zone());
-
- // We don't know anything about the type.
+ // We don't know anything about the result type.
}
void AstTyper::VisitCall(Call* expr) {
- RECURSE(Visit(expr->expression()));
- ZoneList<Expression*>* args = expr->arguments();
- for (int i = 0; i < args->length(); ++i) {
- Expression* arg = args->at(i);
- RECURSE(Visit(arg));
- }
-
+ // Collect type feedback.
Expression* callee = expr->expression();
Property* prop = callee->AsProperty();
if (prop != NULL) {
expr->RecordTypeFeedback(oracle(), CALL_AS_FUNCTION);
}
- // We don't know anything about the type.
+ RECURSE(Visit(expr->expression()));
+ ZoneList<Expression*>* args = expr->arguments();
+ for (int i = 0; i < args->length(); ++i) {
+ Expression* arg = args->at(i);
+ RECURSE(Visit(arg));
+ }
+
+ VariableProxy* proxy = expr->expression()->AsVariableProxy();
+ if (proxy != NULL && proxy->var()->is_possibly_eval(isolate())) {
+ store_.Forget(); // Eval could do whatever to local variables.
+ }
+
+ // We don't know anything about the result type.
}
void AstTyper::VisitCallNew(CallNew* expr) {
+ // Collect type feedback.
+ expr->RecordTypeFeedback(oracle());
+
RECURSE(Visit(expr->expression()));
ZoneList<Expression*>* args = expr->arguments();
for (int i = 0; i < args->length(); ++i) {
RECURSE(Visit(arg));
}
- expr->RecordTypeFeedback(oracle());
-
- // We don't know anything about the type.
+ // We don't know anything about the result type.
}
RECURSE(Visit(arg));
}
- // We don't know anything about the type.
+ // We don't know anything about the result type.
}
void AstTyper::VisitUnaryOperation(UnaryOperation* expr) {
- RECURSE(Visit(expr->expression()));
-
// Collect type feedback.
Handle<Type> op_type = oracle()->UnaryType(expr->UnaryOperationFeedbackId());
NarrowLowerType(expr->expression(), op_type);
expr->expression()->RecordToBooleanTypeFeedback(oracle());
}
+ RECURSE(Visit(expr->expression()));
+
switch (expr->op()) {
case Token::NOT:
case Token::DELETE:
void AstTyper::VisitCountOperation(CountOperation* expr) {
- RECURSE(Visit(expr->expression()));
-
+ // Collect type feedback.
expr->RecordTypeFeedback(oracle(), zone());
Property* prop = expr->expression()->AsProperty();
if (prop != NULL) {
prop->RecordTypeFeedback(oracle(), zone());
}
+ RECURSE(Visit(expr->expression()));
+
NarrowType(expr, Bounds(Type::Smi(), Type::Number(), isolate_));
+
+ if (expr->expression()->AsVariableProxy()) {
+ Variable* var = expr->expression()->AsVariableProxy()->var();
+ if (var->IsStackAllocated()) {
+ store_.Seq(variable_index(var), Effect(expr->bounds()));
+ }
+ }
}
void AstTyper::VisitBinaryOperation(BinaryOperation* expr) {
- RECURSE(Visit(expr->left()));
- RECURSE(Visit(expr->right()));
-
// Collect type feedback.
Handle<Type> type, left_type, right_type;
Maybe<int> fixed_right_arg;
switch (expr->op()) {
case Token::COMMA:
+ RECURSE(Visit(expr->left()));
+ RECURSE(Visit(expr->right()));
NarrowType(expr, expr->right()->bounds());
break;
case Token::OR:
- case Token::AND:
+ case Token::AND: {
+ Effects left_effects = EnterEffects();
+ RECURSE(Visit(expr->left()));
+ ExitEffects();
+ Effects right_effects = EnterEffects();
+ RECURSE(Visit(expr->right()));
+ ExitEffects();
+ left_effects.Alt(right_effects);
+ store_.Seq(left_effects);
+
NarrowType(expr, Bounds::Either(
expr->left()->bounds(), expr->right()->bounds(), isolate_));
break;
+ }
case Token::BIT_OR:
case Token::BIT_AND: {
+ RECURSE(Visit(expr->left()));
+ RECURSE(Visit(expr->right()));
Type* upper = Type::Union(
expr->left()->bounds().upper, expr->right()->bounds().upper);
if (!upper->Is(Type::Signed32())) upper = Type::Signed32();
case Token::BIT_XOR:
case Token::SHL:
case Token::SAR:
+ RECURSE(Visit(expr->left()));
+ RECURSE(Visit(expr->right()));
NarrowType(expr, Bounds(Type::Smi(), Type::Signed32(), isolate_));
break;
case Token::SHR:
+ RECURSE(Visit(expr->left()));
+ RECURSE(Visit(expr->right()));
NarrowType(expr, Bounds(Type::Smi(), Type::Unsigned32(), isolate_));
break;
case Token::ADD: {
+ RECURSE(Visit(expr->left()));
+ RECURSE(Visit(expr->right()));
Bounds l = expr->left()->bounds();
Bounds r = expr->right()->bounds();
Type* lower =
case Token::MUL:
case Token::DIV:
case Token::MOD:
+ RECURSE(Visit(expr->left()));
+ RECURSE(Visit(expr->right()));
NarrowType(expr, Bounds(Type::Smi(), Type::Number(), isolate_));
break;
default:
void AstTyper::VisitCompareOperation(CompareOperation* expr) {
- RECURSE(Visit(expr->left()));
- RECURSE(Visit(expr->right()));
-
// Collect type feedback.
Handle<Type> left_type, right_type, combined_type;
oracle()->CompareType(expr->CompareOperationFeedbackId(),
NarrowLowerType(expr->right(), right_type);
expr->set_combined_type(combined_type);
+ RECURSE(Visit(expr->left()));
+ RECURSE(Visit(expr->right()));
+
NarrowType(expr, Bounds(Type::Boolean(), isolate_));
}
#include "compiler.h"
#include "type-info.h"
#include "types.h"
+#include "effects.h"
#include "zone.h"
#include "scopes.h"
private:
explicit AstTyper(CompilationInfo* info);
+ static const int kNoVar = INT_MIN;
+ typedef v8::internal::Effects<int, kNoVar> Effects;
+ typedef v8::internal::NestedEffects<int, kNoVar> Store;
+
CompilationInfo* info_;
TypeFeedbackOracle oracle_;
+ Store store_;
TypeFeedbackOracle* oracle() { return &oracle_; }
Zone* zone() const { return info_->zone(); }
e->set_bounds(Bounds::NarrowLower(e->bounds(), t, isolate_));
}
+ Effects EnterEffects() {
+ store_ = store_.Push();
+ return store_.Top();
+ }
+ void ExitEffects() { store_ = store_.Pop(); }
+
+ int variable_index(Variable* var) {
+ return var->IsStackLocal() ? var->index() :
+ var->IsParameter() ? -var->index() : kNoVar;
+ }
+
void VisitDeclarations(ZoneList<Declaration*>* declarations);
void VisitStatements(ZoneList<Statement*>* statements);
}
+template <typename T>
+void* ZoneSplayTree<T>::operator new(size_t size, Zone* zone) {
+ return zone->New(static_cast<int>(size));
+}
+
+
} } // namespace v8::internal
#endif // V8_ZONE_INL_H_
explicit ZoneAllocationPolicy(Zone* zone) : zone_(zone) { }
INLINE(void* New(size_t size));
INLINE(static void Delete(void *pointer)) { }
+ Zone* zone() { return zone_; }
private:
Zone* zone_;
explicit ZoneSplayTree(Zone* zone)
: SplayTree<Config, ZoneAllocationPolicy>(ZoneAllocationPolicy(zone)) {}
~ZoneSplayTree();
+
+ INLINE(void* operator new(size_t size, Zone* zone));
};
'../../src/double.h',
'../../src/dtoa.cc',
'../../src/dtoa.h',
+ '../../src/effects.h',
'../../src/elements-kind.cc',
'../../src/elements-kind.h',
'../../src/elements.cc',