Commit 6fda4e4c authored by rossberg@chromium.org's avatar rossberg@chromium.org

Collect type feedback in separate pass and store it in AST

Notes:

- For now, just adds the missing type info fields to the AST nodes directly.
  I'd like to factor that out more nicely in a follow-up CL.

- All type feedback now is uniformly collected through AST nodes'
  RecordTypeFeedback functions. At some point, this logic should be moved
  out of ast.cc.

- The typing pass currently simulates the exact same conditions under
  which feedback was collected in Hydrogen before. That also should be
  made more generic in the future.

- Type information itself is unchanged. Making it more regular is
  yet more future work.

Some additional cleanups:

- Lifted out nested class ObjectLiteral::Property, to enable forward declaration.
- Moved around some auxiliary enums.

R=svenpanne@chromium.org
BUG=

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14825 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 0a428961
......@@ -30,6 +30,7 @@
#include <cmath> // For isfinite.
#include "builtins.h"
#include "code-stubs.h"
#include "contexts.h"
#include "conversions.h"
#include "hashmap.h"
#include "parser.h"
......@@ -181,9 +182,9 @@ LanguageMode FunctionLiteral::language_mode() const {
}
ObjectLiteral::Property::Property(Literal* key,
Expression* value,
Isolate* isolate) {
ObjectLiteralProperty::ObjectLiteralProperty(Literal* key,
Expression* value,
Isolate* isolate) {
emit_store_ = true;
key_ = key;
value_ = value;
......@@ -201,7 +202,8 @@ ObjectLiteral::Property::Property(Literal* key,
}
ObjectLiteral::Property::Property(bool is_getter, FunctionLiteral* value) {
ObjectLiteralProperty::ObjectLiteralProperty(bool is_getter,
FunctionLiteral* value) {
emit_store_ = true;
value_ = value;
kind_ = is_getter ? GETTER : SETTER;
......@@ -415,6 +417,16 @@ bool FunctionDeclaration::IsInlineable() const {
// ----------------------------------------------------------------------------
// Recording of type feedback
void ForInStatement::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
for_in_type_ = static_cast<ForInType>(oracle->ForInType(this));
}
void Expression::RecordToBooleanTypeFeedback(TypeFeedbackOracle* oracle) {
to_boolean_types_ = oracle->ToBooleanTypes(test_id());
}
void Property::RecordTypeFeedback(TypeFeedbackOracle* oracle,
Zone* zone) {
// Record type feedback from the oracle in the AST.
......@@ -486,6 +498,7 @@ void CountOperation::RecordTypeFeedback(TypeFeedbackOracle* oracle,
oracle->CollectKeyedReceiverTypes(id, &receiver_types_);
}
store_mode_ = oracle->GetStoreMode(id);
type_ = oracle->IncrementType(this);
}
......@@ -575,6 +588,32 @@ bool Call::ComputeGlobalTarget(Handle<GlobalObject> global,
}
Handle<JSObject> Call::GetPrototypeForPrimitiveCheck(
CheckType check, Isolate* isolate) {
v8::internal::Context* native_context = isolate->context()->native_context();
JSFunction* function = NULL;
switch (check) {
case RECEIVER_MAP_CHECK:
UNREACHABLE();
break;
case STRING_CHECK:
function = native_context->string_function();
break;
case SYMBOL_CHECK:
function = native_context->symbol_function();
break;
case NUMBER_CHECK:
function = native_context->number_function();
break;
case BOOLEAN_CHECK:
function = native_context->boolean_function();
break;
}
ASSERT(function != NULL);
return Handle<JSObject>(JSObject::cast(function->instance_prototype()));
}
void Call::RecordTypeFeedback(TypeFeedbackOracle* oracle,
CallKind call_kind) {
is_monomorphic_ = oracle->CallIsMonomorphic(this);
......@@ -606,8 +645,7 @@ void Call::RecordTypeFeedback(TypeFeedbackOracle* oracle,
map = receiver_types_.at(0);
} else {
ASSERT(check_type_ != RECEIVER_MAP_CHECK);
holder_ = Handle<JSObject>(
oracle->GetPrototypeForPrimitiveCheck(check_type_));
holder_ = GetPrototypeForPrimitiveCheck(check_type_, oracle->isolate());
map = Handle<Map>(holder_->map());
}
is_monomorphic_ = ComputeTarget(map, name);
......@@ -622,6 +660,12 @@ void CallNew::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
target_ = oracle->GetCallNewTarget(this);
elements_kind_ = oracle->GetCallNewElementsKind(this);
}
Handle<Object> alloc_elements_kind = oracle->GetInfo(CallNewFeedbackId());
// if (alloc_elements_kind->IsSmi())
// alloc_elements_kind_ = Handle<Smi>::cast(alloc_elements_kind);
alloc_elements_kind_ = alloc_elements_kind->IsSmi()
? Handle<Smi>::cast(alloc_elements_kind)
: handle(Smi::FromInt(GetInitialFastElementsKind()), oracle->isolate());
}
......@@ -632,6 +676,30 @@ void ObjectLiteral::Property::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
}
void UnaryOperation::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
type_ = oracle->UnaryType(this);
}
void BinaryOperation::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
oracle->BinaryType(this, &left_type_, &right_type_, &result_type_);
}
void CompareOperation::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
oracle->CompareType(this, &left_type_, &right_type_, &overall_type_);
if (!overall_type_.IsUninitialized() && overall_type_.IsNonPrimitive() &&
(op_ == Token::EQ || op_ == Token::EQ_STRICT)) {
map_ = oracle->GetCompareMap(this);
} else {
// May be a compare to nil.
map_ = oracle->CompareNilMonomorphicReceiverType(this);
if (op_ != Token::EQ_STRICT)
compare_nil_types_ = oracle->CompareNilTypes(this);
}
}
// ----------------------------------------------------------------------------
// Implementation of AstVisitor
......
......@@ -39,6 +39,7 @@
#include "small-pointer-list.h"
#include "smart-pointers.h"
#include "token.h"
#include "type-info.h"
#include "utils.h"
#include "variables.h"
#include "interface.h"
......@@ -373,6 +374,10 @@ class Expression: public AstNode {
return STANDARD_STORE;
}
// TODO(rossberg): this should move to its own AST node eventually.
void RecordToBooleanTypeFeedback(TypeFeedbackOracle* oracle);
byte to_boolean_types() const { return to_boolean_types_; }
BailoutId id() const { return id_; }
TypeFeedbackId test_id() const { return test_id_; }
......@@ -382,6 +387,8 @@ class Expression: public AstNode {
test_id_(GetNextId(isolate)) {}
private:
byte to_boolean_types_;
const BailoutId id_;
const TypeFeedbackId test_id_;
};
......@@ -716,6 +723,7 @@ class IterationStatement: public BreakableStatement {
private:
Statement* body_;
Label continue_target_;
const BailoutId osr_entry_id_;
};
......@@ -751,7 +759,9 @@ class DoWhileStatement: public IterationStatement {
private:
Expression* cond_;
int condition_position_;
const BailoutId continue_id_;
const BailoutId back_edge_id_;
};
......@@ -788,8 +798,10 @@ class WhileStatement: public IterationStatement {
private:
Expression* cond_;
// True if there is a function literal subexpression in the condition.
bool may_have_function_literal_;
const BailoutId body_id_;
};
......@@ -843,9 +855,11 @@ class ForStatement: public IterationStatement {
Statement* init_;
Expression* cond_;
Statement* next_;
// True if there is a function literal subexpression in the condition.
bool may_have_function_literal_;
Variable* loop_variable_;
const BailoutId continue_id_;
const BailoutId body_id_;
};
......@@ -859,6 +873,7 @@ class ForInStatement: public IterationStatement {
IterationStatement::Initialize(body);
each_ = each;
enumerable_ = enumerable;
for_in_type_ = SLOW_FOR_IN;
}
Expression* each() const { return each_; }
......@@ -870,6 +885,9 @@ class ForInStatement: public IterationStatement {
BailoutId PrepareId() const { return prepare_id_; }
TypeFeedbackId ForInFeedbackId() const { return reuse(PrepareId()); }
void RecordTypeFeedback(TypeFeedbackOracle* oracle);
enum ForInType { FAST_FOR_IN, SLOW_FOR_IN };
ForInType for_in_type() const { return for_in_type_; }
protected:
ForInStatement(Isolate* isolate, ZoneStringList* labels)
......@@ -883,6 +901,9 @@ class ForInStatement: public IterationStatement {
private:
Expression* each_;
Expression* enumerable_;
ForInType for_in_type_;
const BailoutId body_id_;
const BailoutId prepare_id_;
};
......@@ -1023,11 +1044,16 @@ class SwitchStatement: public BreakableStatement {
void Initialize(Expression* tag, ZoneList<CaseClause*>* cases) {
tag_ = tag;
cases_ = cases;
switch_type_ = UNKNOWN_SWITCH;
}
Expression* tag() const { return tag_; }
ZoneList<CaseClause*>* cases() const { return cases_; }
enum SwitchType { UNKNOWN_SWITCH, SMI_SWITCH, STRING_SWITCH, GENERIC_SWITCH };
SwitchType switch_type() const { return switch_type_; }
void set_switch_type(SwitchType switch_type) { switch_type_ = switch_type; }
protected:
SwitchStatement(Isolate* isolate, ZoneStringList* labels)
: BreakableStatement(isolate, labels, TARGET_FOR_ANONYMOUS),
......@@ -1037,6 +1063,7 @@ class SwitchStatement: public BreakableStatement {
private:
Expression* tag_;
ZoneList<CaseClause*>* cases_;
SwitchType switch_type_;
};
......@@ -1282,52 +1309,55 @@ class MaterializedLiteral: public Expression {
};
// Property is used for passing information
// about an object literal's properties from the parser
// to the code generator.
class ObjectLiteralProperty: public ZoneObject {
public:
enum Kind {
CONSTANT, // Property with constant value (compile time).
COMPUTED, // Property with computed value (execution time).
MATERIALIZED_LITERAL, // Property value is a materialized literal.
GETTER, SETTER, // Property is an accessor function.
PROTOTYPE // Property is __proto__.
};
ObjectLiteralProperty(Literal* key, Expression* value, Isolate* isolate);
Literal* key() { return key_; }
Expression* value() { return value_; }
Kind kind() { return kind_; }
// Type feedback information.
void RecordTypeFeedback(TypeFeedbackOracle* oracle);
bool IsMonomorphic() { return !receiver_type_.is_null(); }
Handle<Map> GetReceiverType() { return receiver_type_; }
bool IsCompileTimeValue();
void set_emit_store(bool emit_store);
bool emit_store();
protected:
template<class> friend class AstNodeFactory;
ObjectLiteralProperty(bool is_getter, FunctionLiteral* value);
void set_key(Literal* key) { key_ = key; }
private:
Literal* key_;
Expression* value_;
Kind kind_;
bool emit_store_;
Handle<Map> receiver_type_;
};
// An object literal has a boilerplate object that is used
// for minimizing the work when constructing it at runtime.
class ObjectLiteral: public MaterializedLiteral {
public:
// Property is used for passing information
// about an object literal's properties from the parser
// to the code generator.
class Property: public ZoneObject {
public:
enum Kind {
CONSTANT, // Property with constant value (compile time).
COMPUTED, // Property with computed value (execution time).
MATERIALIZED_LITERAL, // Property value is a materialized literal.
GETTER, SETTER, // Property is an accessor function.
PROTOTYPE // Property is __proto__.
};
Property(Literal* key, Expression* value, Isolate* isolate);
Literal* key() { return key_; }
Expression* value() { return value_; }
Kind kind() { return kind_; }
// Type feedback information.
void RecordTypeFeedback(TypeFeedbackOracle* oracle);
bool IsMonomorphic() { return !receiver_type_.is_null(); }
Handle<Map> GetReceiverType() { return receiver_type_; }
bool IsCompileTimeValue();
void set_emit_store(bool emit_store);
bool emit_store();
protected:
template<class> friend class AstNodeFactory;
Property(bool is_getter, FunctionLiteral* value);
void set_key(Literal* key) { key_ = key; }
private:
Literal* key_;
Expression* value_;
Kind kind_;
bool emit_store_;
Handle<Map> receiver_type_;
};
typedef ObjectLiteralProperty Property;
DECLARE_NODE_TYPE(ObjectLiteral)
......@@ -1590,6 +1620,11 @@ class Call: public Expression {
BailoutId ReturnId() const { return return_id_; }
// TODO(rossberg): this should really move somewhere else (and be merged with
// various similar methods in objets.cc), but for now...
static Handle<JSObject> GetPrototypeForPrimitiveCheck(
CheckType check, Isolate* isolate);
#ifdef DEBUG
// Used to assert that the FullCodeGenerator records the return site.
bool return_is_recorded_;
......@@ -1636,10 +1671,11 @@ class CallNew: public Expression {
TypeFeedbackId CallNewFeedbackId() const { return reuse(id()); }
void RecordTypeFeedback(TypeFeedbackOracle* oracle);
virtual bool IsMonomorphic() { return is_monomorphic_; }
Handle<JSFunction> target() { return target_; }
Handle<JSFunction> target() const { return target_; }
ElementsKind elements_kind() const { return elements_kind_; }
Handle<Smi> allocation_elements_kind() const { return alloc_elements_kind_; }
BailoutId ReturnId() const { return return_id_; }
ElementsKind elements_kind() const { return elements_kind_; }
protected:
CallNew(Isolate* isolate,
......@@ -1651,8 +1687,8 @@ class CallNew: public Expression {
arguments_(arguments),
pos_(pos),
is_monomorphic_(false),
return_id_(GetNextId(isolate)),
elements_kind_(GetInitialFastElementsKind()) { }
elements_kind_(GetInitialFastElementsKind()),
return_id_(GetNextId(isolate)) { }
private:
Expression* expression_;
......@@ -1661,9 +1697,10 @@ class CallNew: public Expression {
bool is_monomorphic_;
Handle<JSFunction> target_;
ElementsKind elements_kind_;
Handle<Smi> alloc_elements_kind_;
const BailoutId return_id_;
ElementsKind elements_kind_;
};
......@@ -1713,6 +1750,8 @@ class UnaryOperation: public Expression {
BailoutId MaterializeFalseId() { return materialize_false_id_; }
TypeFeedbackId UnaryOperationFeedbackId() const { return reuse(id()); }
void RecordTypeFeedback(TypeFeedbackOracle* oracle);
TypeInfo type() const { return type_; }
protected:
UnaryOperation(Isolate* isolate,
......@@ -1733,6 +1772,8 @@ class UnaryOperation: public Expression {
Expression* expression_;
int pos_;
TypeInfo type_;
// For unary not (Token::NOT), the AST ids where true and false will
// actually be materialized, respectively.
const BailoutId materialize_true_id_;
......@@ -1754,6 +1795,10 @@ class BinaryOperation: public Expression {
BailoutId RightId() const { return right_id_; }
TypeFeedbackId BinaryOperationFeedbackId() const { return reuse(id()); }
void RecordTypeFeedback(TypeFeedbackOracle* oracle);
TypeInfo left_type() const { return left_type_; }
TypeInfo right_type() const { return right_type_; }
TypeInfo result_type() const { return result_type_; }
protected:
BinaryOperation(Isolate* isolate,
......@@ -1775,6 +1820,11 @@ class BinaryOperation: public Expression {
Expression* left_;
Expression* right_;
int pos_;
TypeInfo left_type_;
TypeInfo right_type_;
TypeInfo result_type_;
// The short-circuit logical operations need an AST ID for their
// right-hand subexpression.
const BailoutId right_id_;
......@@ -1804,6 +1854,7 @@ class CountOperation: public Expression {
virtual KeyedAccessStoreMode GetStoreMode() {
return store_mode_;
}
TypeInfo type() const { return type_; }
BailoutId AssignmentId() const { return assignment_id_; }
......@@ -1832,6 +1883,8 @@ class CountOperation: public Expression {
bool is_monomorphic_ : 1;
KeyedAccessStoreMode store_mode_ : 5; // Windows treats as signed,
// must have extra bit.
TypeInfo type_;
Expression* expression_;
int pos_;
const BailoutId assignment_id_;
......@@ -1851,6 +1904,12 @@ class CompareOperation: public Expression {
// Type feedback information.
TypeFeedbackId CompareOperationFeedbackId() const { return reuse(id()); }
void RecordTypeFeedback(TypeFeedbackOracle* oracle);
TypeInfo left_type() const { return left_type_; }
TypeInfo right_type() const { return right_type_; }
TypeInfo overall_type() const { return overall_type_; }
byte compare_nil_types() const { return compare_nil_types_; }
Handle<Map> map() const { return map_; }
// Match special cases.
bool IsLiteralCompareTypeof(Expression** expr, Handle<String>* check);
......@@ -1876,6 +1935,12 @@ class CompareOperation: public Expression {
Expression* left_;
Expression* right_;
int pos_;
TypeInfo left_type_;
TypeInfo right_type_;
TypeInfo overall_type_;
byte compare_nil_types_;
Handle<Map> map_;
};
......
......@@ -672,7 +672,7 @@ class ArrayConstructorStub: public PlatformCodeStub {
class MathPowStub: public PlatformCodeStub {
public:
enum ExponentType { INTEGER, DOUBLE, TAGGED, ON_STACK};
enum ExponentType { INTEGER, DOUBLE, TAGGED, ON_STACK };
explicit MathPowStub(ExponentType exponent_type)
: exponent_type_(exponent_type) { }
......
......@@ -36,6 +36,7 @@
#include "deoptimizer.h"
#include "full-codegen.h"
#include "gdb-jit.h"
#include "typing.h"
#include "hydrogen.h"
#include "isolate-inl.h"
#include "lithium.h"
......@@ -361,11 +362,11 @@ OptimizingCompiler::Status OptimizingCompiler::CreateGraph() {
PrintF("Compiling method %s using hydrogen\n", *name->ToCString());
isolate()->GetHTracer()->TraceCompilation(info());
}
Handle<Context> native_context(
info()->closure()->context()->native_context());
oracle_ = new(info()->zone()) TypeFeedbackOracle(
code, native_context, isolate(), info()->zone());
graph_builder_ = new(info()->zone()) HOptimizedGraphBuilder(info(), oracle_);
// Type-check the function.
AstTyper::Type(info());
graph_builder_ = new(info()->zone()) HOptimizedGraphBuilder(info());
Timer t(this, &time_taken_to_create_graph_);
graph_ = graph_builder_->CreateGraph();
......
......@@ -449,7 +449,6 @@ class OptimizingCompiler: public ZoneObject {
public:
explicit OptimizingCompiler(CompilationInfo* info)
: info_(info),
oracle_(NULL),
graph_builder_(NULL),
graph_(NULL),
chunk_(NULL),
......@@ -478,7 +477,6 @@ class OptimizingCompiler: public ZoneObject {
private:
CompilationInfo* info_;
TypeFeedbackOracle* oracle_;
HOptimizedGraphBuilder* graph_builder_;
HGraph* graph_;
LChunk* chunk_;
......
......@@ -39,6 +39,7 @@
#include "scopeinfo.h"
#include "scopes.h"
#include "stub-cache.h"
#include "typing.h"
#if V8_TARGET_ARCH_IA32
#include "ia32/lithium-codegen-ia32.h"
......@@ -1934,11 +1935,10 @@ HStoreNamedField* HGraphBuilder::AddStoreMapConstant(HValue *object,
}
HOptimizedGraphBuilder::HOptimizedGraphBuilder(CompilationInfo* info,
TypeFeedbackOracle* oracle)
HOptimizedGraphBuilder::HOptimizedGraphBuilder(CompilationInfo* info)
: HGraphBuilder(info),
function_state_(NULL),
initial_function_state_(this, info, oracle, NORMAL_RETURN),
initial_function_state_(this, info, NORMAL_RETURN),
ast_context_(NULL),
break_scope_(NULL),
inlined_count_(0),
......@@ -3510,11 +3510,9 @@ void HGraph::ComputeMinusZeroChecks() {
// a (possibly inlined) function.
FunctionState::FunctionState(HOptimizedGraphBuilder* owner,
CompilationInfo* info,
TypeFeedbackOracle* oracle,
InliningKind inlining_kind)
: owner_(owner),
compilation_info_(info),
oracle_(oracle),
call_context_(NULL),
inlining_kind_(inlining_kind),
function_return_(NULL),
......@@ -3531,11 +3529,9 @@ FunctionState::FunctionState(HOptimizedGraphBuilder* owner,
if_false->MarkAsInlineReturnTarget();
TestContext* outer_test_context = TestContext::cast(owner->ast_context());
Expression* cond = outer_test_context->condition();
TypeFeedbackOracle* outer_oracle = outer_test_context->oracle();
// The AstContext constructor pushed on the context stack. This newed
// instance is the reason that AstContext can't be BASE_EMBEDDED.
test_context_ =
new TestContext(owner, cond, outer_oracle, if_true, if_false);
test_context_ = new TestContext(owner, cond, if_true, if_false);
} else {
function_return_ = owner->graph()->CreateBasicBlock();
function_return()->MarkAsInlineReturnTarget();
......@@ -3769,8 +3765,7 @@ void TestContext::BuildBranch(HValue* value) {
}
HBasicBlock* empty_true = builder->graph()->CreateBasicBlock();
HBasicBlock* empty_false = builder->graph()->CreateBasicBlock();
TypeFeedbackId test_id = condition()->test_id();
ToBooleanStub::Types expected(oracle()->ToBooleanTypes(test_id));
ToBooleanStub::Types expected(condition()->to_boolean_types());
HBranch* test = new(zone()) HBranch(value, empty_true, empty_false, expected);
builder->current_block()->Finish(test);
......@@ -3825,7 +3820,7 @@ void HOptimizedGraphBuilder::VisitForTypeOf(Expression* expr) {
void HOptimizedGraphBuilder::VisitForControl(Expression* expr,
HBasicBlock* true_block,
HBasicBlock* false_block) {
TestContext for_test(this, expr, oracle(), true_block, false_block);
TestContext for_test(this, expr, true_block, false_block);
Visit(expr);
}
......@@ -4834,9 +4829,8 @@ void HOptimizedGraphBuilder::VisitContinueStatement(
ASSERT(current_block() != NULL);
ASSERT(current_block()->HasPredecessor());
int drop_extra = 0;
HBasicBlock* continue_block = break_scope()->Get(stmt->target(),
CONTINUE,
&drop_extra);
HBasicBlock* continue_block = break_scope()->Get(
stmt->target(), BreakAndContinueScope::CONTINUE, &drop_extra);
Drop(drop_extra);
current_block()->Goto(continue_block);
set_current_block(NULL);
......@@ -4848,9 +4842,8 @@ void HOptimizedGraphBuilder::VisitBreakStatement(BreakStatement* stmt) {
ASSERT(current_block() != NULL);
ASSERT(current_block()->HasPredecessor());
int drop_extra = 0;
HBasicBlock* break_block = break_scope()->Get(stmt->target(),
BREAK,
&drop_extra);
HBasicBlock* break_block = break_scope()->Get(
stmt->target(), BreakAndContinueScope::BREAK, &drop_extra);
Drop(drop_extra);
current_block()->Goto(break_block);
set_current_block(NULL);
......@@ -4941,6 +4934,7 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
ASSERT(!HasStackOverflow());
ASSERT(current_block() != NULL);
ASSERT(current_block()->HasPredecessor());
// We only optimize switch statements with smi-literal smi comparisons,
// with a bounded number of clauses.
const int kCaseClauseLimit = 128;
......@@ -4950,6 +4944,11 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
return Bailout("SwitchStatement: too many clauses");
}
ASSERT(stmt->switch_type() != SwitchStatement::UNKNOWN_SWITCH);
if (stmt->switch_type() == SwitchStatement::GENERIC_SWITCH) {
return Bailout("SwitchStatement: mixed or non-literal switch labels");
}
HValue* context = environment()->LookupContext();
CHECK_ALIVE(VisitForValue(stmt->tag()));
......@@ -4957,34 +4956,11 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
HValue* tag_value = Pop();
HBasicBlock* first_test_block = current_block();
SwitchType switch_type = UNKNOWN_SWITCH;
// 1. Extract clause type
for (int i = 0; i < clause_count; ++i) {
CaseClause* clause = clauses->at(i);
if (clause->is_default()) continue;
if (switch_type == UNKNOWN_SWITCH) {
if (clause->label()->IsSmiLiteral()) {
switch_type = SMI_SWITCH;
} else if (clause->label()->IsStringLiteral()) {
switch_type = STRING_SWITCH;
} else {
return Bailout("SwitchStatement: non-literal switch label");
}
} else if ((switch_type == STRING_SWITCH &&
!clause->label()->IsStringLiteral()) ||
(switch_type == SMI_SWITCH &&
!clause->label()->IsSmiLiteral())) {
return Bailout("SwitchStatement: mixed label types are not supported");
}
}
HUnaryControlInstruction* string_check = NULL;
HBasicBlock* not_string_block = NULL;
// Test switch's tag value if all clauses are string literals
if (switch_type == STRING_SWITCH) {
if (stmt->switch_type() == SwitchStatement::STRING_SWITCH) {
string_check = new(zone()) HIsStringAndBranch(tag_value);
first_test_block = graph()->CreateBasicBlock();
not_string_block = graph()->CreateBasicBlock();
......@@ -4996,7 +4972,7 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
set_current_block(first_test_block);
}
// 2. Build all the tests, with dangling true branches
// 1. Build all the tests, with dangling true branches
BailoutId default_id = BailoutId::None();
for (int i = 0; i < clause_count; ++i) {
CaseClause* clause = clauses->at(i);
......@@ -5004,9 +4980,6 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
default_id = clause->EntryId();
continue;
}
if (switch_type == SMI_SWITCH) {
clause->RecordTypeFeedback(oracle());
}
// Generate a compare and branch.
CHECK_ALIVE(VisitForValue(clause->label()));
......@@ -5017,7 +4990,7 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
HControlInstruction* compare;
if (switch_type == SMI_SWITCH) {
if (stmt->switch_type() == SwitchStatement::SMI_SWITCH) {
if (!clause->IsSmiCompare()) {
// Finish with deoptimize and add uses of enviroment values to
// account for invisible uses.
......@@ -5055,7 +5028,7 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
last_block = CreateJoin(last_block, not_string_block, join_id);
}
// 3. Loop over the clauses and the linked list of tests in lockstep,
// 2. Loop over the clauses and the linked list of tests in lockstep,
// translating the clause bodies.
HBasicBlock* curr_test_block = first_test_block;
HBasicBlock* fall_through_block = NULL;
......@@ -5342,7 +5315,7 @@ void HOptimizedGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
return Bailout("ForInStatement optimization is disabled");
}
if (!oracle()->IsForInFastCase(stmt)) {
if (stmt->for_in_type() != ForInStatement::FAST_FOR_IN) {
return Bailout("ForInStatement is not fast case");
}
......@@ -5483,8 +5456,7 @@ void HOptimizedGraphBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) {
static Handle<SharedFunctionInfo> SearchSharedFunctionInfo(
Code* unoptimized_code, FunctionLiteral* expr) {
int start_position = expr->start_position();
RelocIterator it(unoptimized_code);
for (; !it.done(); it.next()) {
for (RelocIterator it(unoptimized_code); !it.done(); it.next()) {
RelocInfo* rinfo = it.rinfo();
if (rinfo->rmode() != RelocInfo::EMBEDDED_OBJECT) continue;
Object* obj = rinfo->target_object();
......@@ -5505,8 +5477,7 @@ void HOptimizedGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
ASSERT(current_block() != NULL);
ASSERT(current_block()->HasPredecessor());
Handle<SharedFunctionInfo> shared_info =
SearchSharedFunctionInfo(info()->shared_info()->code(),
expr);
SearchSharedFunctionInfo(info()->shared_info()->code(), expr);
if (shared_info.is_null()) {
shared_info = Compiler::BuildFunctionInfo(expr, info()->script());
}
......@@ -5938,7 +5909,6 @@ void HOptimizedGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
case ObjectLiteral::Property::COMPUTED:
if (key->handle()->IsInternalizedString()) {
if (property->emit_store()) {
property->RecordTypeFeedback(oracle());
CHECK_ALIVE(VisitForValue(value));
HValue* value = Pop();
Handle<Map> map = property->GetReceiverType();
......@@ -6474,7 +6444,6 @@ void HOptimizedGraphBuilder::HandlePolymorphicStoreNamedField(
void HOptimizedGraphBuilder::HandlePropertyAssignment(Assignment* expr) {
Property* prop = expr->target()->AsProperty();
ASSERT(prop != NULL);
expr->RecordTypeFeedback(oracle(), zone());
CHECK_ALIVE(VisitForValue(prop->obj()));
if (prop->key()->IsPropertyName()) {
......@@ -6672,8 +6641,6 @@ void HOptimizedGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
return ast_context()->ReturnValue(Pop());
} else if (prop != NULL) {
prop->RecordTypeFeedback(oracle(), zone());
if (prop->key()->IsPropertyName()) {
// Named property.
CHECK_ALIVE(VisitForValue(prop->obj()));
......@@ -6755,7 +6722,6 @@ void HOptimizedGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
Push(load);
if (has_side_effects) AddSimulate(prop->LoadId(), REMOVABLE_SIMULATE);
CHECK_ALIVE(VisitForValue(expr->value()));
HValue* right = Pop();
HValue* left = Pop();
......@@ -6766,7 +6732,6 @@ void HOptimizedGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
AddSimulate(operation->id(), REMOVABLE_SIMULATE);
}
expr->RecordTypeFeedback(oracle(), zone());
HandleKeyedElementAccess(obj, key, instr, expr, expr->AssignmentId(),
RelocInfo::kNoPosition,
true, // is_store
......@@ -7506,7 +7471,6 @@ void HOptimizedGraphBuilder::VisitProperty(Property* expr) {
ASSERT(!HasStackOverflow());
ASSERT(current_block() != NULL);
ASSERT(current_block()->HasPredecessor());
expr->RecordTypeFeedback(oracle(), zone());
if (TryArgumentsAccess(expr)) return;
......@@ -7997,19 +7961,15 @@ bool HOptimizedGraphBuilder::TryInline(CallKind call_kind,
// After this point, we've made a decision to inline this function (so
// TryInline should always return true).
// Save the pending call context and type feedback oracle. Set up new ones
// for the inlined function.
// Type-check the inlined function.
ASSERT(target_shared->has_deoptimization_support());
Handle<Code> unoptimized_code(target_shared->code());
TypeFeedbackOracle target_oracle(
unoptimized_code,
Handle<Context>(target->context()->native_context()),
isolate(),
zone());
AstTyper::Type(&target_info);
// Save the pending call context. Set up new one for the inlined function.
// The function state is new-allocated because we need to delete it
// in two different places.
FunctionState* target_state = new FunctionState(
this, &target_info, &target_oracle, inlining_kind);
this, &target_info, inlining_kind);
HConstant* undefined = graph()->GetConstantUndefined();
bool undefined_receiver = HEnvironment::UseUndefinedReceiver(
......@@ -8083,6 +8043,7 @@ bool HOptimizedGraphBuilder::TryInline(CallKind call_kind,
// Update inlined nodes count.
inlined_count_ += nodes_added;
Handle<Code> unoptimized_code(target_shared->code());
ASSERT(unoptimized_code->kind() == Code::FUNCTION);
Handle<TypeFeedbackInfo> type_info(
TypeFeedbackInfo::cast(unoptimized_code->type_feedback_info()));
......@@ -8299,7 +8260,8 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
HValue* context = environment()->LookupContext();
ASSERT(!expr->holder().is_null());
AddInstruction(new(zone()) HCheckPrototypeMaps(
oracle()->GetPrototypeForPrimitiveCheck(STRING_CHECK),
Call::GetPrototypeForPrimitiveCheck(STRING_CHECK,
expr->holder()->GetIsolate()),
expr->holder(),
zone()));
HInstruction* char_code =
......@@ -8614,8 +8576,6 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) {
}
// Named function call.
expr->RecordTypeFeedback(oracle(), CALL_AS_METHOD);
if (TryCallApply(expr)) return;
CHECK_ALIVE(VisitForValue(prop->obj()));
......@@ -8681,7 +8641,6 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) {
}
} else {
expr->RecordTypeFeedback(oracle(), CALL_AS_FUNCTION);
VariableProxy* proxy = expr->expression()->AsVariableProxy();
bool global_call = proxy != NULL && proxy->var()->IsUnallocated();
......@@ -8817,7 +8776,6 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) {
ASSERT(!HasStackOverflow());
ASSERT(current_block() != NULL);
ASSERT(current_block()->HasPredecessor());
expr->RecordTypeFeedback(oracle());
int argument_count = expr->arguments()->length() + 1; // Plus constructor.
HValue* context = environment()->LookupContext();
......@@ -8877,8 +8835,7 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) {
// information that happened after crankshaft won't be lost. The right
// way to do that is to begin passing the cell to the type feedback oracle
// instead of just the value in the cell. Do this in a follow-up checkin.
Handle<Object> feedback = oracle()->GetInfo(expr->CallNewFeedbackId());
ASSERT(feedback->IsSmi());
Handle<Smi> feedback = expr->allocation_elements_kind();
Handle<JSGlobalPropertyCell> cell =
isolate()->factory()->NewJSGlobalPropertyCell(feedback);
......@@ -8966,6 +8923,7 @@ void HOptimizedGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) {
}
}
void HOptimizedGraphBuilder::VisitDelete(UnaryOperation* expr) {
Property* prop = expr->expression()->AsProperty();
VariableProxy* proxy = expr->expression()->AsVariableProxy();
......@@ -9022,7 +8980,7 @@ void HOptimizedGraphBuilder::VisitSub(UnaryOperation* expr) {
HValue* context = environment()->LookupContext();
HInstruction* instr =
HMul::New(zone(), context, value, graph()->GetConstantMinus1());
TypeInfo info = oracle()->UnaryType(expr);
TypeInfo info = expr->type();
Representation rep = ToRepresentation(info);
if (info.IsUninitialized()) {
AddSoftDeoptimize();
......@@ -9039,7 +8997,7 @@ void HOptimizedGraphBuilder::VisitSub(UnaryOperation* expr) {
void HOptimizedGraphBuilder::VisitBitNot(UnaryOperation* expr) {
CHECK_ALIVE(VisitForValue(expr->expression()));
HValue* value = Pop();
TypeInfo info = oracle()->UnaryType(expr);
TypeInfo info = expr->type();
if (info.IsUninitialized()) {
AddSoftDeoptimize();
}
......@@ -9096,7 +9054,7 @@ HInstruction* HOptimizedGraphBuilder::BuildIncrement(
bool returns_original_input,
CountOperation* expr) {
// The input to the count operation is on top of the expression stack.
TypeInfo info = oracle()->IncrementType(expr);
TypeInfo info = expr->type();
Representation rep = ToRepresentation(info);
if (rep.IsTagged()) {
rep = Representation::Integer32();
......@@ -9210,7 +9168,6 @@ void HOptimizedGraphBuilder::VisitCountOperation(CountOperation* expr) {
} else {
// Argument of the count operation is a property.
ASSERT(prop != NULL);
prop->RecordTypeFeedback(oracle(), zone());
if (prop->key()->IsPropertyName()) {
// Named property.
......@@ -9293,7 +9250,6 @@ void HOptimizedGraphBuilder::VisitCountOperation(CountOperation* expr) {
after = BuildIncrement(returns_original_input, expr);
input = environment()->ExpressionStackAt(0);
expr->RecordTypeFeedback(oracle(), zone());
HandleKeyedElementAccess(obj, key, after, expr, expr->AssignmentId(),
RelocInfo::kNoPosition,
true, // is_store
......@@ -9402,8 +9358,10 @@ HInstruction* HOptimizedGraphBuilder::BuildBinaryOperation(
HValue* left,
HValue* right) {
HValue* context = environment()->LookupContext();
TypeInfo left_info, right_info, result_info, combined_info;
oracle()->BinaryType(expr, &left_info, &right_info, &result_info);
TypeInfo left_info = expr->left_type();
TypeInfo right_info = expr->right_type();
TypeInfo result_info = expr->result_type();
TypeInfo combined_info;
Representation left_rep = ToRepresentation(left_info);
Representation right_rep = ToRepresentation(right_info);
Representation result_rep = ToRepresentation(result_info);
......@@ -9561,8 +9519,7 @@ void HOptimizedGraphBuilder::VisitLogicalExpression(BinaryOperation* expr) {
// We need an extra block to maintain edge-split form.
HBasicBlock* empty_block = graph()->CreateBasicBlock();
HBasicBlock* eval_right = graph()->CreateBasicBlock();
TypeFeedbackId test_id = expr->left()->test_id();
ToBooleanStub::Types expected(oracle()->ToBooleanTypes(test_id));
ToBooleanStub::Types expected(expr->left()->to_boolean_types());
HBranch* test = is_logical_and
? new(zone()) HBranch(left_value, eval_right, empty_block, expected)
: new(zone()) HBranch(left_value, empty_block, eval_right, expected);
......@@ -9730,16 +9687,17 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
return ast_context()->ReturnControl(instr, expr->id());
}
TypeInfo left_type, right_type, overall_type_info;
oracle()->CompareType(expr, &left_type, &right_type, &overall_type_info);
Representation combined_rep = ToRepresentation(overall_type_info);
TypeInfo left_type = expr->left_type();
TypeInfo right_type = expr->right_type();
TypeInfo overall_type = expr->overall_type();
Representation combined_rep = ToRepresentation(overall_type);
Representation left_rep = ToRepresentation(left_type);
Representation right_rep = ToRepresentation(right_type);
// Check if this expression was ever executed according to type feedback.
// Note that for the special typeof/null/undefined cases we get unknown here.
if (overall_type_info.IsUninitialized()) {
if (overall_type.IsUninitialized()) {
AddSoftDeoptimize();
overall_type_info = left_type = right_type = TypeInfo::Unknown();
overall_type = left_type = right_type = TypeInfo::Unknown();
}
CHECK_ALIVE(VisitForValue(expr->left()));
......@@ -9811,12 +9769,12 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
HIn* result = new(zone()) HIn(context, left, right);
result->set_position(expr->position());
return ast_context()->ReturnInstruction(result, expr->id());
} else if (overall_type_info.IsNonPrimitive()) {
} else if (overall_type.IsNonPrimitive()) {
switch (op) {
case Token::EQ:
case Token::EQ_STRICT: {
// Can we get away with map check and not instance type check?
Handle<Map> map = oracle()->GetCompareMap(expr);
Handle<Map> map = expr->map();
if (!map.is_null()) {
AddCheckMapsWithTransitions(left, map);
AddCheckMapsWithTransitions(right, map);
......@@ -9838,7 +9796,7 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
default:
return Bailout("Unsupported non-primitive compare");
}
} else if (overall_type_info.IsInternalizedString() &&
} else if (overall_type.IsInternalizedString() &&
Token::IsEqualityOp(op)) {
BuildCheckNonSmi(left);
AddInstruction(HCheckInstanceType::NewIsInternalizedString(left, zone()));
......@@ -9876,18 +9834,15 @@ void HOptimizedGraphBuilder::HandleLiteralCompareNil(CompareOperation* expr,
EqualityKind kind =
expr->op() == Token::EQ_STRICT ? kStrictEquality : kNonStrictEquality;
HIfContinuation continuation;
TypeFeedbackId id = expr->CompareOperationFeedbackId();
CompareNilICStub::Types types;
if (kind == kStrictEquality) {
types.Add((nil == kNullValue) ? CompareNilICStub::NULL_TYPE :
CompareNilICStub::UNDEFINED);
} else {
types = CompareNilICStub::Types(oracle()->CompareNilTypes(id));
if (types.IsEmpty()) {
types = CompareNilICStub::Types::FullCompare();
}
types = CompareNilICStub::Types(expr->compare_nil_types());
if (types.IsEmpty()) types = CompareNilICStub::Types::FullCompare();
}
Handle<Map> map_handle(oracle()->CompareNilMonomorphicReceiverType(id));
Handle<Map> map_handle = expr->map();
BuildCompareNil(value, kind, types, map_handle,
expr->position(), &continuation);
return ast_context()->ReturnContinuation(&continuation, expr->id());
......
......@@ -34,7 +34,6 @@
#include "ast.h"
#include "compiler.h"
#include "hydrogen-instructions.h"
#include "type-info.h"
#include "zone.h"
#include "scopes.h"
......@@ -792,12 +791,10 @@ class TestContext: public AstContext {
public:
TestContext(HOptimizedGraphBuilder* owner,
Expression* condition,
TypeFeedbackOracle* oracle,
HBasicBlock* if_true,
HBasicBlock* if_false)
: AstContext(owner, Expression::kTest),
condition_(condition),
oracle_(oracle),
if_true_(if_true),
if_false_(if_false) {
}
......@@ -814,7 +811,6 @@ class TestContext: public AstContext {
}
Expression* condition() const { return condition_; }
TypeFeedbackOracle* oracle() const { return oracle_; }
HBasicBlock* if_true() const { return if_true_; }
HBasicBlock* if_false() const { return if_false_; }
......@@ -824,7 +820,6 @@ class TestContext: public AstContext {
void BuildBranch(HValue* value);
Expression* condition_;
TypeFeedbackOracle* oracle_;
HBasicBlock* if_true_;
HBasicBlock* if_false_;
};
......@@ -834,12 +829,10 @@ class FunctionState {
public:
FunctionState(HOptimizedGraphBuilder* owner,
CompilationInfo* info,
TypeFeedbackOracle* oracle,
InliningKind inlining_kind);
~FunctionState();
CompilationInfo* compilation_info() { return compilation_info_; }
TypeFeedbackOracle* oracle() { return oracle_; }
AstContext* call_context() { return call_context_; }
InliningKind inlining_kind() const { return inlining_kind_; }
HBasicBlock* function_return() { return function_return_; }
......@@ -865,7 +858,6 @@ class FunctionState {
HOptimizedGraphBuilder* owner_;
CompilationInfo* compilation_info_;
TypeFeedbackOracle* oracle_;
// During function inlining, expression context of the call being
// inlined. NULL when not inlining.
......@@ -1353,9 +1345,6 @@ class HGraphBuilder {
class HOptimizedGraphBuilder: public HGraphBuilder, public AstVisitor {
public:
enum BreakType { BREAK, CONTINUE };
enum SwitchType { UNKNOWN_SWITCH, SMI_SWITCH, STRING_SWITCH };
// A class encapsulating (lazily-allocated) break and continue blocks for
// a breakable statement. Separated from BreakAndContinueScope so that it
// can have a separate lifetime.
......@@ -1400,6 +1389,7 @@ class HOptimizedGraphBuilder: public HGraphBuilder, public AstVisitor {
BreakAndContinueScope* next() { return next_; }
// Search the break stack for a break or continue target.
enum BreakType { BREAK, CONTINUE };
HBasicBlock* Get(BreakableStatement* stmt, BreakType type, int* drop_extra);
private:
......@@ -1408,7 +1398,7 @@ class HOptimizedGraphBuilder: public HGraphBuilder, public AstVisitor {
BreakAndContinueScope* next_;
};
HOptimizedGraphBuilder(CompilationInfo* info, TypeFeedbackOracle* oracle);
explicit HOptimizedGraphBuilder(CompilationInfo* info);
virtual bool BuildGraph();
......@@ -1426,8 +1416,6 @@ class HOptimizedGraphBuilder: public HGraphBuilder, public AstVisitor {
HBasicBlock* second,
BailoutId join_id);
TypeFeedbackOracle* oracle() const { return function_state()->oracle(); }
FunctionState* function_state() const { return function_state_; }
void VisitDeclarations(ZoneList<Declaration*>* declarations);
......
......@@ -184,10 +184,11 @@ bool TypeFeedbackOracle::ObjectLiteralStoreIsMonomorphic(
}
bool TypeFeedbackOracle::IsForInFastCase(ForInStatement* stmt) {
byte TypeFeedbackOracle::ForInType(ForInStatement* stmt) {
Handle<Object> value = GetInfo(stmt->ForInFeedbackId());
return value->IsSmi() &&
Smi::cast(*value)->value() == TypeFeedbackCells::kForInFastCaseMarker;
Smi::cast(*value)->value() == TypeFeedbackCells::kForInFastCaseMarker
? ForInStatement::FAST_FOR_IN : ForInStatement::SLOW_FOR_IN;
}
......@@ -221,8 +222,8 @@ Handle<Map> TypeFeedbackOracle::StoreMonomorphicReceiverType(
Handle<Map> TypeFeedbackOracle::CompareNilMonomorphicReceiverType(
TypeFeedbackId id) {
Handle<Object> maybe_code = GetInfo(id);
CompareOperation* expr) {
Handle<Object> maybe_code = GetInfo(expr->CompareOperationFeedbackId());
if (maybe_code->IsCode()) {
Map* map = Handle<Code>::cast(maybe_code)->FindFirstMap();
if (map == NULL) return Handle<Map>();
......@@ -296,31 +297,6 @@ CheckType TypeFeedbackOracle::GetCallCheckType(Call* expr) {
}
Handle<JSObject> TypeFeedbackOracle::GetPrototypeForPrimitiveCheck(
CheckType check) {
JSFunction* function = NULL;
switch (check) {
case RECEIVER_MAP_CHECK:
UNREACHABLE();
break;
case STRING_CHECK:
function = native_context_->string_function();
break;
case SYMBOL_CHECK:
function = native_context_->symbol_function();
break;
case NUMBER_CHECK:
function = native_context_->number_function();
break;
case BOOLEAN_CHECK:
function = native_context_->boolean_function();
break;
}
ASSERT(function != NULL);
return Handle<JSObject>(JSObject::cast(function->instance_prototype()));
}
Handle<JSFunction> TypeFeedbackOracle::GetCallTarget(Call* expr) {
return Handle<JSFunction>::cast(GetInfo(expr->CallFeedbackId()));
}
......@@ -641,8 +617,8 @@ byte TypeFeedbackOracle::ToBooleanTypes(TypeFeedbackId id) {
}
byte TypeFeedbackOracle::CompareNilTypes(TypeFeedbackId id) {
Handle<Object> object = GetInfo(id);
byte TypeFeedbackOracle::CompareNilTypes(CompareOperation* expr) {
Handle<Object> object = GetInfo(expr->CompareOperationFeedbackId());
if (object->IsCode() &&
Handle<Code>::cast(object)->is_compare_nil_ic_stub()) {
return Handle<Code>::cast(object)->compare_nil_types();
......
......@@ -29,7 +29,6 @@
#define V8_TYPE_INFO_H_
#include "allocation.h"
#include "ast.h"
#include "globals.h"
#include "zone-inl.h"
......@@ -232,6 +231,8 @@ class ICStub;
class Property;
class SmallMapList;
class UnaryOperation;
class ObjectLiteral;
class ObjectLiteralProperty;
class TypeFeedbackOracle: public ZoneObject {
......@@ -248,13 +249,15 @@ class TypeFeedbackOracle: public ZoneObject {
bool StoreIsPolymorphic(TypeFeedbackId ast_id);
bool CallIsMonomorphic(Call* expr);
bool CallNewIsMonomorphic(CallNew* expr);
bool ObjectLiteralStoreIsMonomorphic(ObjectLiteral::Property* prop);
bool ObjectLiteralStoreIsMonomorphic(ObjectLiteralProperty* prop);
bool IsForInFastCase(ForInStatement* expr);
// TODO(1571) We can't use ForInStatement::ForInType as the return value due
// to various cycles in our headers.
byte ForInType(ForInStatement* expr);
Handle<Map> LoadMonomorphicReceiverType(Property* expr);
Handle<Map> StoreMonomorphicReceiverType(TypeFeedbackId id);
Handle<Map> CompareNilMonomorphicReceiverType(TypeFeedbackId id);
Handle<Map> CompareNilMonomorphicReceiverType(CompareOperation* expr);
KeyedAccessStoreMode GetStoreMode(TypeFeedbackId ast_id);
......@@ -278,26 +281,24 @@ class TypeFeedbackOracle: public ZoneObject {
void CollectPolymorphicMaps(Handle<Code> code, SmallMapList* types);
CheckType GetCallCheckType(Call* expr);
Handle<JSObject> GetPrototypeForPrimitiveCheck(CheckType check);
Handle<JSFunction> GetCallTarget(Call* expr);
Handle<JSFunction> GetCallNewTarget(CallNew* expr);
ElementsKind GetCallNewElementsKind(CallNew* expr);
Handle<Map> GetObjectLiteralStoreMap(ObjectLiteral::Property* prop);
Handle<Map> GetObjectLiteralStoreMap(ObjectLiteralProperty* prop);
bool LoadIsBuiltin(Property* expr, Builtins::Name id);
bool LoadIsStub(Property* expr, ICStub* stub);
// TODO(1571) We can't use ToBooleanStub::Types as the return value because
// of various cylces in our headers. Death to tons of implementations in
// of various cycles in our headers. Death to tons of implementations in
// headers!! :-P
byte ToBooleanTypes(TypeFeedbackId ast_id);
// TODO(1571) We can't use CompareNilICStub::Types as the return value because
// of various cylces in our headers. Death to tons of implementations in
// headers!! :-P
byte CompareNilTypes(TypeFeedbackId ast_id);
byte CompareNilTypes(CompareOperation* expr);
// Get type information for arithmetic operations and compares.
TypeInfo UnaryType(UnaryOperation* expr);
......@@ -314,6 +315,7 @@ class TypeFeedbackOracle: public ZoneObject {
TypeInfo IncrementType(CountOperation* expr);
Zone* zone() const { return zone_; }
Isolate* isolate() const { return isolate_; }
private:
void CollectReceiverTypes(TypeFeedbackId ast_id,
......
// 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.
#include "typing.h"
#include "v8.h"
#include "parser.h" // for CompileTimeValue; TODO(rossberg): should move
#include "scopes.h"
namespace v8 {
namespace internal {
AstTyper::AstTyper(CompilationInfo* info)
: info_(info),
oracle_(
Handle<Code>(info->closure()->shared()->code()),
Handle<Context>(info->closure()->context()->native_context()),
info->isolate(),
info->zone()) {
InitializeAstVisitor();
}
#define CHECK_ALIVE(call) \
do { \
call; \
if (visitor->HasStackOverflow()) return; \
} while (false)
void AstTyper::Type(CompilationInfo* info) {
AstTyper* visitor = new(info->zone()) AstTyper(info);
Scope* scope = info->scope();
// Handle implicit declaration of the function name in named function
// expressions before other declarations.
if (scope->is_function_scope() && scope->function() != NULL) {
CHECK_ALIVE(visitor->VisitVariableDeclaration(scope->function()));
}
CHECK_ALIVE(visitor->VisitDeclarations(scope->declarations()));
CHECK_ALIVE(visitor->VisitStatements(info->function()->body()));
}
#undef CHECK_ALIVE
#define CHECK_ALIVE(call) \
do { \
call; \
if (HasStackOverflow()) return; \
} while (false)
void AstTyper::VisitStatements(ZoneList<Statement*>* stmts) {
ASSERT(!HasStackOverflow());
for (int i = 0; i < stmts->length(); ++i) {
Statement* stmt = stmts->at(i);
CHECK_ALIVE(Visit(stmt));
}
}
void AstTyper::VisitBlock(Block* stmt) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(VisitStatements(stmt->statements()));
}
void AstTyper::VisitExpressionStatement(ExpressionStatement* stmt) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(Visit(stmt->expression()));
}
void AstTyper::VisitEmptyStatement(EmptyStatement* stmt) {
ASSERT(!HasStackOverflow());
}
void AstTyper::VisitIfStatement(IfStatement* stmt) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(Visit(stmt->condition()));
CHECK_ALIVE(Visit(stmt->then_statement()));
CHECK_ALIVE(Visit(stmt->else_statement()));
if (!stmt->condition()->ToBooleanIsTrue() &&
!stmt->condition()->ToBooleanIsFalse()) {
stmt->condition()->RecordToBooleanTypeFeedback(oracle());
}
}
void AstTyper::VisitContinueStatement(ContinueStatement* stmt) {
ASSERT(!HasStackOverflow());
}
void AstTyper::VisitBreakStatement(BreakStatement* stmt) {
ASSERT(!HasStackOverflow());
}
void AstTyper::VisitReturnStatement(ReturnStatement* stmt) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(Visit(stmt->expression()));
// TODO(rossberg): we only need this for inlining into test contexts...
stmt->expression()->RecordToBooleanTypeFeedback(oracle());
}
void AstTyper::VisitWithStatement(WithStatement* stmt) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(stmt->expression());
CHECK_ALIVE(stmt->statement());
}
void AstTyper::VisitSwitchStatement(SwitchStatement* stmt) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(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();
CHECK_ALIVE(Visit(label));
SwitchStatement::SwitchType label_switch_type =
label->IsSmiLiteral() ? SwitchStatement::SMI_SWITCH :
label->IsStringLiteral() ? SwitchStatement::STRING_SWITCH :
SwitchStatement::GENERIC_SWITCH;
if (switch_type == SwitchStatement::UNKNOWN_SWITCH)
switch_type = label_switch_type;
else if (switch_type != label_switch_type)
switch_type = SwitchStatement::GENERIC_SWITCH;
}
CHECK_ALIVE(VisitStatements(clause->statements()));
}
if (switch_type == SwitchStatement::UNKNOWN_SWITCH)
switch_type = SwitchStatement::GENERIC_SWITCH;
stmt->set_switch_type(switch_type);
// 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) {
CaseClause* clause = clauses->at(i);
if (!clause->is_default())
clause->RecordTypeFeedback(oracle());
}
}
}
void AstTyper::VisitDoWhileStatement(DoWhileStatement* stmt) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(Visit(stmt->body()));
CHECK_ALIVE(Visit(stmt->cond()));
if (!stmt->cond()->ToBooleanIsTrue()) {
stmt->cond()->RecordToBooleanTypeFeedback(oracle());
}
}
void AstTyper::VisitWhileStatement(WhileStatement* stmt) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(Visit(stmt->cond()));
CHECK_ALIVE(Visit(stmt->body()));
if (!stmt->cond()->ToBooleanIsTrue()) {
stmt->cond()->RecordToBooleanTypeFeedback(oracle());
}
}
void AstTyper::VisitForStatement(ForStatement* stmt) {
ASSERT(!HasStackOverflow());
if (stmt->init() != NULL) {
CHECK_ALIVE(Visit(stmt->init()));
}
if (stmt->cond() != NULL) {
CHECK_ALIVE(Visit(stmt->cond()));
stmt->cond()->RecordToBooleanTypeFeedback(oracle());
}
CHECK_ALIVE(Visit(stmt->body()));
if (stmt->next() != NULL) {
CHECK_ALIVE(Visit(stmt->next()));
}
}
void AstTyper::VisitForInStatement(ForInStatement* stmt) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(Visit(stmt->enumerable()));
CHECK_ALIVE(Visit(stmt->body()));
stmt->RecordTypeFeedback(oracle());
}
void AstTyper::VisitTryCatchStatement(TryCatchStatement* stmt) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(Visit(stmt->try_block()));
CHECK_ALIVE(Visit(stmt->catch_block()));
}
void AstTyper::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(Visit(stmt->try_block()));
CHECK_ALIVE(Visit(stmt->finally_block()));
}
void AstTyper::VisitDebuggerStatement(DebuggerStatement* stmt) {
ASSERT(!HasStackOverflow());
}
void AstTyper::VisitFunctionLiteral(FunctionLiteral* expr) {
ASSERT(!HasStackOverflow());
}
void AstTyper::VisitSharedFunctionInfoLiteral(SharedFunctionInfoLiteral* expr) {
ASSERT(!HasStackOverflow());
}
void AstTyper::VisitConditional(Conditional* expr) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(Visit(expr->condition()));
CHECK_ALIVE(Visit(expr->then_expression()));
CHECK_ALIVE(Visit(expr->else_expression()));
expr->condition()->RecordToBooleanTypeFeedback(oracle());
}
void AstTyper::VisitVariableProxy(VariableProxy* expr) {
ASSERT(!HasStackOverflow());
}
void AstTyper::VisitLiteral(Literal* expr) {
ASSERT(!HasStackOverflow());
}
void AstTyper::VisitRegExpLiteral(RegExpLiteral* expr) {
ASSERT(!HasStackOverflow());
}
void AstTyper::VisitObjectLiteral(ObjectLiteral* expr) {
ASSERT(!HasStackOverflow());
ZoneList<ObjectLiteral::Property*>* properties = expr->properties();
for (int i = 0; i < properties->length(); ++i) {
ObjectLiteral::Property* prop = properties->at(i);
CHECK_ALIVE(Visit(prop->value()));
if ((prop->kind() == ObjectLiteral::Property::MATERIALIZED_LITERAL &&
!CompileTimeValue::IsCompileTimeValue(prop->value())) ||
prop->kind() == ObjectLiteral::Property::COMPUTED) {
if (prop->key()->handle()->IsInternalizedString() && prop->emit_store())
prop->RecordTypeFeedback(oracle());
}
}
}
void AstTyper::VisitArrayLiteral(ArrayLiteral* expr) {
ASSERT(!HasStackOverflow());
ZoneList<Expression*>* values = expr->values();
for (int i = 0; i < values->length(); ++i) {
Expression* value = values->at(i);
CHECK_ALIVE(Visit(value));
}
}
void AstTyper::VisitAssignment(Assignment* expr) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(Visit(expr->target()));
CHECK_ALIVE(Visit(expr->value()));
// TODO(rossberg): Can we clean this up?
if (expr->is_compound()) {
CHECK_ALIVE(Visit(expr->binary_operation()));
Expression* target = expr->target();
Property* prop = target->AsProperty();
if (prop != NULL) {
prop->RecordTypeFeedback(oracle(), zone());
if (!prop->key()->IsPropertyName()) // i.e., keyed
expr->RecordTypeFeedback(oracle(), zone());
}
return;
}
if (expr->target()->AsProperty())
expr->RecordTypeFeedback(oracle(), zone());
}
void AstTyper::VisitYield(Yield* expr) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(Visit(expr->generator_object()));
CHECK_ALIVE(Visit(expr->expression()));
}
void AstTyper::VisitThrow(Throw* expr) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(Visit(expr->exception()));
}
void AstTyper::VisitProperty(Property* expr) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(Visit(expr->obj()));
CHECK_ALIVE(Visit(expr->key()));
expr->RecordTypeFeedback(oracle(), zone());
}
void AstTyper::VisitCall(Call* expr) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(Visit(expr->expression()));
ZoneList<Expression*>* args = expr->arguments();
for (int i = 0; i < args->length(); ++i) {
Expression* arg = args->at(i);
CHECK_ALIVE(Visit(arg));
}
Expression* callee = expr->expression();
Property* prop = callee->AsProperty();
if (prop != NULL) {
if (prop->key()->IsPropertyName())
expr->RecordTypeFeedback(oracle(), CALL_AS_METHOD);
} else {
expr->RecordTypeFeedback(oracle(), CALL_AS_FUNCTION);
}
}
void AstTyper::VisitCallNew(CallNew* expr) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(Visit(expr->expression()));
ZoneList<Expression*>* args = expr->arguments();
for (int i = 0; i < args->length(); ++i) {
Expression* arg = args->at(i);
CHECK_ALIVE(Visit(arg));
}
expr->RecordTypeFeedback(oracle());
}
void AstTyper::VisitCallRuntime(CallRuntime* expr) {
ASSERT(!HasStackOverflow());
ZoneList<Expression*>* args = expr->arguments();
for (int i = 0; i < args->length(); ++i) {
Expression* arg = args->at(i);
CHECK_ALIVE(Visit(arg));
}
}
void AstTyper::VisitUnaryOperation(UnaryOperation* expr) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(Visit(expr->expression()));
expr->RecordTypeFeedback(oracle());
if (expr->op() == Token::NOT) {
// TODO(rossberg): only do in test or value context.
expr->expression()->RecordToBooleanTypeFeedback(oracle());
}
}
void AstTyper::VisitCountOperation(CountOperation* expr) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(Visit(expr->expression()));
expr->RecordTypeFeedback(oracle(), zone());
Property* prop = expr->expression()->AsProperty();
if (prop != NULL) {
prop->RecordTypeFeedback(oracle(), zone());
}
}
void AstTyper::VisitBinaryOperation(BinaryOperation* expr) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(Visit(expr->left()));
CHECK_ALIVE(Visit(expr->right()));
expr->RecordTypeFeedback(oracle());
if (expr->op() == Token::OR || expr->op() == Token::AND) {
expr->left()->RecordToBooleanTypeFeedback(oracle());
}
}
void AstTyper::VisitCompareOperation(CompareOperation* expr) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(Visit(expr->left()));
CHECK_ALIVE(Visit(expr->right()));
expr->RecordTypeFeedback(oracle());
}
void AstTyper::VisitThisFunction(ThisFunction* expr) {
ASSERT(!HasStackOverflow());
}
void AstTyper::VisitDeclarations(ZoneList<Declaration*>* decls) {
ASSERT(!HasStackOverflow());
for (int i = 0; i < decls->length(); ++i) {
Declaration* decl = decls->at(i);
CHECK_ALIVE(Visit(decl));
}
}
void AstTyper::VisitVariableDeclaration(VariableDeclaration* declaration) {
ASSERT(!HasStackOverflow());
}
void AstTyper::VisitFunctionDeclaration(FunctionDeclaration* declaration) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(Visit(declaration->fun()));
}
void AstTyper::VisitModuleDeclaration(ModuleDeclaration* declaration) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(Visit(declaration->module()));
}
void AstTyper::VisitImportDeclaration(ImportDeclaration* declaration) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(Visit(declaration->module()));
}
void AstTyper::VisitExportDeclaration(ExportDeclaration* declaration) {
ASSERT(!HasStackOverflow());
}
void AstTyper::VisitModuleLiteral(ModuleLiteral* module) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(Visit(module->body()));
}
void AstTyper::VisitModuleVariable(ModuleVariable* module) {
ASSERT(!HasStackOverflow());
}
void AstTyper::VisitModulePath(ModulePath* module) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(Visit(module->module()));
}
void AstTyper::VisitModuleUrl(ModuleUrl* module) {
ASSERT(!HasStackOverflow());
}
void AstTyper::VisitModuleStatement(ModuleStatement* stmt) {
ASSERT(!HasStackOverflow());
CHECK_ALIVE(Visit(stmt->body()));
}
} } // namespace v8::internal
// 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_TYPING_H_
#define V8_TYPING_H_
#include "v8.h"
#include "allocation.h"
#include "ast.h"
#include "compiler.h"
#include "type-info.h"
#include "zone.h"
#include "scopes.h"
namespace v8 {
namespace internal {
class AstTyper: public AstVisitor {
public:
static void Type(CompilationInfo* info);
void* operator new(size_t size, Zone* zone) {
return zone->New(static_cast<int>(size));
}
void operator delete(void* pointer, Zone* zone) { }
void operator delete(void* pointer) { }
DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
private:
explicit AstTyper(CompilationInfo* info);
CompilationInfo* info_;
TypeFeedbackOracle oracle_;
TypeFeedbackOracle* oracle() { return &oracle_; }
Zone* zone() const { return info_->zone(); }
void VisitDeclarations(ZoneList<Declaration*>* declarations);
void VisitStatements(ZoneList<Statement*>* statements);
#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
AST_NODE_LIST(DECLARE_VISIT)
#undef DECLARE_VISIT
DISALLOW_COPY_AND_ASSIGN(AstTyper);
};
} } // namespace v8::internal
#endif // V8_TYPING_H_
......@@ -453,6 +453,8 @@
'../../src/transitions.h',
'../../src/type-info.cc',
'../../src/type-info.h',
'../../src/typing.cc',
'../../src/typing.h',
'../../src/unbound-queue-inl.h',
'../../src/unbound-queue.h',
'../../src/unicode-inl.h',
......
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