Force context allocation for variables in generator scopes.

* src/scopes.h (ForceContextAllocation, has_forced_context_allocation):
  New interface to force context allocation for an entire function's
  scope.

* src/scopes.cc: Unless a new scope is a function scope, if its outer
  scope has forced context allocation, it should also force context
  allocation.
  (MustAllocateInContext): Return true if the scope as a whole has
  forced context allocation.
  (CollectStackAndContextLocals): Allow temporaries to be
  context-allocated.

* src/parser.cc (ParseFunctionLiteral): Force context allocation for
  generator scopes.

* src/v8globals.h (VariableMode): Update comment on TEMPORARY.

* src/arm/full-codegen-arm.cc (Generate):
* src/ia32/full-codegen-ia32.cc (Generate):
* src/x64/full-codegen-x64.cc (Generate): Assert that generators have no
  stack slots.

* test/mjsunit/harmony/generators-instantiation.js: New test.

BUG=v8:2355
TEST=mjsunit/harmony/generators-instantiation

Review URL: https://codereview.chromium.org/13408005
Patch from Andy Wingo <wingo@igalia.com>.

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14152 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
mstarzinger@chromium.org 2013-04-05 13:19:31 +00:00
parent d71678676f
commit b6efbd79de
8 changed files with 87 additions and 9 deletions

View File

@ -162,8 +162,6 @@ void FullCodeGenerator::Generate() {
// the frame (that is done below). // the frame (that is done below).
FrameScope frame_scope(masm_, StackFrame::MANUAL); FrameScope frame_scope(masm_, StackFrame::MANUAL);
int locals_count = info->scope()->num_stack_slots();
info->set_prologue_offset(masm_->pc_offset()); info->set_prologue_offset(masm_->pc_offset());
{ {
PredictableCodeSizeScope predictible_code_size_scope( PredictableCodeSizeScope predictible_code_size_scope(
@ -179,6 +177,9 @@ void FullCodeGenerator::Generate() {
} }
{ Comment cmnt(masm_, "[ Allocate locals"); { Comment cmnt(masm_, "[ Allocate locals");
int locals_count = info->scope()->num_stack_slots();
// Generators allocate locals, if any, in context slots.
ASSERT(!info->function()->is_generator() || locals_count == 0);
for (int i = 0; i < locals_count; i++) { for (int i = 0; i < locals_count; i++) {
__ push(ip); __ push(ip);
} }

View File

@ -164,6 +164,8 @@ void FullCodeGenerator::Generate() {
{ Comment cmnt(masm_, "[ Allocate locals"); { Comment cmnt(masm_, "[ Allocate locals");
int locals_count = info->scope()->num_stack_slots(); int locals_count = info->scope()->num_stack_slots();
// Generators allocate locals, if any, in context slots.
ASSERT(!info->function()->is_generator() || locals_count == 0);
if (locals_count == 1) { if (locals_count == 1) {
__ push(Immediate(isolate()->factory()->undefined_value())); __ push(Immediate(isolate()->factory()->undefined_value()));
} else if (locals_count > 1) { } else if (locals_count > 1) {

View File

@ -4391,6 +4391,9 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> function_name,
// Parse function body. // Parse function body.
{ FunctionState function_state(this, scope, is_generator, isolate()); { FunctionState function_state(this, scope, is_generator, isolate());
top_scope_->SetScopeName(function_name); top_scope_->SetScopeName(function_name);
// For generators, allocating variables in contexts is currently a win
// because it minimizes the work needed to suspend and resume an activation.
if (is_generator) top_scope_->ForceContextAllocation();
// FormalParameterList :: // FormalParameterList ::
// '(' (Identifier)*[','] ')' // '(' (Identifier)*[','] ')'

View File

@ -197,6 +197,8 @@ void Scope::SetDefaults(ScopeType type,
outer_scope_calls_non_strict_eval_ = false; outer_scope_calls_non_strict_eval_ = false;
inner_scope_calls_eval_ = false; inner_scope_calls_eval_ = false;
force_eager_compilation_ = false; force_eager_compilation_ = false;
force_context_allocation_ = (outer_scope != NULL && !is_function_scope())
? outer_scope->has_forced_context_allocation() : false;
num_var_or_const_ = 0; num_var_or_const_ = 0;
num_stack_slots_ = 0; num_stack_slots_ = 0;
num_heap_slots_ = 0; num_heap_slots_ = 0;
@ -603,12 +605,18 @@ void Scope::CollectStackAndContextLocals(ZoneList<Variable*>* stack_locals,
} }
} }
// Collect temporaries which are always allocated on the stack. // Collect temporaries which are always allocated on the stack, unless the
// context as a whole has forced context allocation.
for (int i = 0; i < temps_.length(); i++) { for (int i = 0; i < temps_.length(); i++) {
Variable* var = temps_[i]; Variable* var = temps_[i];
if (var->is_used()) { if (var->is_used()) {
ASSERT(var->IsStackLocal()); if (var->IsContextSlot()) {
stack_locals->Add(var, zone()); ASSERT(has_forced_context_allocation());
context_locals->Add(var, zone());
} else {
ASSERT(var->IsStackLocal());
stack_locals->Add(var, zone());
}
} }
} }
@ -1182,8 +1190,11 @@ bool Scope::MustAllocateInContext(Variable* var) {
// an eval() call or a runtime with lookup), it must be allocated in the // an eval() call or a runtime with lookup), it must be allocated in the
// context. // context.
// //
// Exceptions: temporary variables are never allocated in a context; // Exceptions: If the scope as a whole has forced context allocation, all
// catch-bound variables are always allocated in a context. // variables will have context allocation, even temporaries. Otherwise
// temporary variables are always stack-allocated. Catch-bound variables are
// always context-allocated.
if (has_forced_context_allocation()) return true;
if (var->mode() == TEMPORARY) return false; if (var->mode() == TEMPORARY) return false;
if (var->mode() == INTERNAL) return true; if (var->mode() == INTERNAL) return true;
if (is_catch_scope() || is_block_scope() || is_module_scope()) return true; if (is_catch_scope() || is_block_scope() || is_module_scope()) return true;

View File

@ -269,6 +269,15 @@ class Scope: public ZoneObject {
end_position_ = statement_pos; end_position_ = statement_pos;
} }
// In some cases we want to force context allocation for a whole scope.
void ForceContextAllocation() {
ASSERT(!already_resolved());
force_context_allocation_ = true;
}
bool has_forced_context_allocation() const {
return force_context_allocation_;
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Predicates. // Predicates.
@ -494,6 +503,7 @@ class Scope: public ZoneObject {
bool outer_scope_calls_non_strict_eval_; bool outer_scope_calls_non_strict_eval_;
bool inner_scope_calls_eval_; bool inner_scope_calls_eval_;
bool force_eager_compilation_; bool force_eager_compilation_;
bool force_context_allocation_;
// True if it doesn't need scope resolution (e.g., if the scope was // True if it doesn't need scope resolution (e.g., if the scope was
// constructed based on a serialized scope info or a catch context). // constructed based on a serialized scope info or a catch context).

View File

@ -496,8 +496,8 @@ enum VariableMode {
INTERNAL, // like VAR, but not user-visible (may or may not INTERNAL, // like VAR, but not user-visible (may or may not
// be in a context) // be in a context)
TEMPORARY, // temporary variables (not user-visible), never TEMPORARY, // temporary variables (not user-visible), stack-allocated
// in a context // unless the scope as a whole has forced context allocation
DYNAMIC, // always require dynamic lookup (we don't know DYNAMIC, // always require dynamic lookup (we don't know
// the declaration) // the declaration)

View File

@ -160,6 +160,8 @@ void FullCodeGenerator::Generate() {
{ Comment cmnt(masm_, "[ Allocate locals"); { Comment cmnt(masm_, "[ Allocate locals");
int locals_count = info->scope()->num_stack_slots(); int locals_count = info->scope()->num_stack_slots();
// Generators allocate locals, if any, in context slots.
ASSERT(!info->function()->is_generator() || locals_count == 0);
if (locals_count == 1) { if (locals_count == 1) {
__ PushRoot(Heap::kUndefinedValueRootIndex); __ PushRoot(Heap::kUndefinedValueRootIndex);
} else if (locals_count > 1) { } else if (locals_count > 1) {

View File

@ -0,0 +1,49 @@
// 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.
// Flags: --harmony-generators --harmony-scoping
// Test instantations of generators.
// Generators shouldn't allocate stack slots. This test will abort in debug
// mode if generators have stack slots.
function TestContextAllocation() {
function* g1(a, b, c) { yield 1; return [a, b, c]; }
function* g2() { yield 1; return arguments; }
function* g3() { yield 1; return this; }
function* g4() { var x = 10; yield 1; return x; }
// Temporary variable context allocation
function* g5(l) { "use strict"; yield 1; for (let x in l) { yield x; } }
g1();
g2();
g3();
g4();
g5(["foo"]);
}
TestContextAllocation();