Commit 0332bebd authored by franzih's avatar franzih Committed by Commit bot

Collect type profile for DevTools

Collect type information for JavaScript variables and display it
in Chrome DevTools.
Design Doc: https://docs.google.com/a/google.com/document/d/1O1uepXZXBI6IwiawTrYC3ohhiNgzkyTdjn3R8ysbYgk/edit?usp=sharing

When debugging JavaScript, it’s helpful to know the type of
a variable, parameter, and return values. JavaScript is
dynamically typed, and for complex
source code it’s often hard to infer types. With type profiling, we
can provide type information to JavaScript developers.

This CL is a proof of concept. It collects type profile for
assignments and simply prints the types to stdout.

The output looks something like this:

#my_var1
  #Object
  #number
  #string
  #number
  #undefined
  #string
  #Object
  #Object

We use an extra slot in the feedback vector of assignments to
carry the list of types for that assignment. The extra slot is
only added when the flag --type-profile is given.

Missing work:
* Collect data for parameters and return values (currently only assignments).
* Remove duplicates from the list of collected types and use a common base class.
* Add line numbers or source position instead of the variable name.

BUG=v8:5935

Review-Url: https://codereview.chromium.org/2707873002
Cr-Commit-Position: refs/heads/master@{#43791}
parent d9978e94
...@@ -15,7 +15,8 @@ namespace internal { ...@@ -15,7 +15,8 @@ namespace internal {
class AstNumberingVisitor final : public AstVisitor<AstNumberingVisitor> { class AstNumberingVisitor final : public AstVisitor<AstNumberingVisitor> {
public: public:
AstNumberingVisitor(uintptr_t stack_limit, Zone* zone, AstNumberingVisitor(uintptr_t stack_limit, Zone* zone,
Compiler::EagerInnerFunctionLiterals* eager_literals) Compiler::EagerInnerFunctionLiterals* eager_literals,
bool collect_type_profile = false)
: zone_(zone), : zone_(zone),
eager_literals_(eager_literals), eager_literals_(eager_literals),
next_id_(BailoutId::FirstUsable().ToInt()), next_id_(BailoutId::FirstUsable().ToInt()),
...@@ -25,7 +26,8 @@ class AstNumberingVisitor final : public AstVisitor<AstNumberingVisitor> { ...@@ -25,7 +26,8 @@ class AstNumberingVisitor final : public AstVisitor<AstNumberingVisitor> {
slot_cache_(zone), slot_cache_(zone),
disable_crankshaft_reason_(kNoReason), disable_crankshaft_reason_(kNoReason),
dont_optimize_reason_(kNoReason), dont_optimize_reason_(kNoReason),
catch_prediction_(HandlerTable::UNCAUGHT) { catch_prediction_(HandlerTable::UNCAUGHT),
collect_type_profile_(collect_type_profile) {
InitializeAstVisitor(stack_limit); InitializeAstVisitor(stack_limit);
} }
...@@ -101,6 +103,7 @@ class AstNumberingVisitor final : public AstVisitor<AstNumberingVisitor> { ...@@ -101,6 +103,7 @@ class AstNumberingVisitor final : public AstVisitor<AstNumberingVisitor> {
BailoutReason disable_crankshaft_reason_; BailoutReason disable_crankshaft_reason_;
BailoutReason dont_optimize_reason_; BailoutReason dont_optimize_reason_;
HandlerTable::CatchPrediction catch_prediction_; HandlerTable::CatchPrediction catch_prediction_;
bool collect_type_profile_;
DEFINE_AST_VISITOR_SUBCLASS_MEMBERS(); DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
DISALLOW_COPY_AND_ASSIGN(AstNumberingVisitor); DISALLOW_COPY_AND_ASSIGN(AstNumberingVisitor);
...@@ -422,7 +425,8 @@ void AstNumberingVisitor::VisitAssignment(Assignment* node) { ...@@ -422,7 +425,8 @@ void AstNumberingVisitor::VisitAssignment(Assignment* node) {
if (node->is_compound()) VisitBinaryOperation(node->binary_operation()); if (node->is_compound()) VisitBinaryOperation(node->binary_operation());
VisitReference(node->target()); VisitReference(node->target());
Visit(node->value()); Visit(node->value());
ReserveFeedbackSlots(node); node->AssignFeedbackSlots(properties_.get_spec(), language_mode_,
&slot_cache_, collect_type_profile_);
} }
...@@ -714,12 +718,14 @@ bool AstNumberingVisitor::Renumber(FunctionLiteral* node) { ...@@ -714,12 +718,14 @@ bool AstNumberingVisitor::Renumber(FunctionLiteral* node) {
bool AstNumbering::Renumber( bool AstNumbering::Renumber(
uintptr_t stack_limit, Zone* zone, FunctionLiteral* function, uintptr_t stack_limit, Zone* zone, FunctionLiteral* function,
Compiler::EagerInnerFunctionLiterals* eager_literals) { Compiler::EagerInnerFunctionLiterals* eager_literals,
bool collect_type_profile) {
DisallowHeapAllocation no_allocation; DisallowHeapAllocation no_allocation;
DisallowHandleAllocation no_handles; DisallowHandleAllocation no_handles;
DisallowHandleDereference no_deref; DisallowHandleDereference no_deref;
AstNumberingVisitor visitor(stack_limit, zone, eager_literals); AstNumberingVisitor visitor(stack_limit, zone, eager_literals,
collect_type_profile);
return visitor.Renumber(function); return visitor.Renumber(function);
} }
} // namespace internal } // namespace internal
......
...@@ -27,7 +27,8 @@ namespace AstNumbering { ...@@ -27,7 +27,8 @@ namespace AstNumbering {
// non-null, adds any eager inner literal functions into it. // non-null, adds any eager inner literal functions into it.
bool Renumber( bool Renumber(
uintptr_t stack_limit, Zone* zone, FunctionLiteral* function, uintptr_t stack_limit, Zone* zone, FunctionLiteral* function,
ThreadedList<ThreadedListZoneEntry<FunctionLiteral*>>* eager_literals); ThreadedList<ThreadedListZoneEntry<FunctionLiteral*>>* eager_literals,
bool collect_type_profile = false);
} }
// Some details on yield IDs // Some details on yield IDs
......
...@@ -283,8 +283,13 @@ Assignment::Assignment(Token::Value op, Expression* target, Expression* value, ...@@ -283,8 +283,13 @@ Assignment::Assignment(Token::Value op, Expression* target, Expression* value,
void Assignment::AssignFeedbackSlots(FeedbackVectorSpec* spec, void Assignment::AssignFeedbackSlots(FeedbackVectorSpec* spec,
LanguageMode language_mode, LanguageMode language_mode,
FeedbackSlotCache* cache) { FeedbackSlotCache* cache,
bool collect_type_profile) {
AssignVectorSlots(target(), spec, language_mode, &slot_); AssignVectorSlots(target(), spec, language_mode, &slot_);
if (collect_type_profile) {
type_profile_slot_ = spec->AddTypeProfileSlot();
}
} }
void CountOperation::AssignFeedbackSlots(FeedbackVectorSpec* spec, void CountOperation::AssignFeedbackSlots(FeedbackVectorSpec* spec,
......
...@@ -2414,9 +2414,17 @@ class Assignment final : public Expression { ...@@ -2414,9 +2414,17 @@ class Assignment final : public Expression {
} }
void AssignFeedbackSlots(FeedbackVectorSpec* spec, LanguageMode language_mode, void AssignFeedbackSlots(FeedbackVectorSpec* spec, LanguageMode language_mode,
FeedbackSlotCache* cache); FeedbackSlotCache* cache,
bool collect_type_profile = false);
FeedbackSlot AssignmentSlot() const { return slot_; } FeedbackSlot AssignmentSlot() const { return slot_; }
FeedbackSlot TypeProfileSlot() const {
DCHECK(HasTypeProfileSlot());
return type_profile_slot_;
}
bool HasTypeProfileSlot() const { return !type_profile_slot_.IsInvalid(); }
private: private:
friend class AstNodeFactory; friend class AstNodeFactory;
...@@ -2438,6 +2446,8 @@ class Assignment final : public Expression { ...@@ -2438,6 +2446,8 @@ class Assignment final : public Expression {
Expression* value_; Expression* value_;
BinaryOperation* binary_operation_; BinaryOperation* binary_operation_;
SmallMapList receiver_types_; SmallMapList receiver_types_;
FeedbackSlot type_profile_slot_;
}; };
......
...@@ -478,9 +478,23 @@ bool Renumber(ParseInfo* parse_info, ...@@ -478,9 +478,23 @@ bool Renumber(ParseInfo* parse_info,
Compiler::EagerInnerFunctionLiterals* eager_literals) { Compiler::EagerInnerFunctionLiterals* eager_literals) {
RuntimeCallTimerScope runtimeTimer(parse_info->isolate(), RuntimeCallTimerScope runtimeTimer(parse_info->isolate(),
&RuntimeCallStats::CompileRenumber); &RuntimeCallStats::CompileRenumber);
// CollectTypeProfile uses its own feedback slots. If we have existing
// FeedbackMetadata, we can only collect type profile, if the feedback vector
// has the appropriate slots.
bool collect_type_profile;
if (parse_info->shared_info().is_null() ||
parse_info->shared_info()->feedback_metadata()->length() == 0) {
collect_type_profile = FLAG_type_profile;
} else {
collect_type_profile =
parse_info->shared_info()->feedback_metadata()->HasTypeProfileSlot();
}
if (!AstNumbering::Renumber( if (!AstNumbering::Renumber(
parse_info->isolate()->stack_guard()->real_climit(), parse_info->isolate()->stack_guard()->real_climit(),
parse_info->zone(), parse_info->literal(), eager_literals)) { parse_info->zone(), parse_info->literal(), eager_literals,
collect_type_profile)) {
return false; return false;
} }
if (!parse_info->shared_info().is_null()) { if (!parse_info->shared_info().is_null()) {
......
...@@ -802,6 +802,22 @@ void BytecodeGraphBuilder::VisitStaDataPropertyInLiteral() { ...@@ -802,6 +802,22 @@ void BytecodeGraphBuilder::VisitStaDataPropertyInLiteral() {
environment()->RecordAfterState(node, Environment::kAttachFrameState); environment()->RecordAfterState(node, Environment::kAttachFrameState);
} }
void BytecodeGraphBuilder::VisitCollectTypeProfile() {
PrepareEagerCheckpoint();
Node* name =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
Node* value = environment()->LookupAccumulator();
Node* index = jsgraph()->Constant(bytecode_iterator().GetIndexOperand(1));
Node* vector = jsgraph()->Constant(feedback_vector());
const Operator* op = javascript()->CallRuntime(Runtime::kCollectTypeProfile);
Node* node = NewNode(op, name, value, vector, index);
environment()->RecordAfterState(node, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitLdaContextSlot() { void BytecodeGraphBuilder::VisitLdaContextSlot() {
const Operator* op = javascript()->LoadContext( const Operator* op = javascript()->LoadContext(
bytecode_iterator().GetUnsignedImmediateOperand(2), bytecode_iterator().GetUnsignedImmediateOperand(2),
......
...@@ -54,6 +54,7 @@ int FeedbackMetadata::GetSlotSize(FeedbackSlotKind kind) { ...@@ -54,6 +54,7 @@ int FeedbackMetadata::GetSlotSize(FeedbackSlotKind kind) {
case FeedbackSlotKind::kToBoolean: case FeedbackSlotKind::kToBoolean:
case FeedbackSlotKind::kLiteral: case FeedbackSlotKind::kLiteral:
case FeedbackSlotKind::kCreateClosure: case FeedbackSlotKind::kCreateClosure:
case FeedbackSlotKind::kTypeProfile:
return 1; return 1;
case FeedbackSlotKind::kCall: case FeedbackSlotKind::kCall:
...@@ -186,7 +187,8 @@ void FeedbackVector::ComputeCounts(int* with_type_info, int* generic, ...@@ -186,7 +187,8 @@ void FeedbackVector::ComputeCounts(int* with_type_info, int* generic,
case FeedbackSlotKind::kStoreOwnNamed: case FeedbackSlotKind::kStoreOwnNamed:
case FeedbackSlotKind::kStoreKeyedSloppy: case FeedbackSlotKind::kStoreKeyedSloppy:
case FeedbackSlotKind::kStoreKeyedStrict: case FeedbackSlotKind::kStoreKeyedStrict:
case FeedbackSlotKind::kStoreDataPropertyInLiteral: { case FeedbackSlotKind::kStoreDataPropertyInLiteral:
case FeedbackSlotKind::kTypeProfile: {
if (obj->IsWeakCell() || obj->IsFixedArray() || obj->IsString()) { if (obj->IsWeakCell() || obj->IsFixedArray() || obj->IsString()) {
with++; with++;
} else if (obj == megamorphic_sentinel) { } else if (obj == megamorphic_sentinel) {
......
...@@ -149,6 +149,8 @@ const char* FeedbackMetadata::Kind2String(FeedbackSlotKind kind) { ...@@ -149,6 +149,8 @@ const char* FeedbackMetadata::Kind2String(FeedbackSlotKind kind) {
return "kCreateClosure"; return "kCreateClosure";
case FeedbackSlotKind::kLiteral: case FeedbackSlotKind::kLiteral:
return "LITERAL"; return "LITERAL";
case FeedbackSlotKind::kTypeProfile:
return "TYPE_PROFILE";
case FeedbackSlotKind::kGeneral: case FeedbackSlotKind::kGeneral:
return "STUB"; return "STUB";
case FeedbackSlotKind::kKindsNumber: case FeedbackSlotKind::kKindsNumber:
...@@ -158,6 +160,18 @@ const char* FeedbackMetadata::Kind2String(FeedbackSlotKind kind) { ...@@ -158,6 +160,18 @@ const char* FeedbackMetadata::Kind2String(FeedbackSlotKind kind) {
return "?"; return "?";
} }
bool FeedbackMetadata::HasTypeProfileSlot() {
FeedbackMetadataIterator iter(this);
while (iter.HasNext()) {
iter.Next();
FeedbackSlotKind kind = iter.kind();
if (kind == FeedbackSlotKind::kTypeProfile) {
return true;
}
}
return false;
}
FeedbackSlotKind FeedbackVector::GetKind(FeedbackSlot slot) const { FeedbackSlotKind FeedbackVector::GetKind(FeedbackSlot slot) const {
DCHECK(!is_empty()); DCHECK(!is_empty());
return metadata()->GetKind(slot); return metadata()->GetKind(slot);
...@@ -219,6 +233,7 @@ Handle<FeedbackVector> FeedbackVector::New(Isolate* isolate, ...@@ -219,6 +233,7 @@ Handle<FeedbackVector> FeedbackVector::New(Isolate* isolate,
case FeedbackSlotKind::kStoreKeyedStrict: case FeedbackSlotKind::kStoreKeyedStrict:
case FeedbackSlotKind::kStoreDataPropertyInLiteral: case FeedbackSlotKind::kStoreDataPropertyInLiteral:
case FeedbackSlotKind::kGeneral: case FeedbackSlotKind::kGeneral:
case FeedbackSlotKind::kTypeProfile:
array->set(index, *uninitialized_sentinel, SKIP_WRITE_BARRIER); array->set(index, *uninitialized_sentinel, SKIP_WRITE_BARRIER);
break; break;
...@@ -336,7 +351,8 @@ void FeedbackVector::ClearSlots(JSFunction* host_function) { ...@@ -336,7 +351,8 @@ void FeedbackVector::ClearSlots(JSFunction* host_function) {
break; break;
} }
case FeedbackSlotKind::kCreateClosure: { case FeedbackSlotKind::kCreateClosure: {
break; case FeedbackSlotKind::kTypeProfile:
break;
} }
case FeedbackSlotKind::kGeneral: { case FeedbackSlotKind::kGeneral: {
if (obj->IsHeapObject()) { if (obj->IsHeapObject()) {
...@@ -1023,5 +1039,49 @@ void StoreDataPropertyInLiteralICNexus::ConfigureMonomorphic( ...@@ -1023,5 +1039,49 @@ void StoreDataPropertyInLiteralICNexus::ConfigureMonomorphic(
SetFeedbackExtra(*name); SetFeedbackExtra(*name);
} }
InlineCacheState CollectTypeProfileNexus::StateFromFeedback() const {
Isolate* isolate = GetIsolate();
Object* const feedback = GetFeedback();
if (feedback == *FeedbackVector::UninitializedSentinel(isolate)) {
return UNINITIALIZED;
}
return MONOMORPHIC;
}
void CollectTypeProfileNexus::Collect(Handle<Name> type) {
Isolate* isolate = GetIsolate();
Object* const feedback = GetFeedback();
Handle<ArrayList> types;
if (feedback == *FeedbackVector::UninitializedSentinel(isolate)) {
types = ArrayList::New(isolate, 1);
} else {
types = Handle<ArrayList>(ArrayList::cast(feedback), isolate);
}
// TODO(franzih): Somehow sort this list. Either avoid duplicates
// or use the common base type.
SetFeedback(*ArrayList::Add(types, type));
}
void CollectTypeProfileNexus::Print() const {
Isolate* isolate = GetIsolate();
Object* const feedback = GetFeedback();
if (feedback == *FeedbackVector::UninitializedSentinel(isolate)) {
return;
}
Handle<ArrayList> list;
list = Handle<ArrayList>(ArrayList::cast(feedback), isolate);
for (int i = 0; i < list->Length(); i++) {
Name* name = Name::cast(list->Get(i));
name->Print();
}
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -36,6 +36,7 @@ enum class FeedbackSlotKind { ...@@ -36,6 +36,7 @@ enum class FeedbackSlotKind {
kCompareOp, kCompareOp,
kToBoolean, kToBoolean,
kStoreDataPropertyInLiteral, kStoreDataPropertyInLiteral,
kTypeProfile,
kCreateClosure, kCreateClosure,
kLiteral, kLiteral,
// This is a general purpose slot that occupies one feedback vector element. // This is a general purpose slot that occupies one feedback vector element.
...@@ -150,6 +151,11 @@ class FeedbackVectorSpecBase { ...@@ -150,6 +151,11 @@ class FeedbackVectorSpecBase {
return AddSlot(FeedbackSlotKind::kStoreDataPropertyInLiteral); return AddSlot(FeedbackSlotKind::kStoreDataPropertyInLiteral);
} }
FeedbackSlot AddTypeProfileSlot() {
DCHECK(FLAG_type_profile);
return AddSlot(FeedbackSlotKind::kTypeProfile);
}
#ifdef OBJECT_PRINT #ifdef OBJECT_PRINT
// For gdb debugging. // For gdb debugging.
void Print(); void Print();
...@@ -250,6 +256,8 @@ class FeedbackMetadata : public FixedArray { ...@@ -250,6 +256,8 @@ class FeedbackMetadata : public FixedArray {
static const char* Kind2String(FeedbackSlotKind kind); static const char* Kind2String(FeedbackSlotKind kind);
bool HasTypeProfileSlot();
private: private:
static const int kFeedbackSlotKindBits = 5; static const int kFeedbackSlotKindBits = 5;
STATIC_ASSERT(static_cast<int>(FeedbackSlotKind::kKindsNumber) < STATIC_ASSERT(static_cast<int>(FeedbackSlotKind::kKindsNumber) <
...@@ -742,6 +750,30 @@ class StoreDataPropertyInLiteralICNexus : public FeedbackNexus { ...@@ -742,6 +750,30 @@ class StoreDataPropertyInLiteralICNexus : public FeedbackNexus {
InlineCacheState StateFromFeedback() const override; InlineCacheState StateFromFeedback() const override;
}; };
// For each assignment, store the type of the value in the collection of types
// in the feedback vector.
class CollectTypeProfileNexus : public FeedbackNexus {
public:
CollectTypeProfileNexus(Handle<FeedbackVector> vector, FeedbackSlot slot)
: FeedbackNexus(vector, slot) {
DCHECK_EQ(FeedbackSlotKind::kTypeProfile, vector->GetKind(slot));
}
CollectTypeProfileNexus(FeedbackVector* vector, FeedbackSlot slot)
: FeedbackNexus(vector, slot) {
DCHECK_EQ(FeedbackSlotKind::kTypeProfile, vector->GetKind(slot));
}
// Add a type to the list of types.
void Collect(Handle<Name> type);
// Dump the types to stdout.
// TODO(franzih): pass this information to the debugger protocol instead of
// stdout.
void Print() const;
InlineCacheState StateFromFeedback() const override;
};
inline BinaryOperationHint BinaryOperationHintFromFeedback(int type_feedback); inline BinaryOperationHint BinaryOperationHintFromFeedback(int type_feedback);
inline CompareOperationHint CompareOperationHintFromFeedback(int type_feedback); inline CompareOperationHint CompareOperationHintFromFeedback(int type_feedback);
......
...@@ -581,6 +581,7 @@ void IC::ConfigureVectorState(Handle<Name> name, Handle<Map> map, ...@@ -581,6 +581,7 @@ void IC::ConfigureVectorState(Handle<Name> name, Handle<Map> map,
case FeedbackSlotKind::kLiteral: case FeedbackSlotKind::kLiteral:
case FeedbackSlotKind::kGeneral: case FeedbackSlotKind::kGeneral:
case FeedbackSlotKind::kStoreDataPropertyInLiteral: case FeedbackSlotKind::kStoreDataPropertyInLiteral:
case FeedbackSlotKind::kTypeProfile:
case FeedbackSlotKind::kInvalid: case FeedbackSlotKind::kInvalid:
case FeedbackSlotKind::kKindsNumber: case FeedbackSlotKind::kKindsNumber:
UNREACHABLE(); UNREACHABLE();
...@@ -628,6 +629,7 @@ void IC::ConfigureVectorState(Handle<Name> name, MapHandleList* maps, ...@@ -628,6 +629,7 @@ void IC::ConfigureVectorState(Handle<Name> name, MapHandleList* maps,
case FeedbackSlotKind::kLiteral: case FeedbackSlotKind::kLiteral:
case FeedbackSlotKind::kGeneral: case FeedbackSlotKind::kGeneral:
case FeedbackSlotKind::kStoreDataPropertyInLiteral: case FeedbackSlotKind::kStoreDataPropertyInLiteral:
case FeedbackSlotKind::kTypeProfile:
case FeedbackSlotKind::kInvalid: case FeedbackSlotKind::kInvalid:
case FeedbackSlotKind::kKindsNumber: case FeedbackSlotKind::kKindsNumber:
UNREACHABLE(); UNREACHABLE();
......
...@@ -632,6 +632,13 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreDataPropertyInLiteral( ...@@ -632,6 +632,13 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreDataPropertyInLiteral(
return *this; return *this;
} }
BytecodeArrayBuilder& BytecodeArrayBuilder::CollectTypeProfile(
Register name, int feedback_slot) {
DCHECK(FLAG_type_profile);
OutputCollectTypeProfile(name, feedback_slot);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreNamedProperty( BytecodeArrayBuilder& BytecodeArrayBuilder::StoreNamedProperty(
Register object, size_t name_index, int feedback_slot, Register object, size_t name_index, int feedback_slot,
LanguageMode language_mode) { LanguageMode language_mode) {
......
...@@ -137,6 +137,11 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final ...@@ -137,6 +137,11 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final
Register object, Register name, DataPropertyInLiteralFlags flags, Register object, Register name, DataPropertyInLiteralFlags flags,
int feedback_slot); int feedback_slot);
// Collect type information for developer tools. The value for which we
// record the type is stored in the accumulator.
// TODO(franzih): Do not pass the name, instead use the source position.
BytecodeArrayBuilder& CollectTypeProfile(Register name, int feedback_slot);
// Store a property named by a property name. The value to be stored should be // Store a property named by a property name. The value to be stored should be
// in the accumulator. // in the accumulator.
BytecodeArrayBuilder& StoreNamedProperty(Register object, BytecodeArrayBuilder& StoreNamedProperty(Register object,
......
...@@ -2214,18 +2214,34 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) { ...@@ -2214,18 +2214,34 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) {
LhsKind assign_type = Property::GetAssignType(property); LhsKind assign_type = Property::GetAssignType(property);
// Evaluate LHS expression. // Evaluate LHS expression.
Register lhs_name;
if (expr->HasTypeProfileSlot()) {
lhs_name = register_allocator()->NewRegister();
}
switch (assign_type) { switch (assign_type) {
case VARIABLE: case VARIABLE:
if (expr->HasTypeProfileSlot()) {
builder()
->LoadLiteral(expr->target()->AsVariableProxy()->var()->raw_name())
.StoreAccumulatorInRegister(lhs_name);
}
// Nothing to do to evaluate variable assignment LHS. // Nothing to do to evaluate variable assignment LHS.
break; break;
case NAMED_PROPERTY: { case NAMED_PROPERTY: {
object = VisitForRegisterValue(property->obj()); object = VisitForRegisterValue(property->obj());
name = property->key()->AsLiteral()->AsRawPropertyName(); name = property->key()->AsLiteral()->AsRawPropertyName();
if (expr->HasTypeProfileSlot()) {
builder()->LoadLiteral(name).StoreAccumulatorInRegister(lhs_name);
}
break; break;
} }
case KEYED_PROPERTY: { case KEYED_PROPERTY: {
object = VisitForRegisterValue(property->obj()); object = VisitForRegisterValue(property->obj());
key = VisitForRegisterValue(property->key()); key = VisitForRegisterValue(property->key());
if (expr->HasTypeProfileSlot()) {
builder()->StoreAccumulatorInRegister(lhs_name);
}
break; break;
} }
case NAMED_SUPER_PROPERTY: { case NAMED_SUPER_PROPERTY: {
...@@ -2238,6 +2254,9 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) { ...@@ -2238,6 +2254,9 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) {
builder() builder()
->LoadLiteral(property->key()->AsLiteral()->AsRawPropertyName()) ->LoadLiteral(property->key()->AsLiteral()->AsRawPropertyName())
.StoreAccumulatorInRegister(super_property_args[2]); .StoreAccumulatorInRegister(super_property_args[2]);
if (expr->HasTypeProfileSlot()) {
builder()->StoreAccumulatorInRegister(lhs_name);
}
break; break;
} }
case KEYED_SUPER_PROPERTY: { case KEYED_SUPER_PROPERTY: {
...@@ -2248,6 +2267,10 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) { ...@@ -2248,6 +2267,10 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) {
VisitForRegisterValue(super_property->home_object(), VisitForRegisterValue(super_property->home_object(),
super_property_args[1]); super_property_args[1]);
VisitForRegisterValue(property->key(), super_property_args[2]); VisitForRegisterValue(property->key(), super_property_args[2]);
if (expr->HasTypeProfileSlot()) {
builder()->StoreAccumulatorInRegister(lhs_name);
}
break; break;
} }
} }
...@@ -2336,6 +2359,14 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) { ...@@ -2336,6 +2359,14 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) {
break; break;
} }
} }
// Value is in accumulator.
if (expr->HasTypeProfileSlot()) {
FeedbackSlot collect_type_feedback_slot = expr->TypeProfileSlot();
builder()->CollectTypeProfile(lhs_name,
feedback_index(collect_type_feedback_slot));
}
} }
void BytecodeGenerator::VisitYield(Yield* expr) { void BytecodeGenerator::VisitYield(Yield* expr) {
......
...@@ -105,6 +105,8 @@ namespace interpreter { ...@@ -105,6 +105,8 @@ namespace interpreter {
OperandType::kReg, OperandType::kIdx) \ OperandType::kReg, OperandType::kIdx) \
V(StaDataPropertyInLiteral, AccumulatorUse::kRead, OperandType::kReg, \ V(StaDataPropertyInLiteral, AccumulatorUse::kRead, OperandType::kReg, \
OperandType::kReg, OperandType::kFlag8, OperandType::kIdx) \ OperandType::kReg, OperandType::kFlag8, OperandType::kIdx) \
V(CollectTypeProfile, AccumulatorUse::kRead, OperandType::kReg, \
OperandType::kIdx) \
\ \
/* Binary Operators */ \ /* Binary Operators */ \
V(Add, AccumulatorUse::kReadWrite, OperandType::kReg, OperandType::kIdx) \ V(Add, AccumulatorUse::kReadWrite, OperandType::kReg, OperandType::kIdx) \
......
...@@ -1045,6 +1045,19 @@ void Interpreter::DoStaDataPropertyInLiteral(InterpreterAssembler* assembler) { ...@@ -1045,6 +1045,19 @@ void Interpreter::DoStaDataPropertyInLiteral(InterpreterAssembler* assembler) {
__ Dispatch(); __ Dispatch();
} }
void Interpreter::DoCollectTypeProfile(InterpreterAssembler* assembler) {
Node* name = __ LoadRegister(__ BytecodeOperandReg(0));
Node* value = __ GetAccumulator();
Node* vector_index = __ SmiTag(__ BytecodeOperandIdx(1));
Node* feedback_vector = __ LoadFeedbackVector();
Node* context = __ GetContext();
__ CallRuntime(Runtime::kCollectTypeProfile, context, name, value,
feedback_vector, vector_index);
__ Dispatch();
}
// LdaModuleVariable <cell_index> <depth> // LdaModuleVariable <cell_index> <depth>
// //
// Load the contents of a module variable into the accumulator. The variable is // Load the contents of a module variable into the accumulator. The variable is
......
...@@ -751,6 +751,7 @@ void FeedbackVector::FeedbackVectorPrint(std::ostream& os) { // NOLINT ...@@ -751,6 +751,7 @@ void FeedbackVector::FeedbackVectorPrint(std::ostream& os) { // NOLINT
case FeedbackSlotKind::kCreateClosure: case FeedbackSlotKind::kCreateClosure:
case FeedbackSlotKind::kLiteral: case FeedbackSlotKind::kLiteral:
case FeedbackSlotKind::kGeneral: case FeedbackSlotKind::kGeneral:
case FeedbackSlotKind::kTypeProfile:
break; break;
case FeedbackSlotKind::kToBoolean: case FeedbackSlotKind::kToBoolean:
case FeedbackSlotKind::kInvalid: case FeedbackSlotKind::kInvalid:
......
...@@ -691,6 +691,32 @@ RUNTIME_FUNCTION(Runtime_DefineDataPropertyInLiteral) { ...@@ -691,6 +691,32 @@ RUNTIME_FUNCTION(Runtime_DefineDataPropertyInLiteral) {
return *object; return *object;
} }
RUNTIME_FUNCTION(Runtime_CollectTypeProfile) {
HandleScope scope(isolate);
DCHECK_EQ(4, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, name, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, value, 1);
CONVERT_ARG_HANDLE_CHECKED(FeedbackVector, vector, 2);
CONVERT_SMI_ARG_CHECKED(index, 3);
DCHECK(FLAG_type_profile);
Handle<Name> type = Object::TypeOf(isolate, value);
if (value->IsJSReceiver()) {
Handle<JSReceiver> object = Handle<JSReceiver>::cast(value);
type = JSReceiver::GetConstructorName(object);
}
CollectTypeProfileNexus nexus(vector, vector->ToSlot(index));
nexus.Collect(type);
name->Print();
nexus.Print();
PrintF("\n");
return *name;
}
// Return property without being observable by accessors or interceptors. // Return property without being observable by accessors or interceptors.
RUNTIME_FUNCTION(Runtime_GetDataProperty) { RUNTIME_FUNCTION(Runtime_GetDataProperty) {
HandleScope scope(isolate); HandleScope scope(isolate);
......
...@@ -394,6 +394,7 @@ namespace internal { ...@@ -394,6 +394,7 @@ namespace internal {
F(IsJSGlobalProxy, 1, 1) \ F(IsJSGlobalProxy, 1, 1) \
F(DefineAccessorPropertyUnchecked, 5, 1) \ F(DefineAccessorPropertyUnchecked, 5, 1) \
F(DefineDataPropertyInLiteral, 6, 1) \ F(DefineDataPropertyInLiteral, 6, 1) \
F(CollectTypeProfile, 4, 1) \
F(GetDataProperty, 2, 1) \ F(GetDataProperty, 2, 1) \
F(GetConstructorName, 1, 1) \ F(GetConstructorName, 1, 1) \
F(HasFastPackedElements, 1, 1) \ F(HasFastPackedElements, 1, 1) \
......
#Export
#undefined
#PostDebug
#undefined
#PostExperimentals
#undefined
#g
#undefined
#setTimeout
#Function
#.result
#Function
#attachInspector
#Function
#detachInspector
#Function
#setMaxAsyncTaskStacks
#Function
#breakProgram
#Function
#createObjectWithStrictCheck
#Function
#callWithScheduledBreak
#Function
#allowAccessorFormatting
#Function
#.result
#Function
#Export
#undefined
#PostDebug
#undefined
#PostExperimentals
#undefined
#g
#undefined
#print
#Function
#quit
#Function
#setlocale
#Function
#read
#Function
#load
#Function
#compileAndRunWithOrigin
#Function
#setCurrentTimeMSForTest
#Function
#schedulePauseOnNextStatement
#Function
#cancelPauseOnNextStatement
#Function
#reconnect
#Function
#createContextGroup
#Function
#.result
#Function
#sendMessageToBackend
#Function
#.result
#Function
#.result
#Function
#InspectorTest
#Object
#_dispatchTable
#Map
#_requestId
#number
#_dumpInspectorProtocolMessages
#boolean
#_eventHandler
#Object
#Protocol
#Object
#log
#Function
#logMessage
#Function
#logObject
#Function
#logCallFrames
#Function
#logSourceLocation
#Function
#logSourceLocations
#Function
#logAsyncStackTrace
#Function
#completeTest
#Function
#completeTestAfterPendingTimeouts
#Function
#waitPendingTasks
#Function
#addScript
#Function
#addScriptWithUrl
#Function
#addModule
#Function
#startDumpingProtocolMessages
#Function
#sendRawCommand
#Function
#checkExpectation
#Function
#expectedSuccess
#Function
#expectedError
#Function
#setupScriptMap
#Function
#runTestSuite
#Function
#runAsyncTestSuite
"AsyncFunction"
#_sendCommandPromise
#Function
#_waitForEventPromise
#Function
#_dispatchMessage
#Function
#loadScript
#Function
#.result
#Function
#expression
#string
#eventPattern
#RegExp
#match
#object
#requestId
#number
#messageObject
#Object
#fulfillCallback
#Function
#promise
#Promise
#l
#object
#n
#number
#m
#number
#m
#number
#m
#number
#m
#number
#m
#number
#m
#number
#m
#number
#m
#number
#m
#number
#m
#number
#o
#number
#q
#number
#q
#number
#y
#number
#z
#number
#A
#number
#q
#number
#B
#number
#o
#number
#C
#number
#.result
#Promise
#.result
#Function
#__proto__
#object
#__proto__
#object
#object
#__proto__
#object
#object
#object
#my_var1
#Object
#my_var2
#number
#my_var1
#Object
#number
#my_var2
#number
#number
#my_var1
#Object
#number
#string
#my_var2
#number
#number
#number
#my_var1
#Object
#number
#string
#number
#my_var2
#number
#number
#number
#number
#my_var1
#Object
#number
#string
#number
#undefined
#my_var2
#number
#number
#number
#number
#number
#my_var1
#Object
#number
#string
#number
#undefined
#string
#my_var2
#number
#number
#number
#number
#number
#number
#my_var1
#Object
#number
#string
#number
#undefined
#string
#Object
#my_var2
#number
#number
#number
#number
#number
#number
#number
#my_var1
#Object
#number
#string
#number
#undefined
#string
#Object
#Object
#my_var2
#number
#number
#number
#number
#number
#number
#number
#number
#
#Function
#MyClass
#Function
#my_var
#MyClass
#my_var
#MyClass
#Object
#my_var
#MyClass
#Object
#number
#.result
#undefined
#type
#string
#messageId
#number
#l
#object
#n
#number
#m
#number
#number
#m
#number
#number
#m
#number
#number
#m
#number
#number
#m
#number
#number
#m
#number
#number
#m
#number
#number
#m
#number
#number
#m
#number
#o
#number
#number
#q
#number
#number
#r
#number
#q
#number
#handler
#Function
#l
#object
#n
#number
#m
#number
#number
#number
#m
#number
#number
#number
#m
#number
#number
#number
#m
#number
#number
#number
#m
#number
#number
#number
#m
#number
#number
#number
#m
#number
#number
#number
#m
#number
#number
#number
#m
#number
#number
#m
#number
#o
#number
#number
#number
#q
#number
#number
#number
#r
#number
#number
#q
#number
#y
#number
#z
#number
#B
#number
#.result
#undefined
#eventPattern
#RegExp
#RegExp
#match
#object
#object
#requestId
#number
#number
#messageObject
#Object
#Object
#fulfillCallback
#Function
#Function
#promise
#Promise
#Promise
#l
#object
#object
#n
#number
#number
#m
#number
#number
#number
#number
#m
#number
#number
#number
#number
#m
#number
#number
#number
#number
#m
#number
#number
#number
#number
#m
#number
#number
#number
#number
#m
#number
#number
#number
#number
#m
#number
#number
#number
#number
#m
#number
#number
#number
#number
#m
#number
#number
#number
#m
#number
#number
#o
#number
#number
#number
#number
#q
#number
#number
#number
#number
#q
#number
#number
#y
#number
#number
#z
#number
#number
#A
#number
#number
#q
#number
#number
#B
#number
#number
#o
#number
#number
#C
#number
#number
#messageId
#number
#number
#l
#object
#object
#n
#number
#number
#m
#number
#number
#number
#number
#number
#m
#number
#number
#number
#number
#number
#m
#number
#number
#number
#number
#number
#m
#number
#number
#number
#number
#number
#m
#number
#number
#number
#number
#number
#m
#number
#number
#number
#number
#number
#m
#number
#number
#number
#number
#number
#m
#number
#number
#number
#number
#number
#m
#number
#number
#o
#number
#number
#number
#number
#number
#q
#number
#number
#number
#number
#number
#r
#number
#number
#number
#q
#number
#number
#handler
#Function
#Function
#l
#object
#object
#n
#number
#number
#m
#number
#number
#number
#number
#number
#number
#m
#number
#number
#number
#number
#number
#number
#m
#number
#number
#number
#number
#number
#number
#m
#number
#number
#number
#number
#number
#number
#m
#number
#number
#number
#number
#number
#number
#m
#number
#number
#number
#number
#number
#number
#m
#number
#number
#number
#number
#number
#number
#m
#number
#number
#number
#number
#number
#number
#m
#number
#number
#number
#number
#m
#number
#number
#o
#number
#number
#number
#number
#number
#number
#q
#number
#number
#number
#number
#number
#number
#r
#number
#number
#number
#number
#q
#number
#number
#y
#number
#number
#z
#number
#number
#B
#number
#number
#.result
#undefined
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --type-profile --turbo
const expression = `
function test(param) {
var my_var1 = param;
var my_var2 = 17;
}
test({});
test(123);
test('hello');
test(123);
test(undefined);
test('hello');
test({x: 12});
test({x: 12});
class MyClass {
constructor() {}
}
function testConstructorNames(param) {
var my_var = param;
}
testConstructorNames(new MyClass());
testConstructorNames({});
testConstructorNames(2);
`;
Protocol.Runtime.evaluate({ expression: expression })
.then(function() {InspectorTest.completeTest();});
...@@ -446,6 +446,12 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) { ...@@ -446,6 +446,12 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
scorecard[Bytecodes::ToByte(Bytecode::kTestNull)] = 1; scorecard[Bytecodes::ToByte(Bytecode::kTestNull)] = 1;
} }
if (!FLAG_type_profile) {
// Bytecode for CollectTypeProfile is only emitted when
// Type Information for DevTools is turned on.
scorecard[Bytecodes::ToByte(Bytecode::kCollectTypeProfile)] = 1;
}
// Check return occurs at the end and only once in the BytecodeArray. // Check return occurs at the end and only once in the BytecodeArray.
CHECK_EQ(final_bytecode, Bytecode::kReturn); CHECK_EQ(final_bytecode, Bytecode::kReturn);
CHECK_EQ(scorecard[Bytecodes::ToByte(final_bytecode)], 1); CHECK_EQ(scorecard[Bytecodes::ToByte(final_bytecode)], 1);
......
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