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
This commit is contained in:
parent
0a4289618d
commit
6fda4e4c28
80
src/ast.cc
80
src/ast.cc
@ -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
|
||||
|
||||
|
159
src/ast.h
159
src/ast.h
@ -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_;
|
||||
|
151
src/hydrogen.cc
151
src/hydrogen.cc
@ -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,
|
||||
|
512
src/typing.cc
Normal file
512
src/typing.cc
Normal file
@ -0,0 +1,512 @@
|
||||
// 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
|
77
src/typing.h
Normal file
77
src/typing.h
Normal file
@ -0,0 +1,77 @@
|
||||
// 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',
|
||||
|
Loading…
Reference in New Issue
Block a user