From 36dd23aa1b24f1b30defd7037f948610a7d0c767 Mon Sep 17 00:00:00 2001 From: "verwaest@chromium.org" Date: Mon, 17 Sep 2012 17:18:27 +0000 Subject: [PATCH] Do not go to slow mode and back to fast in initializer blocks. Review URL: https://chromiumcodereview.appspot.com/10905308 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12534 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/full-codegen-arm.cc | 59 +--------------- src/ast.cc | 2 - src/ast.h | 12 ---- src/ia32/full-codegen-ia32.cc | 50 +------------- src/parser.cc | 152 +++--------------------------------------- src/runtime.cc | 9 --- src/runtime.h | 1 - src/x64/full-codegen-x64.cc | 50 +------------- 8 files changed, 14 insertions(+), 321 deletions(-) diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index de09516..8ff3e93 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -2183,43 +2183,16 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { ASSERT(prop != NULL); ASSERT(prop->key()->AsLiteral() != NULL); - // If the assignment starts a block of assignments to the same object, - // change to slow case to avoid the quadratic behavior of repeatedly - // adding fast properties. - if (expr->starts_initialization_block()) { - __ push(result_register()); - __ ldr(ip, MemOperand(sp, kPointerSize)); // Receiver is now under value. - __ push(ip); - __ CallRuntime(Runtime::kToSlowProperties, 1); - __ pop(result_register()); - } - // Record source code position before IC call. SetSourcePosition(expr->position()); __ mov(r2, Operand(prop->key()->AsLiteral()->handle())); - // Load receiver to r1. Leave a copy in the stack if needed for turning the - // receiver into fast case. - if (expr->ends_initialization_block()) { - __ ldr(r1, MemOperand(sp)); - } else { - __ pop(r1); - } + __ pop(r1); Handle ic = is_classic_mode() ? isolate()->builtins()->StoreIC_Initialize() : isolate()->builtins()->StoreIC_Initialize_Strict(); CallIC(ic, RelocInfo::CODE_TARGET, expr->AssignmentFeedbackId()); - // If the assignment ends an initialization block, revert to fast case. - if (expr->ends_initialization_block()) { - __ push(r0); // Result of assignment, saved even if not needed. - // Receiver is under the result value. - __ ldr(ip, MemOperand(sp, kPointerSize)); - __ push(ip); - __ CallRuntime(Runtime::kToFastProperties, 1); - __ pop(r0); - __ Drop(1); - } PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); context()->Plug(r0); } @@ -2228,44 +2201,16 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { // Assignment to a property, using a keyed store IC. - // If the assignment starts a block of assignments to the same object, - // change to slow case to avoid the quadratic behavior of repeatedly - // adding fast properties. - if (expr->starts_initialization_block()) { - __ push(result_register()); - // Receiver is now under the key and value. - __ ldr(ip, MemOperand(sp, 2 * kPointerSize)); - __ push(ip); - __ CallRuntime(Runtime::kToSlowProperties, 1); - __ pop(result_register()); - } - // Record source code position before IC call. SetSourcePosition(expr->position()); __ pop(r1); // Key. - // Load receiver to r2. Leave a copy in the stack if needed for turning the - // receiver into fast case. - if (expr->ends_initialization_block()) { - __ ldr(r2, MemOperand(sp)); - } else { - __ pop(r2); - } + __ pop(r2); Handle ic = is_classic_mode() ? isolate()->builtins()->KeyedStoreIC_Initialize() : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); CallIC(ic, RelocInfo::CODE_TARGET, expr->AssignmentFeedbackId()); - // If the assignment ends an initialization block, revert to fast case. - if (expr->ends_initialization_block()) { - __ push(r0); // Result of assignment, saved even if not needed. - // Receiver is under the result value. - __ ldr(ip, MemOperand(sp, kPointerSize)); - __ push(ip); - __ CallRuntime(Runtime::kToFastProperties, 1); - __ pop(r0); - __ Drop(1); - } PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); context()->Plug(r0); } diff --git a/src/ast.cc b/src/ast.cc index 6b68705..52990b8 100644 --- a/src/ast.cc +++ b/src/ast.cc @@ -126,8 +126,6 @@ Assignment::Assignment(Isolate* isolate, pos_(pos), binary_operation_(NULL), assignment_id_(GetNextId(isolate)), - block_start_(false), - block_end_(false), is_monomorphic_(false) { } diff --git a/src/ast.h b/src/ast.h index e72296c..802ac65 100644 --- a/src/ast.h +++ b/src/ast.h @@ -1870,15 +1870,6 @@ class Assignment: public Expression { // This check relies on the definition order of token in token.h. bool is_compound() const { return op() > Token::ASSIGN; } - // An initialization block is a series of statments of the form - // x.y.z.a = ...; x.y.z.b = ...; etc. The parser marks the beginning and - // ending of these blocks to allow for optimizations of initialization - // blocks. - bool starts_initialization_block() { return block_start_; } - bool ends_initialization_block() { return block_end_; } - void mark_block_start() { block_start_ = true; } - void mark_block_end() { block_end_ = true; } - BailoutId AssignmentId() const { return assignment_id_; } // Type feedback information. @@ -1911,9 +1902,6 @@ class Assignment: public Expression { BinaryOperation* binary_operation_; const BailoutId assignment_id_; - bool block_start_; - bool block_end_; - bool is_monomorphic_; SmallMapList receiver_types_; }; diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index a058701..72021b4 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -2139,37 +2139,15 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { ASSERT(prop != NULL); ASSERT(prop->key()->AsLiteral() != NULL); - // If the assignment starts a block of assignments to the same object, - // change to slow case to avoid the quadratic behavior of repeatedly - // adding fast properties. - if (expr->starts_initialization_block()) { - __ push(result_register()); - __ push(Operand(esp, kPointerSize)); // Receiver is now under value. - __ CallRuntime(Runtime::kToSlowProperties, 1); - __ pop(result_register()); - } - // Record source code position before IC call. SetSourcePosition(expr->position()); __ mov(ecx, prop->key()->AsLiteral()->handle()); - if (expr->ends_initialization_block()) { - __ mov(edx, Operand(esp, 0)); - } else { - __ pop(edx); - } + __ pop(edx); Handle ic = is_classic_mode() ? isolate()->builtins()->StoreIC_Initialize() : isolate()->builtins()->StoreIC_Initialize_Strict(); CallIC(ic, RelocInfo::CODE_TARGET, expr->AssignmentFeedbackId()); - // If the assignment ends an initialization block, revert to fast case. - if (expr->ends_initialization_block()) { - __ push(eax); // Result of assignment, saved even if not needed. - __ push(Operand(esp, kPointerSize)); // Receiver is under value. - __ CallRuntime(Runtime::kToFastProperties, 1); - __ pop(eax); - __ Drop(1); - } PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); context()->Plug(eax); } @@ -2181,23 +2159,8 @@ void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { // esp[0] : key // esp[kPointerSize] : receiver - // If the assignment starts a block of assignments to the same object, - // change to slow case to avoid the quadratic behavior of repeatedly - // adding fast properties. - if (expr->starts_initialization_block()) { - __ push(result_register()); - // Receiver is now under the key and value. - __ push(Operand(esp, 2 * kPointerSize)); - __ CallRuntime(Runtime::kToSlowProperties, 1); - __ pop(result_register()); - } - __ pop(ecx); // Key. - if (expr->ends_initialization_block()) { - __ mov(edx, Operand(esp, 0)); // Leave receiver on the stack for later. - } else { - __ pop(edx); - } + __ pop(edx); // Record source code position before IC call. SetSourcePosition(expr->position()); Handle ic = is_classic_mode() @@ -2205,15 +2168,6 @@ void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); CallIC(ic, RelocInfo::CODE_TARGET, expr->AssignmentFeedbackId()); - // If the assignment ends an initialization block, revert to fast case. - if (expr->ends_initialization_block()) { - __ pop(edx); - __ push(eax); // Result of assignment, saved even if not needed. - __ push(edx); - __ CallRuntime(Runtime::kToFastProperties, 1); - __ pop(eax); - } - PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); context()->Plug(eax); } diff --git a/src/parser.cc b/src/parser.cc index 37e903a..a626d99 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -832,145 +832,10 @@ void Parser::ReportMessageAt(Scanner::Location source_location, } -// Base class containing common code for the different finder classes used by -// the parser. -class ParserFinder { - protected: - ParserFinder() {} - static Assignment* AsAssignment(Statement* stat) { - if (stat == NULL) return NULL; - ExpressionStatement* exp_stat = stat->AsExpressionStatement(); - if (exp_stat == NULL) return NULL; - return exp_stat->expression()->AsAssignment(); - } -}; - - -// An InitializationBlockFinder finds and marks sequences of statements of the -// form expr.a = ...; expr.b = ...; etc. -class InitializationBlockFinder : public ParserFinder { - public: - // We find and mark the initialization blocks in top level - // non-looping code only. This is because the optimization prevents - // reuse of the map transitions, so it should be used only for code - // that will only be run once. - InitializationBlockFinder(Scope* top_scope, Target* target) - : enabled_(top_scope->DeclarationScope()->is_global_scope() && - !IsLoopTarget(target)), - first_in_block_(NULL), - last_in_block_(NULL), - block_size_(0) {} - - ~InitializationBlockFinder() { - if (!enabled_) return; - if (InBlock()) EndBlock(); - } - - void Update(Statement* stat) { - if (!enabled_) return; - Assignment* assignment = AsAssignment(stat); - if (InBlock()) { - if (BlockContinues(assignment)) { - UpdateBlock(assignment); - } else { - EndBlock(); - } - } - if (!InBlock() && (assignment != NULL) && - (assignment->op() == Token::ASSIGN)) { - StartBlock(assignment); - } - } - - private: - // The minimum number of contiguous assignment that will - // be treated as an initialization block. Benchmarks show that - // the overhead exceeds the savings below this limit. - static const int kMinInitializationBlock = 3; - - static bool IsLoopTarget(Target* target) { - while (target != NULL) { - if (target->node()->AsIterationStatement() != NULL) return true; - target = target->previous(); - } - return false; - } - - // Returns true if the expressions appear to denote the same object. - // In the context of initialization blocks, we only consider expressions - // of the form 'expr.x' or expr["x"]. - static bool SameObject(Expression* e1, Expression* e2) { - VariableProxy* v1 = e1->AsVariableProxy(); - VariableProxy* v2 = e2->AsVariableProxy(); - if (v1 != NULL && v2 != NULL) { - return v1->name()->Equals(*v2->name()); - } - Property* p1 = e1->AsProperty(); - Property* p2 = e2->AsProperty(); - if ((p1 == NULL) || (p2 == NULL)) return false; - Literal* key1 = p1->key()->AsLiteral(); - Literal* key2 = p2->key()->AsLiteral(); - if ((key1 == NULL) || (key2 == NULL)) return false; - if (!key1->handle()->IsString() || !key2->handle()->IsString()) { - return false; - } - String* name1 = String::cast(*key1->handle()); - String* name2 = String::cast(*key2->handle()); - if (!name1->Equals(name2)) return false; - return SameObject(p1->obj(), p2->obj()); - } - - // Returns true if the expressions appear to denote different properties - // of the same object. - static bool PropertyOfSameObject(Expression* e1, Expression* e2) { - Property* p1 = e1->AsProperty(); - Property* p2 = e2->AsProperty(); - if ((p1 == NULL) || (p2 == NULL)) return false; - return SameObject(p1->obj(), p2->obj()); - } - - bool BlockContinues(Assignment* assignment) { - if ((assignment == NULL) || (first_in_block_ == NULL)) return false; - if (assignment->op() != Token::ASSIGN) return false; - return PropertyOfSameObject(first_in_block_->target(), - assignment->target()); - } - - void StartBlock(Assignment* assignment) { - first_in_block_ = assignment; - last_in_block_ = assignment; - block_size_ = 1; - } - - void UpdateBlock(Assignment* assignment) { - last_in_block_ = assignment; - ++block_size_; - } - - void EndBlock() { - if (block_size_ >= kMinInitializationBlock) { - first_in_block_->mark_block_start(); - last_in_block_->mark_block_end(); - } - last_in_block_ = first_in_block_ = NULL; - block_size_ = 0; - } - - bool InBlock() { return first_in_block_ != NULL; } - - const bool enabled_; - Assignment* first_in_block_; - Assignment* last_in_block_; - int block_size_; - - DISALLOW_COPY_AND_ASSIGN(InitializationBlockFinder); -}; - - // A ThisNamedPropertyAssignmentFinder finds and marks statements of the form // this.x = ...;, where x is a named property. It also determines whether a // function contains only assignments of this type. -class ThisNamedPropertyAssignmentFinder : public ParserFinder { +class ThisNamedPropertyAssignmentFinder { public: ThisNamedPropertyAssignmentFinder(Isolate* isolate, Zone* zone) : isolate_(isolate), @@ -981,6 +846,13 @@ class ThisNamedPropertyAssignmentFinder : public ParserFinder { zone_(zone) { } + static Assignment* AsAssignment(Statement* stat) { + if (stat == NULL) return NULL; + ExpressionStatement* exp_stat = stat->AsExpressionStatement(); + if (exp_stat == NULL) return NULL; + return exp_stat->expression()->AsAssignment(); + } + void Update(Scope* scope, Statement* stat) { // Bail out if function already has property assignment that are // not simple this property assignments. @@ -1146,7 +1018,6 @@ void* Parser::ParseSourceElements(ZoneList* processor, TargetScope scope(&this->target_stack_); ASSERT(processor != NULL); - InitializationBlockFinder block_finder(top_scope_, target_stack_); ThisNamedPropertyAssignmentFinder this_property_assignment_finder(isolate(), zone()); bool directive_prologue = true; // Parsing directive prologue. @@ -1201,7 +1072,6 @@ void* Parser::ParseSourceElements(ZoneList* processor, } } - block_finder.Update(stat); // Find and mark all assignments to named properties in this (this.x =) if (top_scope_->is_function_scope()) { this_property_assignment_finder.Update(top_scope_, stat); @@ -1354,13 +1224,11 @@ Module* Parser::ParseModuleLiteral(bool* ok) { TargetCollector collector(zone()); Target target(&this->target_stack_, &collector); Target target_body(&this->target_stack_, body); - InitializationBlockFinder block_finder(top_scope_, target_stack_); while (peek() != Token::RBRACE) { Statement* stat = ParseModuleElement(NULL, CHECK_OK); if (stat && !stat->IsEmpty()) { body->AddStatement(stat, zone()); - block_finder.Update(stat); } } } @@ -2036,12 +1904,10 @@ Block* Parser::ParseBlock(ZoneStringList* labels, bool* ok) { Block* result = factory()->NewBlock(labels, 16, false); Target target(&this->target_stack_, result); Expect(Token::LBRACE, CHECK_OK); - InitializationBlockFinder block_finder(top_scope_, target_stack_); while (peek() != Token::RBRACE) { Statement* stat = ParseStatement(NULL, CHECK_OK); if (stat && !stat->IsEmpty()) { result->AddStatement(stat, zone()); - block_finder.Update(stat); } } Expect(Token::RBRACE, CHECK_OK); @@ -2066,13 +1932,11 @@ Block* Parser::ParseScopedBlock(ZoneStringList* labels, bool* ok) { TargetCollector collector(zone()); Target target(&this->target_stack_, &collector); Target target_body(&this->target_stack_, body); - InitializationBlockFinder block_finder(top_scope_, target_stack_); while (peek() != Token::RBRACE) { Statement* stat = ParseBlockElement(NULL, CHECK_OK); if (stat && !stat->IsEmpty()) { body->AddStatement(stat, zone()); - block_finder.Update(stat); } } } diff --git a/src/runtime.cc b/src/runtime.cc index b822814..a514b94 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -5032,15 +5032,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ToFastProperties) { } -RUNTIME_FUNCTION(MaybeObject*, Runtime_ToSlowProperties) { - ASSERT(args.length() == 1); - Object* obj = args[0]; - return (obj->IsJSObject() && !obj->IsJSGlobalProxy()) - ? JSObject::cast(obj)->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0) - : obj; -} - - RUNTIME_FUNCTION(MaybeObject*, Runtime_ToBool) { NoHandleAllocation ha; ASSERT(args.length() == 1); diff --git a/src/runtime.h b/src/runtime.h index da60ee1..c9939d0 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -62,7 +62,6 @@ namespace internal { F(GetIndexedInterceptorElementNames, 1, 1) \ F(GetArgumentsProperty, 1, 1) \ F(ToFastProperties, 1, 1) \ - F(ToSlowProperties, 1, 1) \ F(FinishArrayPrototypeSetup, 1, 1) \ F(SpecialArrayFunctions, 1, 1) \ F(GetDefaultReceiver, 1, 1) \ diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index 954d043..1949c31 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -2118,37 +2118,15 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { ASSERT(prop != NULL); ASSERT(prop->key()->AsLiteral() != NULL); - // If the assignment starts a block of assignments to the same object, - // change to slow case to avoid the quadratic behavior of repeatedly - // adding fast properties. - if (expr->starts_initialization_block()) { - __ push(result_register()); - __ push(Operand(rsp, kPointerSize)); // Receiver is now under value. - __ CallRuntime(Runtime::kToSlowProperties, 1); - __ pop(result_register()); - } - // Record source code position before IC call. SetSourcePosition(expr->position()); __ Move(rcx, prop->key()->AsLiteral()->handle()); - if (expr->ends_initialization_block()) { - __ movq(rdx, Operand(rsp, 0)); - } else { - __ pop(rdx); - } + __ pop(rdx); Handle ic = is_classic_mode() ? isolate()->builtins()->StoreIC_Initialize() : isolate()->builtins()->StoreIC_Initialize_Strict(); CallIC(ic, RelocInfo::CODE_TARGET, expr->AssignmentFeedbackId()); - // If the assignment ends an initialization block, revert to fast case. - if (expr->ends_initialization_block()) { - __ push(rax); // Result of assignment, saved even if not needed. - __ push(Operand(rsp, kPointerSize)); // Receiver is under value. - __ CallRuntime(Runtime::kToFastProperties, 1); - __ pop(rax); - __ Drop(1); - } PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); context()->Plug(rax); } @@ -2157,23 +2135,8 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { // Assignment to a property, using a keyed store IC. - // If the assignment starts a block of assignments to the same object, - // change to slow case to avoid the quadratic behavior of repeatedly - // adding fast properties. - if (expr->starts_initialization_block()) { - __ push(result_register()); - // Receiver is now under the key and value. - __ push(Operand(rsp, 2 * kPointerSize)); - __ CallRuntime(Runtime::kToSlowProperties, 1); - __ pop(result_register()); - } - __ pop(rcx); - if (expr->ends_initialization_block()) { - __ movq(rdx, Operand(rsp, 0)); // Leave receiver on the stack for later. - } else { - __ pop(rdx); - } + __ pop(rdx); // Record source code position before IC call. SetSourcePosition(expr->position()); Handle ic = is_classic_mode() @@ -2181,15 +2144,6 @@ void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); CallIC(ic, RelocInfo::CODE_TARGET, expr->AssignmentFeedbackId()); - // If the assignment ends an initialization block, revert to fast case. - if (expr->ends_initialization_block()) { - __ pop(rdx); - __ push(rax); // Result of assignment, saved even if not needed. - __ push(rdx); - __ CallRuntime(Runtime::kToFastProperties, 1); - __ pop(rax); - } - PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); context()->Plug(rax); } -- 2.7.4